diff options
Diffstat (limited to 'fs/autofs4/expire.c')
-rw-r--r-- | fs/autofs4/expire.c | 115 |
1 files changed, 32 insertions, 83 deletions
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index e93557db8..98a7a0e6c 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -14,102 +14,39 @@ #include "autofs_i.h" /* - * Determine if a dentry tree is in use. This is much the - * same as the standard is_root_busy() function, except - * that :- - * - the extra dentry reference in autofs dentries is not - * considered to be busy - * - mountpoints within the tree are not busy - * - it traverses across mountpoints - * XXX doesn't consider children of covered dentries at mountpoints + * Determine if a subtree of the namespace is busy. */ -static int is_tree_busy(struct dentry *root) +static int is_tree_busy(struct vfsmount *mnt) { - struct dentry *this_parent; + struct vfsmount *this_parent = mnt; struct list_head *next; int count; - root = root->d_mounts; - - count = root->d_count; - this_parent = root; - - DPRINTK(("is_tree_busy: starting at %.*s/%.*s, d_count=%d\n", - root->d_covers->d_parent->d_name.len, - root->d_covers->d_parent->d_name.name, - root->d_name.len, root->d_name.name, - root->d_count)); - - /* Ignore autofs's extra reference */ - if (is_autofs4_dentry(root)) { - DPRINTK(("is_tree_busy: autofs\n")); - count--; - } - - /* Mountpoints don't count (either mountee or mounter) */ - if (d_mountpoint(root) || - root != root->d_covers) { - DPRINTK(("is_tree_busy: mountpoint\n")); - count--; - } - + count = atomic_read(&mnt->mnt_count); repeat: - next = this_parent->d_mounts->d_subdirs.next; + next = this_parent->mnt_mounts.next; resume: - while (next != &this_parent->d_mounts->d_subdirs) { - int adj = 0; + while (next != &this_parent->mnt_mounts) { struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, - d_child); - + struct vfsmount *p = list_entry(tmp, struct vfsmount, + mnt_child); next = tmp->next; - - dentry = dentry->d_mounts; - - DPRINTK(("is_tree_busy: considering %.*s/%.*s, d_count=%d, count=%d\n", - this_parent->d_name.len, - this_parent->d_name.name, - dentry->d_covers->d_name.len, - dentry->d_covers->d_name.name, - dentry->d_count, count)); - /* Decrement count for unused children */ - count += (dentry->d_count - 1); - - /* Mountpoints don't count (either mountee or mounter) */ - if (d_mountpoint(dentry) || - dentry != dentry->d_covers) { - DPRINTK(("is_tree_busy: mountpoint dentry=%p covers=%p mounts=%p\n", - dentry, dentry->d_covers, dentry->d_mounts)); - adj++; - } - - /* Ignore autofs's extra reference */ - if (is_autofs4_dentry(dentry)) { - DPRINTK(("is_tree_busy: autofs\n")); - adj++; - } - - count -= adj; - - if (!list_empty(&dentry->d_mounts->d_subdirs)) { - this_parent = dentry->d_mounts; + count += atomic_read(&p->mnt_count) - 1; + if (!list_empty(&p->mnt_mounts)) { + this_parent = p; goto repeat; } - /* root is busy if any leaf is busy */ - if (dentry->d_count != adj) { - DPRINTK(("is_tree_busy: busy leaf (d_count=%d adj=%d)\n", - dentry->d_count, adj)); + if (atomic_read(&p->mnt_count) > 1) return 1; - } } /* * All done at this level ... ascend and resume the search. */ - if (this_parent != root) { - next = this_parent->d_covers->d_child.next; - this_parent = this_parent->d_covers->d_parent; + if (this_parent != mnt) { + next = this_parent->mnt_child.next; + this_parent = this_parent->mnt_parent; goto resume; } @@ -124,6 +61,7 @@ resume: * - it has been unused for exp_timeout time */ static struct dentry *autofs4_expire(struct super_block *sb, + struct vfsmount *mnt, struct autofs_sb_info *sbi, int do_now) { @@ -131,6 +69,8 @@ static struct dentry *autofs4_expire(struct super_block *sb, unsigned long timeout; struct dentry *root = sb->s_root; struct list_head *tmp; + struct dentry *d; + struct vfsmount *p; if (!sbi->exp_timeout || !root) return NULL; @@ -168,8 +108,14 @@ static struct dentry *autofs4_expire(struct super_block *sb, attempts if expire fails the first time */ ino->last_used = now; } - - if (!is_tree_busy(dentry)) { + p = mntget(mnt); + d = dget(dentry); + while(d_mountpoint(d) && follow_down(&p, &d)) + ; + + if (!is_tree_busy(p)) { + dput(d); + mntput(p); DPRINTK(("autofs_expire: returning %p %.*s\n", dentry, dentry->d_name.len, dentry->d_name.name)); /* Start from here next time */ @@ -177,6 +123,8 @@ static struct dentry *autofs4_expire(struct super_block *sb, list_add(&root->d_subdirs, &dentry->d_child); return dentry; } + dput(d); + mntput(p); } return NULL; @@ -184,6 +132,7 @@ static struct dentry *autofs4_expire(struct super_block *sb, /* Perform an expiry operation */ int autofs4_expire_run(struct super_block *sb, + struct vfsmount *mnt, struct autofs_sb_info *sbi, struct autofs_packet_expire *pkt_p) { @@ -195,7 +144,7 @@ int autofs4_expire_run(struct super_block *sb, pkt.hdr.proto_version = sbi->version; pkt.hdr.type = autofs_ptype_expire; - if ((dentry = autofs4_expire(sb, sbi, 0)) == NULL) + if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) return -EAGAIN; pkt.len = dentry->d_name.len; @@ -210,7 +159,7 @@ int autofs4_expire_run(struct super_block *sb, /* Call repeatedly until it returns -EAGAIN, meaning there's nothing more to be done */ -int autofs4_expire_multi(struct super_block *sb, +int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, int *arg) { struct dentry *dentry; @@ -220,7 +169,7 @@ int autofs4_expire_multi(struct super_block *sb, if (arg && get_user(do_now, arg)) return -EFAULT; - if ((dentry = autofs4_expire(sb, sbi, do_now)) != NULL) { + if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { struct autofs_info *de_info = autofs4_dentry_ino(dentry); /* This is synchronous because it makes the daemon a |