From: This is a second attempt at a patch to address Christophs' concerns. - renamed autofs4_may_umount to __may_umount_tree, made it static and moved it to namespace.c. - added stub function may_umount_tree with description - altered may_umount to use above stub function and added little description - added may_umount_tree prototype to fs.h - removed the EXPORT_SYMBOL for vfsmount_lock - updated expire.c to suit --- 25-akpm/fs/autofs4/expire.c | 43 ----------------------- 25-akpm/fs/namespace.c | 79 ++++++++++++++++++++++++++++++++++++++++---- 25-akpm/include/linux/fs.h | 1 3 files changed, 75 insertions(+), 48 deletions(-) diff -puN fs/autofs4/expire.c~4-autofs4-260-expire-20040405-fix-fix fs/autofs4/expire.c --- 25/fs/autofs4/expire.c~4-autofs4-260-expire-20040405-fix-fix 2004-05-04 20:55:11.753834888 -0700 +++ 25-akpm/fs/autofs4/expire.c 2004-05-04 20:55:11.760833824 -0700 @@ -45,47 +45,6 @@ static inline int autofs4_can_expire(str return 1; } -/* Sorry I can't solve the problem without using counts either */ -static int autofs4_may_umount(struct vfsmount *mnt) -{ - struct list_head *next; - struct vfsmount *this_parent = mnt; - int actual_refs; - int minimum_refs; - - spin_lock(&vfsmount_lock); - actual_refs = atomic_read(&mnt->mnt_count); - minimum_refs = 2; -repeat: - next = this_parent->mnt_mounts.next; -resume: - while (next != &this_parent->mnt_mounts) { - struct vfsmount *p = list_entry(next, struct vfsmount, mnt_child); - - next = next->next; - - actual_refs += atomic_read(&p->mnt_count); - minimum_refs += 2; - - if ( !list_empty(&p->mnt_mounts) ) { - this_parent = p; - goto repeat; - } - } - - if (this_parent != mnt) { - next = this_parent->mnt_child.next; - this_parent = this_parent->mnt_parent; - goto resume; - } - spin_unlock(&vfsmount_lock); - - DPRINTK(("autofs4_may_umount: done actual = %d, minimum = %d\n", - actual_refs, minimum_refs)); - - return actual_refs > minimum_refs; -} - /* Check a mount point for busyness return 1 if not busy, otherwise */ static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) { @@ -108,7 +67,7 @@ static int autofs4_check_mount(struct vf goto done; /* The big question */ - if (autofs4_may_umount(mnt) == 0) + if (may_umount_tree(mnt) == 0) status = 1; done: DPRINTK(("autofs4_check_mount: returning = %d\n", status)); diff -puN fs/namespace.c~4-autofs4-260-expire-20040405-fix-fix fs/namespace.c --- 25/fs/namespace.c~4-autofs4-260-expire-20040405-fix-fix 2004-05-04 20:55:11.755834584 -0700 +++ 25-akpm/fs/namespace.c 2004-05-04 20:55:11.761833672 -0700 @@ -37,8 +37,6 @@ static inline int sysfs_init(void) /* spinlock for vfsmount related operations, inplace of dcache_lock */ spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; -EXPORT_SYMBOL(vfsmount_lock); - static struct list_head *mount_hashtable; static int hash_mask, hash_bits; static kmem_cache_t *mnt_cache; @@ -262,16 +260,85 @@ struct seq_operations mounts_op = { .show = show_vfsmnt }; -/* +static int __may_umount_tree(struct vfsmount *mnt, int root_mnt_only) +{ + struct list_head *next; + struct vfsmount *this_parent = mnt; + int actual_refs; + int minimum_refs; + + spin_lock(&vfsmount_lock); + actual_refs = atomic_read(&mnt->mnt_count); + minimum_refs = 2; + + if (root_mnt_only) { + spin_unlock(&vfsmount_lock); + if (actual_refs > minimum_refs) + return -EBUSY; + return 0; + } + +repeat: + next = this_parent->mnt_mounts.next; +resume: + while (next != &this_parent->mnt_mounts) { + struct vfsmount *p = list_entry(next, struct vfsmount, mnt_child); + + next = next->next; + + actual_refs += atomic_read(&p->mnt_count); + minimum_refs += 2; + + if (!list_empty(&p->mnt_mounts)) { + this_parent = p; + goto repeat; + } + } + + if (this_parent != mnt) { + next = this_parent->mnt_child.next; + this_parent = this_parent->mnt_parent; + goto resume; + } + spin_unlock(&vfsmount_lock); + + if (actual_refs > minimum_refs) + return -EBUSY; + + return 0; +} + +/** + * may_umount_tree - check if a mount tree is busy + * @mnt: root of mount tree + * + * This is called to check if a tree of mounts has any + * open files, pwds, chroots or sub mounts that are + * busy. + */ +int may_umount_tree(struct vfsmount *mnt) +{ + return __may_umount_tree(mnt, 0); +} + +EXPORT_SYMBOL(may_umount_tree); + +/** + * may_umount - check if a mount point is busy + * @mnt: root of mount + * + * This is called to check if a mount point has any + * open files, pwds, chroots or sub mounts. If the + * mount has sub mounts this will return busy + * regardless of whether the sub mounts are busy. + * * Doesn't take quota and stuff into account. IOW, in some cases it will * give false negatives. The main reason why it's here is that we need * a non-destructive way to look for easily umountable filesystems. */ int may_umount(struct vfsmount *mnt) { - if (atomic_read(&mnt->mnt_count) > 2) - return -EBUSY; - return 0; + return __may_umount_tree(mnt, 1); } EXPORT_SYMBOL(may_umount); diff -puN include/linux/fs.h~4-autofs4-260-expire-20040405-fix-fix include/linux/fs.h --- 25/include/linux/fs.h~4-autofs4-260-expire-20040405-fix-fix 2004-05-04 20:55:11.757834280 -0700 +++ 25-akpm/include/linux/fs.h 2004-05-04 20:55:11.763833368 -0700 @@ -1129,6 +1129,7 @@ void unnamed_dev_init(void); extern int register_filesystem(struct file_system_type *); extern int unregister_filesystem(struct file_system_type *); extern struct vfsmount *kern_mount(struct file_system_type *); +extern int may_umount_tree(struct vfsmount *); extern int may_umount(struct vfsmount *); extern long do_mount(char *, char *, char *, unsigned long, void *); _