[PATCH 3/4] fs: add drain_fs_pwd_pool() helper

From: Christian Brauner

Date: Thu Mar 05 2026 - 16:07:07 EST


Add a drain_fs_pwd_pool() function in to encapsulate draining the pwd
reference pool. This keeps the pool implementation details private to
fs_struct code.

Use it in free_fs_struct() and copy_mnt_ns(). The latter previously
manipulated fs->pwd_refs directly from namespace code.

The caller must ensure exclusive access to the fs_struct, either
because fs->users == 1 or the write side of the seqlock is held.

Signed-off-by: Christian Brauner <brauner@xxxxxxxxxx>
---
fs/fs_struct.c | 18 ++++++++++++++----
fs/namespace.c | 12 +++++++-----
include/linux/fs_struct.h | 1 +
3 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index ce814b76bde7..0a72fb3ea427 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -89,13 +89,23 @@ void chroot_fs_refs(const struct path *old_root, const struct path *new_root)
path_put(old_root);
}

-void free_fs_struct(struct fs_struct *fs)
+/*
+ * Drain extra pwd references from the pool. The caller must ensure
+ * exclusive access to @fs (e.g., fs->users == 1 or under write_seqlock).
+ */
+void drain_fs_pwd_pool(struct fs_struct *fs)
{
- int count = fs->pwd_refs + 1;
+ while (fs->pwd_refs) {
+ path_put(&fs->pwd);
+ fs->pwd_refs--;
+ }
+}

+void free_fs_struct(struct fs_struct *fs)
+{
path_put(&fs->root);
- while (count--)
- path_put(&fs->pwd);
+ drain_fs_pwd_pool(fs);
+ path_put(&fs->pwd);
kmem_cache_free(fs_cachep, fs);
}

diff --git a/fs/namespace.c b/fs/namespace.c
index 89aef4e81f23..06b856410a01 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4262,11 +4262,13 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns,
if (new_fs)
WARN_ON_ONCE(new_fs->users != 1);

- /* Release the extra pwd references of new_fs, if present. */
- while (new_fs && new_fs->pwd_refs) {
- path_put(&new_fs->pwd);
- new_fs->pwd_refs--;
- }
+ /*
+ * Drain the pwd reference pool. The pool must be empty before we
+ * update new_fs->pwd.mnt below since the pooled references belong
+ * to the old mount. Safe to access without locking: new_fs->users == 1.
+ */
+ if (new_fs)
+ drain_fs_pwd_pool(new_fs);
p = old;
q = new;
while (p) {
diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h
index b88437f04672..e67d92f88605 100644
--- a/include/linux/fs_struct.h
+++ b/include/linux/fs_struct.h
@@ -23,6 +23,7 @@ extern void set_fs_root(struct fs_struct *, const struct path *);
extern void set_fs_pwd(struct fs_struct *, const struct path *);
extern struct fs_struct *copy_fs_struct(struct fs_struct *);
extern void free_fs_struct(struct fs_struct *);
+extern void drain_fs_pwd_pool(struct fs_struct *);
extern int unshare_fs_struct(void);

static inline void get_fs_root(struct fs_struct *fs, struct path *root)
--
2.47.3


--jqlkhartfnby3ev3
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0004-fs-factor-out-get_fs_pwd_pool_locked-for-lock-held-c.patch"