diff mbox series

[3/3] vfs: make may_umount_tree() mount namespace aware

Message ID 165751067234.210556.2619133379044425664.stgit@donald.themaw.net (mailing list archive)
State New
Headers show
Series autofs: fix may_umount_tree() | expand

Commit Message

Ian Kent July 11, 2022, 3:37 a.m. UTC
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@themaw.net>
---
 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 mbox series

Patch

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 *);