Skip to content

Commit 50e10e9

Browse files
committed
sanitize handling of long-term internal mounts
JIRA: https://issues.redhat.com/browse/RHEL-113576 commit 24368a7 Author: Al Viro <viro@zeniv.linux.org.uk> Date: Fri May 2 21:32:01 2025 -0400 sanitize handling of long-term internal mounts Original rationale for those had been the reduced cost of mntput() for the stuff that is mounted somewhere. Mount refcount increments and decrements are frequent; what's worse, they tend to concentrate on the same instances and cacheline pingpong is quite noticable. As the result, mount refcounts are per-cpu; that allows a very cheap increment. Plain decrement would be just as easy, but decrement-and-test is anything but (we need to add the components up, with exclusion against possible increment-from-zero, etc.). Fortunately, there is a very common case where we can tell that decrement won't be the final one - if the thing we are dropping is currently mounted somewhere. We have an RCU delay between the removal from mount tree and dropping the reference that used to pin it there, so we can just take rcu_read_lock() and check if the victim is mounted somewhere. If it is, we can go ahead and decrement without and further checks - the reference we are dropping is not the last one. If it isn't, we get all the fun with locking, carefully adding up components, etc., but the majority of refcount decrements end up taking the fast path. There is a major exception, though - pipes and sockets. Those live on the internal filesystems that are not going to be mounted anywhere. They are not going to be _un_mounted, of course, so having to take the slow path every time a pipe or socket gets closed is really obnoxious. Solution had been to mark them as long-lived ones - essentially faking "they are mounted somewhere" indicator. With minor modification that works even for ones that do eventually get dropped - all it takes is making sure we have an RCU delay between clearing the "mounted somewhere" indicator and dropping the reference. There are some additional twists (if you want to drop a dozen of such internal mounts, you'd be better off with clearing the indicator on all of them, doing an RCU delay once, then dropping the references), but in the basic form it had been * use kern_mount() if you want your internal mount to be a long-term one. * use kern_unmount() to undo that. Unfortunately, the things did rot a bit during the mount API reshuffling. In several cases we have lost the "fake the indicator" part; kern_unmount() on the unmount side remained (it doesn't warn if you use it on a mount without the indicator), but all benefits regaring mntput() cost had been lost. To get rid of that bitrot, let's add a new helper that would work with fs_context-based API: fc_mount_longterm(). It's a counterpart of fc_mount() that does, on success, mark its result as long-term. It must be paired with kern_unmount() or equivalents. Converted: 1) mqueue (it used to use kern_mount_data() and the umount side is still as it used to be) 2) hugetlbfs (used to use kern_mount_data(), internal mount is never unmounted in this one) 3) i915 gemfs (used to be kern_mount() + manual remount to set options, still uses kern_unmount() on umount side) 4) v3d gemfs (copied from i915) Reviewed-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: José Expósito <jexposit@redhat.com>
1 parent 03ac6a1 commit 50e10e9

File tree

5 files changed

+30
-5
lines changed

5 files changed

+30
-5
lines changed

drivers/gpu/drm/i915/gem/i915_gemfs.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,23 @@
66

77
#include <linux/fs.h>
88
#include <linux/mount.h>
9+
#include <linux/fs_context.h>
910

1011
#include "i915_drv.h"
1112
#include "i915_gemfs.h"
1213
#include "i915_utils.h"
1314

15+
static int add_param(struct fs_context *fc, const char *key, const char *val)
16+
{
17+
return vfs_parse_fs_string(fc, key, val, strlen(val));
18+
}
19+
1420
void i915_gemfs_init(struct drm_i915_private *i915)
1521
{
16-
char huge_opt[] = "huge=within_size"; /* r/w */
1722
struct file_system_type *type;
23+
struct fs_context *fc;
1824
struct vfsmount *gemfs;
25+
int ret;
1926

2027
/*
2128
* By creating our own shmemfs mountpoint, we can pass in
@@ -39,8 +46,16 @@ void i915_gemfs_init(struct drm_i915_private *i915)
3946
if (!type)
4047
goto err;
4148

42-
gemfs = vfs_kern_mount(type, SB_KERNMOUNT, type->name, huge_opt);
43-
if (IS_ERR(gemfs))
49+
fc = fs_context_for_mount(type, SB_KERNMOUNT);
50+
if (IS_ERR(fc))
51+
goto err;
52+
ret = add_param(fc, "source", "tmpfs");
53+
if (!ret)
54+
ret = add_param(fc, "huge", "within_size");
55+
if (!ret)
56+
gemfs = fc_mount_longterm(fc);
57+
put_fs_context(fc);
58+
if (ret)
4459
goto err;
4560

4661
i915->mm.gemfs = gemfs;

fs/hugetlbfs/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1647,7 +1647,7 @@ static struct vfsmount *__init mount_one_hugetlbfs(struct hstate *h)
16471647
} else {
16481648
struct hugetlbfs_fs_context *ctx = fc->fs_private;
16491649
ctx->hstate = h;
1650-
mnt = fc_mount(fc);
1650+
mnt = fc_mount_longterm(fc);
16511651
put_fs_context(fc);
16521652
}
16531653
if (IS_ERR(mnt))

fs/namespace.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,15 @@ struct vfsmount *fc_mount(struct fs_context *fc)
998998
}
999999
EXPORT_SYMBOL(fc_mount);
10001000

1001+
struct vfsmount *fc_mount_longterm(struct fs_context *fc)
1002+
{
1003+
struct vfsmount *mnt = fc_mount(fc);
1004+
if (!IS_ERR(mnt))
1005+
real_mount(mnt)->mnt_ns = MNT_NS_INTERNAL;
1006+
return mnt;
1007+
}
1008+
EXPORT_SYMBOL(fc_mount_longterm);
1009+
10011010
struct vfsmount *vfs_kern_mount(struct file_system_type *type,
10021011
int flags, const char *name,
10031012
void *data)

include/linux/mount.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ extern int __mnt_want_write(struct vfsmount *);
9696
extern void __mnt_drop_write(struct vfsmount *);
9797

9898
extern struct vfsmount *fc_mount(struct fs_context *fc);
99+
extern struct vfsmount *fc_mount_longterm(struct fs_context *fc);
99100
extern struct vfsmount *vfs_create_mount(struct fs_context *fc);
100101
extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
101102
int flags, const char *name,

ipc/mqueue.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ static struct vfsmount *mq_create_mount(struct ipc_namespace *ns)
482482
put_user_ns(fc->user_ns);
483483
fc->user_ns = get_user_ns(ctx->ipc_ns->user_ns);
484484

485-
mnt = fc_mount(fc);
485+
mnt = fc_mount_longterm(fc);
486486
put_fs_context(fc);
487487
return mnt;
488488
}

0 commit comments

Comments
 (0)