Change may_umount_tree() (and the associated autofs code) to use the propagate_mount_tree_busy() helper so it also checks if propagated mounts are busy. This avoids unnecessary umount requests being sent to the automount daemon when a mount in another mount namespace is in use when the expire check is done. Signed-off-by: Ian Kent <raven@xxxxxxxxxx> --- fs/autofs/expire.c | 14 ++++++++++++-- fs/namespace.c | 32 ++++++++++++++++++++------------ fs/pnode.h | 2 -- include/linux/mount.h | 5 ++++- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c index 038b3d2d9f57..1352c454cf1d 100644 --- a/fs/autofs/expire.c +++ b/fs/autofs/expire.c @@ -31,10 +31,13 @@ static int autofs_mount_busy(struct vfsmount *mnt, { struct dentry *top = dentry; struct path path = {.mnt = mnt, .dentry = dentry}; + unsigned int flags; int status = 1; pr_debug("dentry %p %pd\n", dentry, dentry); + /* A reference to the mount is held. */ + flags = TREE_BUSY_REFERENCED; path_get(&path); if (!follow_down_one(&path)) @@ -55,7 +58,7 @@ static int autofs_mount_busy(struct vfsmount *mnt, } /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(path.mnt)) { + if (!may_umount_tree(path.mnt, flags)) { struct autofs_info *ino; ino = autofs_dentry_ino(top); @@ -152,14 +155,21 @@ static int autofs_direct_busy(struct vfsmount *mnt, unsigned long timeout, unsigned int how) { + unsigned int flags; + pr_debug("top %p %pd\n", top, top); /* Forced expire, user space handles busy mounts */ if (how & AUTOFS_EXP_FORCED) return 0; + /* A mounted direct mount will have an open file handle + * associated with it so we need TREE_BUSY_REFERENCED. + */ + flags = TREE_BUSY_REFERENCED; + /* If it's busy update the expiry counters */ - if (!may_umount_tree(mnt)) { + if (!may_umount_tree(mnt, flags)) { struct autofs_info *ino; ino = autofs_dentry_ino(top); diff --git a/fs/namespace.c b/fs/namespace.c index 3c1ee5b5bb69..bdcb55e821f4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1431,28 +1431,36 @@ void mnt_cursor_del(struct mnt_namespace *ns, struct mount *cursor) * open files, pwds, chroots or sub mounts that are * busy. */ -int may_umount_tree(struct vfsmount *m) +int may_umount_tree(struct vfsmount *m, unsigned int flags) { struct mount *mnt = real_mount(m); - int actual_refs = 0; - int minimum_refs = 0; struct mount *p; + int ret = 1; + BUG_ON(!m); - /* write lock needed for mnt_get_count */ + down_read(&namespace_sem); lock_mount_hash(); - for (p = mnt; p; p = next_mnt(p, mnt)) { - actual_refs += mnt_get_count(p); - minimum_refs += 2; + if (propagate_mount_tree_busy(mnt, flags)) { + ret = 0; + goto out; } + /* Only the passed in mount will have a reference held by + * the caller. + */ + flags &= ~TREE_BUSY_REFERENCED; + for (p = next_mnt(mnt, mnt); p; p = next_mnt(p, mnt)) { + if (propagate_mount_tree_busy(p, flags)) { + ret = 0; + break; + } + } +out: unlock_mount_hash(); + up_read(&namespace_sem); - if (actual_refs > minimum_refs) - return 0; - - return 1; + return ret; } - EXPORT_SYMBOL(may_umount_tree); /** diff --git a/fs/pnode.h b/fs/pnode.h index d7b9dddb257b..12c3ab5962a0 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -30,8 +30,6 @@ #define CL_COPY_ALL (CL_COPY_UNBINDABLE | CL_COPY_MNT_NS_FILE) -#define TREE_BUSY_REFERENCED 0x01 - static inline void set_mnt_shared(struct mount *mnt) { mnt->mnt.mnt_flags &= ~MNT_SHARED_MASK; diff --git a/include/linux/mount.h b/include/linux/mount.h index 55a4abaf6715..c21d74ea3d85 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -112,7 +112,10 @@ extern bool our_mnt(struct vfsmount *mnt); extern struct vfsmount *kern_mount(struct file_system_type *); extern void kern_unmount(struct vfsmount *mnt); -extern int may_umount_tree(struct vfsmount *); + +#define TREE_BUSY_REFERENCED 0x01 + +extern int may_umount_tree(struct vfsmount *m, unsigned int flags); extern int may_umount(struct vfsmount *); extern long do_mount(const char *, const char __user *, const char *, unsigned long, void *);