/* * Calculate and dget next entry in top down tree traversal.
*/ staticstruct dentry *get_next_positive_dentry(struct dentry *prev, struct dentry *root)
{ struct autofs_sb_info *sbi = autofs_sbi(root->d_sb); struct dentry *p = prev, *ret = NULL, *d = NULL;
if (prev == NULL) return dget(root);
spin_lock(&sbi->lookup_lock);
spin_lock(&p->d_lock); while (1) { struct dentry *parent;
ret = positive_after(p, d); if (ret || p == root) break;
parent = p->d_parent;
spin_unlock(&p->d_lock);
spin_lock(&parent->d_lock);
d = p;
p = parent;
}
spin_unlock(&p->d_lock);
spin_unlock(&sbi->lookup_lock);
dput(prev); return ret;
}
/* * Check a direct mount point for busyness. * Direct mounts have similar expiry semantics to tree mounts. * The tree is not busy iff no mountpoints are busy and there are no * autofs submounts.
*/ staticint autofs_direct_busy(struct vfsmount *mnt, struct dentry *top, unsignedlong timeout, unsignedint how)
{
pr_debug("top %p %pd\n", top, top);
/* Forced expire, user space handles busy mounts */ if (how & AUTOFS_EXP_FORCED) return 0;
/* If it's busy update the expiry counters */ if (!may_umount_tree(mnt)) { struct autofs_info *ino;
/* Timeout of a direct mount is determined by its top dentry */ if (!autofs_can_expire(top, timeout, how)) return 1;
return 0;
}
/* * Check a directory tree of mount points for busyness * The tree is not busy iff no mountpoints are busy
*/ staticint autofs_tree_busy(struct vfsmount *mnt, struct dentry *top, unsignedlong timeout, unsignedint how)
{ struct autofs_info *top_ino = autofs_dentry_ino(top); struct dentry *p;
pr_debug("top %p %pd\n", top, top);
/* Negative dentry - give up */ if (!simple_positive(top)) return 1;
p = NULL; while ((p = get_next_positive_dentry(p, top))) {
pr_debug("dentry %p %pd\n", p, p);
/* * Is someone visiting anywhere in the subtree ? * If there's no mount we need to check the usage * count for the autofs dentry. * If the fs is busy update the expiry counter.
*/ if (d_mountpoint(p)) { if (autofs_mount_busy(mnt, p, how)) {
top_ino->last_used = jiffies;
dput(p); return 1;
}
} else { struct autofs_info *ino = autofs_dentry_ino(p); unsignedint ino_count = READ_ONCE(ino->count);
/* allow for dget above and top is already dgot */ if (p == top)
ino_count += 2; else
ino_count++;
p = NULL; while ((p = get_next_positive_dentry(p, parent))) {
pr_debug("dentry %p %pd\n", p, p);
if (d_mountpoint(p)) { /* Can we umount this guy */ if (autofs_mount_busy(mnt, p, how)) continue;
/* This isn't a submount so if a forced expire * has been requested, user space handles busy
* mounts */ if (how & AUTOFS_EXP_FORCED) return p;
/* Can we expire this guy */ if (autofs_can_expire(p, timeout, how)) return p;
}
} return NULL;
}
/* Check if we can expire a direct mount (possibly a tree) */ staticstruct dentry *autofs_expire_direct(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, unsignedint how)
{ struct dentry *root = dget(sb->s_root); struct autofs_info *ino; unsignedlong timeout;
if (!root) return NULL;
timeout = sbi->exp_timeout;
if (!autofs_direct_busy(mnt, root, timeout, how)) {
spin_lock(&sbi->fs_lock);
ino = autofs_dentry_ino(root); /* No point expiring a pending mount */ if (ino->flags & AUTOFS_INF_PENDING) {
spin_unlock(&sbi->fs_lock); goto out;
}
ino->flags |= AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
synchronize_rcu(); if (!autofs_direct_busy(mnt, root, timeout, how)) {
spin_lock(&sbi->fs_lock);
ino->flags |= AUTOFS_INF_EXPIRING;
init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock); return root;
}
spin_lock(&sbi->fs_lock);
ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
}
out:
dput(root);
return NULL;
}
/* Check if 'dentry' should expire, or return a nearby * dentry that is suitable. * If returned dentry is different from arg dentry, * then a dget() reference was taken, else not.
*/ staticstruct dentry *should_expire(struct dentry *dentry, struct vfsmount *mnt, unsignedlong timeout, unsignedint how)
{ struct autofs_info *ino = autofs_dentry_ino(dentry); unsignedint ino_count;
/* No point expiring a pending mount */ if (ino->flags & AUTOFS_INF_PENDING) return NULL;
/* * Case 1: (i) indirect mount or top level pseudo direct mount * (autofs-4.1). * (ii) indirect mount with offset mount, check the "/" * offset (autofs-5.0+).
*/ if (d_mountpoint(dentry)) {
pr_debug("checking mountpoint %p %pd\n", dentry, dentry);
/* Can we umount this guy */ if (autofs_mount_busy(mnt, dentry, how)) return NULL;
/* This isn't a submount so if a forced expire * has been requested, user space handles busy
* mounts */ if (how & AUTOFS_EXP_FORCED) return dentry;
/* Can we expire this guy */ if (autofs_can_expire(dentry, timeout, how)) return dentry; return NULL;
}
if (d_is_symlink(dentry)) {
pr_debug("checking symlink %p %pd\n", dentry, dentry);
/* Forced expire, user space handles busy mounts */ if (how & AUTOFS_EXP_FORCED) return dentry;
/* * A symlink can't be "busy" in the usual sense so * just check last used for expire timeout.
*/ if (autofs_can_expire(dentry, timeout, how)) return dentry; return NULL;
}
if (autofs_empty(ino)) return NULL;
/* Case 2: tree mount, expire iff entire tree is not busy */ if (!(how & AUTOFS_EXP_LEAVES)) { /* Not a forced expire? */ if (!(how & AUTOFS_EXP_FORCED)) { /* ref-walk currently on this dentry? */
ino_count = READ_ONCE(ino->count) + 1; if (d_count(dentry) > ino_count) return NULL;
}
if (!autofs_tree_busy(mnt, dentry, timeout, how)) return dentry; /* * Case 3: pseudo direct mount, expire individual leaves * (autofs-4.1).
*/
} else { struct dentry *expired;
/* Not a forced expire? */ if (!(how & AUTOFS_EXP_FORCED)) { /* ref-walk currently on this dentry? */
ino_count = READ_ONCE(ino->count) + 1; if (d_count(dentry) > ino_count) return NULL;
}
/* * Find an eligible tree to time-out * A tree is eligible if :- * - it is unused by any user process * - it has been unused for exp_timeout time
*/ staticstruct dentry *autofs_expire_indirect(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, unsignedint how)
{ unsignedlong timeout; struct dentry *root = sb->s_root; struct dentry *dentry; struct dentry *expired; struct dentry *found; struct autofs_info *ino;
/* Make sure a reference is not taken on found if * things have changed.
*/
how &= ~AUTOFS_EXP_LEAVES;
found = should_expire(expired, mnt, timeout, how); if (found != expired) { // something has changed, continue
dput(found); goto next;
}
/* * Call repeatedly until it returns -EAGAIN, meaning there's nothing * more to be done.
*/ int autofs_expire_multi(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, int __user *arg)
{ unsignedint how = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.