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

From: Ian Kent
Date: Sun Jul 10 2022 - 23:38:08 EST


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