On 24/6/25 02:55, Al Viro wrote:
On Mon, Jun 23, 2025 at 05:06:52PM +0800, Ian Kent wrote:
I also have revived my patch to make may_umount_tree() namespace aware
and it still seems to work fine.
Could you post it?
Not sure the formatting will be ok since my email setup is a mess.
It's against the v1 of your series.
Your advice (and Christian and others) would be much appreciated.
vfs: make may_umount_tree() mount namespace aware
From: Ian Kent <ikent@xxxxxxxxxx>
Change may_umount_tree() to also check if propagated mounts are busy
during autofs expire runs.
Also alter may_umount_tree() to take a flag to indicate a reference to
the passed in mount is held.
This avoids unnecessary umount requests being sent to the automount
daemon if 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 | 4 ++--
fs/namespace.c | 36 ++++++++++++++++++++++++++++++------
fs/pnode.c | 32 ++++++++++++++++++++++++++++++++
fs/pnode.h | 1 +
include/linux/mount.h | 5 ++++-
5 files changed, 69 insertions(+), 9 deletions(-)
diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c
index 5c2d459e1e48..c303d11f4c12 100644
--- a/fs/autofs/expire.c
+++ b/fs/autofs/expire.c
@@ -55,7 +55,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, TREE_BUSY_REFERENCED)) {
struct autofs_info *ino;
ino = autofs_dentry_ino(top);
@@ -156,7 +156,7 @@ static int autofs_direct_busy(struct vfsmount *mnt,
return 0;
/* If it's busy update the expiry counters */
- if (!may_umount_tree(mnt)) {
+ if (!may_umount_tree(mnt, TREE_BUSY_REFERENCED)) {
struct autofs_info *ino;
ino = autofs_dentry_ino(top);
diff --git a/fs/namespace.c b/fs/namespace.c
index bb95e5102916..3cb90bb46b94 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1635,25 +1635,49 @@ const struct seq_operations mounts_op = {
/**
* may_umount_tree - check if a mount tree is busy
* @m: root of mount tree
+ * @flags: behaviour modifier flags:
+ * TREE_BUSY_REFERENCED caller holds additional reference
+ * to @m.
*
* 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 *m)
+bool may_umount_tree(struct vfsmount *m, unsigned int flags)
{
struct mount *mnt = real_mount(m);
+ struct mount *p, *q;
bool busy = false;
- /* write lock needed for mnt_get_count */
+ down_read(&namespace_sem);
lock_mount_hash();
- for (struct mount *p = mnt; p; p = next_mnt(p, mnt)) {
- if (mnt_get_count(p) > (p == mnt ? 2 : 1)) {
- busy = true;
- break;
+ for (p = mnt; p; p = next_mnt(p, mnt)) {
+ unsigned int f = 0;
+
+ if (p->mnt_mountpoint != mnt->mnt.mnt_root) {
+ if (p == mnt)
+ f = flags;
+ if (propagate_mount_tree_busy(p, f)) {
+ busy = true;
+ break;
+ }
+ continue;
+ }
+
+ /* p is a covering mnt, need to check if p or any of its
+ * children are in use. A reference to p is not held so
+ * don't pass TREE_BUSY_REFERENCED to the propagation
+ * helper.
+ */
+ for (q = p; q; q = next_mnt(q, p)) {
+ if (propagate_mount_tree_busy(q, f)) {
+ busy = true;
+ break;
+ }
}
}
unlock_mount_hash();
+ up_read(&namespace_sem);
return !busy;
}
diff --git a/fs/pnode.c b/fs/pnode.c
index efed6bb20c72..e4222a008039 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -429,6 +429,38 @@ int propagate_mount_busy(struct mount *mnt, int refcnt)
return 0;
}
+/*
+ * Check if the mount tree at 'mnt' is in use or any of its
+ * propogated mounts are in use.
+ * @mnt: the mount to be checked
+ * @flags: see may_umount_tree() for modifier descriptions.
+ *
+ * Check if mnt or any of its propogated mounts have a reference
+ * count greater than the minimum reference count (ie. are in use).
+ */
+int propagate_mount_tree_busy(struct mount *mnt, unsigned int flags)
+{
+ struct mount *m;
+ struct mount *parent = mnt->mnt_parent;
+ int refcnt = flags & TREE_BUSY_REFERENCED ? 2 : 1;
+
+ if (do_refcount_check(mnt, refcnt))
+ return 1;
+
+ for (m = propagation_next(parent, parent); m;
+ m = propagation_next(m, parent)) {
+ struct mount *child;
+
+ child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint);
+ if (!child)
+ continue;
+
+ if (do_refcount_check(child, 1))
+ return 1;
+ }
+ return 0;
+}
+
/*
* Clear MNT_LOCKED when it can be shown to be safe.
*
diff --git a/fs/pnode.h b/fs/pnode.h
index bfc10c095cbf..a0d2974e57d7 100644
--- a/fs/pnode.h
+++ b/fs/pnode.h
@@ -46,6 +46,7 @@ int propagate_mnt(struct mount *, struct mountpoint *,
struct mount *,
struct hlist_head *);
void propagate_umount(struct list_head *);
int propagate_mount_busy(struct mount *, int);
+int propagate_mount_tree_busy(struct mount *, unsigned int);
void propagate_mount_unlock(struct mount *);
void mnt_release_group_id(struct mount *);
int get_dominating_id(struct mount *mnt, const struct path *root);
diff --git a/include/linux/mount.h b/include/linux/mount.h
index cae7324650b6..d66555cc8e96 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -114,7 +114,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 bool may_umount_tree(struct vfsmount *, unsigned int);
extern int may_umount(struct vfsmount *);
int do_mount(const char *, const char __user *,
const char *, unsigned long, void *);