summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-10-26 15:00:42 +0100
committerChristian Brauner <brauner@kernel.org>2026-10-29 14:44:43 +0100
commit02a336a831f02a18047bacbd49018a237af8a172 (patch)
treef76c46b5676741ea2e221f581092cfcfef8138b4
parenteeecea75ece2ba894bee4d3c53ed2e40abda8f0e (diff)
downloadlinux-02a336a831f02a18047bacbd49018a237af8a172.tar.gz
linux-02a336a831f02a18047bacbd49018a237af8a172.zip
libfs: hold one mount reference for all pins
simple_pin_fs() takes a reference on the internal mount for every pin and simple_release_fs() drops it again. So every debugfs, tracefs, securityfs, configfs, apparmorfs, drm and vfio file that is created and removed costs a mntget() and a mntput() on that mount. The mount comes from vfs_kern_mount() and not from kern_mount(), so it never gets MNT_NS_INTERNAL and mntput_no_expire() can't take the fast path for it. Each of those mntput()s ends up in mntput_no_expire_slowpath(), takes mount_lock and sums the per-cpu counters in mnt_get_count() to find out whether it dropped the last reference. It never did for anything but the last pin, that's what the pin count is for. The reference per pin never bought anything. The pin count under pin_fs_lock is what keeps the mount alive, nothing looks at the mount's refcount to find out how many pins there are and the mount is only torn down once the last pin is gone. Keep the reference vfs_kern_mount() returned as the one reference the pins hold. Further pins only bump the count and the last simple_release_fs() drops the reference. The refcount of a pinned mount now changes when it is first pinned and when the last pin goes away. Everything in between is a spinlock and a counter. Reported-by: kernel test robot <yi1.lai@intel.com> Closes: https://lore.kernel.org/oe-lkp/202610130959.d3ca4cfa-lkp@intel.com Link: https://patch.msgid.link/20261026-work-libfs-pin_fs-v1-1-99ef52ee10a3@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--fs/libfs.c12
1 files changed, 7 insertions, 5 deletions
diff --git a/fs/libfs.c b/fs/libfs.c
index 8e2cc627bc7f..96a6e9b1535f 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1103,10 +1103,11 @@ int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *c
if (IS_ERR(mnt))
return PTR_ERR(mnt);
spin_lock(&pin_fs_lock);
- if (!*mount)
+ if (!*mount) {
*mount = mnt;
+ mnt = NULL;
+ }
}
- mntget(*mount);
++*count;
spin_unlock(&pin_fs_lock);
mntput(mnt);
@@ -1116,11 +1117,12 @@ EXPORT_SYMBOL(simple_pin_fs);
void simple_release_fs(struct vfsmount **mount, int *count)
{
- struct vfsmount *mnt;
+ struct vfsmount *mnt = NULL;
spin_lock(&pin_fs_lock);
- mnt = *mount;
- if (!--*count)
+ if (!--*count) {
+ mnt = *mount;
*mount = NULL;
+ }
spin_unlock(&pin_fs_lock);
mntput(mnt);
}