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>
This commit is contained in:
Christian Brauner 2026-10-26 15:00:42 +01:00
parent eeecea75ec
commit 02a336a831
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -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);
}