From badb2cc8cf4aa37ecd8beda7aa5c003da93a9f74 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 29 May 2026 10:43:40 +0200 Subject: [PATCH 1/4] ext4: convert extents KUnit test to sget_fc() The extents KUnit test uses sget() to get an initialized superblock for its fake file_system_type. sget() predates fs_context and we want to retire it. Switch this caller over to sget_fc(). Add a no-op ext_init_fs_context() so fs_context_for_mount() has something to call on the fake fs_type. ext_set() now takes a struct fs_context * (still a no-op). extents_kunit_init() allocates the fc, hands it to sget_fc() and drops the fc reference once the sb is published. sget_fc() does not retain a pointer to it. No functional change for the test. Link: https://patch.msgid.link/20260529-work-sget-v2-1-57bbe08604e4@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/ext4/extents-test.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c index 6b53a3f39fcd..bd7795a82607 100644 --- a/fs/ext4/extents-test.c +++ b/fs/ext4/extents-test.c @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -130,14 +131,20 @@ static void ext_kill_sb(struct super_block *sb) generic_shutdown_super(sb); } -static int ext_set(struct super_block *sb, void *data) +static int ext_init_fs_context(struct fs_context *fc) +{ + return 0; +} + +static int ext_set(struct super_block *sb, struct fs_context *fc) { return 0; } static struct file_system_type ext_fs_type = { - .name = "extents test", - .kill_sb = ext_kill_sb, + .name = "extents test", + .init_fs_context = ext_init_fs_context, + .kill_sb = ext_kill_sb, }; static void extents_kunit_exit(struct kunit *test) @@ -223,6 +230,7 @@ static int extents_kunit_init(struct kunit *test) struct ext4_inode_info *ei; struct inode *inode; struct super_block *sb; + struct fs_context *fc; struct ext4_sb_info *sbi = NULL; struct kunit_ext_test_param *param = (struct kunit_ext_test_param *)(test->param_value); @@ -232,7 +240,13 @@ static int extents_kunit_init(struct kunit *test) if (sbi == NULL) return -ENOMEM; - sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL); + fc = fs_context_for_mount(&ext_fs_type, 0); + if (IS_ERR(fc)) { + kfree(sbi); + return PTR_ERR(fc); + } + sb = sget_fc(fc, NULL, ext_set); + put_fs_context(fc); if (IS_ERR(sb)) { kfree(sbi); return PTR_ERR(sb); From ed56dc8bbf9dfad4a5b2f8d29344594b1cf8ca27 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 29 May 2026 10:43:41 +0200 Subject: [PATCH 2/4] ext4: convert mballoc KUnit test to sget_fc() Same treatment as the extents KUnit test. The mballoc test uses sget() as a thin "give me an initialized superblock" wrapper for a fake file_system_type. Move it onto sget_fc() so sget() can go away. Add a no-op mbt_init_fs_context() so fs_context_for_mount() has something to call on the fake fs_type. mbt_set() now takes a struct fs_context * (still a no-op). mbt_ext4_alloc_super_block() allocates the fc, hands it to sget_fc() and drops the fc reference once the sb is published. No functional change. Link: https://patch.msgid.link/20260529-work-sget-v2-2-57bbe08604e4@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/ext4/mballoc-test.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index 90ed505fa4b1..d90da44aadbd 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -5,6 +5,7 @@ #include #include +#include #include #include "ext4.h" @@ -63,8 +64,14 @@ static void mbt_kill_sb(struct super_block *sb) generic_shutdown_super(sb); } +static int mbt_init_fs_context(struct fs_context *fc) +{ + return 0; +} + static struct file_system_type mbt_fs_type = { .name = "mballoc test", + .init_fs_context = mbt_init_fs_context, .kill_sb = mbt_kill_sb, }; @@ -127,7 +134,7 @@ static void mbt_mb_release(struct super_block *sb) kfree(sb->s_bdev); } -static int mbt_set(struct super_block *sb, void *data) +static int mbt_set(struct super_block *sb, struct fs_context *fc) { return 0; } @@ -136,13 +143,19 @@ static struct super_block *mbt_ext4_alloc_super_block(void) { struct mbt_ext4_super_block *fsb; struct super_block *sb; + struct fs_context *fc; struct ext4_sb_info *sbi; fsb = kzalloc_obj(*fsb); if (fsb == NULL) return NULL; - sb = sget(&mbt_fs_type, NULL, mbt_set, 0, NULL); + fc = fs_context_for_mount(&mbt_fs_type, 0); + if (IS_ERR(fc)) + goto out; + + sb = sget_fc(fc, NULL, mbt_set); + put_fs_context(fc); if (IS_ERR(sb)) goto out; From b50d895dfde2bd7a5a14b444e2c6921256fdb54b Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 29 May 2026 10:43:42 +0200 Subject: [PATCH 3/4] smb: client: convert cifs_smb3_do_mount() to sget_fc() The CIFS mount path already runs through fs_context: smb3_get_tree() calls smb3_get_tree_common() with a struct fs_context * in hand. But the fc is dropped on the way to sget(). Plumb it through to sget_fc() so the legacy sget() interface can go. cifs_smb3_do_mount() now takes (struct fs_context *, struct smb3_fs_context *). The old (fs_type, flags) pair is reconstructed from fc->fs_type and fc->sb_flags. The flags argument was always passed as 0 by the sole caller anyway. The cifs_dbg diagnostic now prints fc->sb_flags directly. cifs_match_super() and cifs_set_super() were the two void-data callbacks for sget(). The match callback now takes (struct super_block *, struct fs_context *) and reads struct cifs_mnt_data out of fc->sget_key. The set callback is gone entirely: sget_fc() pre-populates sb->s_fs_info from fc->s_fs_info before invoking set() so set_anon_super_fc() (which just allocates an anon bdev) is sufficient. Before sget_fc() we stash cifs_sb in fc->s_fs_info, the per-mount data in fc->sget_key and force fc->sb_flags to SB_NODIRATIME | SB_NOATIME to reproduce the previous hard-coded behaviour (alloc_super() reads fc->sb_flags). The original sb_flags is saved and restored around the call so the rest of the mount path sees the same fc semantics as before. mnt_data.flags keeps its historical value of 0 so the CIFS_MS_MASK comparison in compare_mount_options() returns the same (always-equal) result. No functional change. With this in place sget() has no remaining CIFS caller. Link: https://patch.msgid.link/20260529-work-sget-v2-3-57bbe08604e4@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/smb/client/cifsfs.c | 43 +++++++++++++++++++++++--------------- fs/smb/client/cifsfs.h | 3 ++- fs/smb/client/cifsproto.h | 3 ++- fs/smb/client/connect.c | 5 +++-- fs/smb/client/fs_context.c | 2 +- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 9f76b0347fa9..d5074e3fbb85 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -966,26 +967,19 @@ cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb) return dentry; } -static int cifs_set_super(struct super_block *sb, void *data) -{ - struct cifs_mnt_data *mnt_data = data; - sb->s_fs_info = mnt_data->cifs_sb; - return set_anon_super(sb, NULL); -} - struct dentry * -cifs_smb3_do_mount(struct file_system_type *fs_type, - int flags, struct smb3_fs_context *old_ctx) +cifs_smb3_do_mount(struct fs_context *fc, struct smb3_fs_context *old_ctx) { struct cifs_mnt_data mnt_data; struct cifs_sb_info *cifs_sb; struct super_block *sb; struct dentry *root; + unsigned int saved_sb_flags; int rc; if (cifsFYI) { - cifs_dbg(FYI, "%s: devname=%s flags=0x%x\n", __func__, - old_ctx->source, flags); + cifs_dbg(FYI, "%s: devname=%s sb_flags=0x%x\n", __func__, + old_ctx->source, fc->sb_flags); } else { cifs_info("Attempting to mount %s\n", old_ctx->source); } @@ -1012,7 +1006,7 @@ cifs_smb3_do_mount(struct file_system_type *fs_type, rc = cifs_mount(cifs_sb, cifs_sb->ctx); if (rc) { - if (!(flags & SB_SILENT)) + if (!(fc->sb_flags & SB_SILENT)) cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n", rc); root = ERR_PTR(rc); @@ -1021,12 +1015,27 @@ cifs_smb3_do_mount(struct file_system_type *fs_type, mnt_data.ctx = cifs_sb->ctx; mnt_data.cifs_sb = cifs_sb; - mnt_data.flags = flags; + mnt_data.flags = 0; - /* BB should we make this contingent on mount parm? */ - flags |= SB_NODIRATIME | SB_NOATIME; - - sb = sget(fs_type, cifs_match_super, cifs_set_super, flags, &mnt_data); + /* + * sb->s_flags is set from fc->sb_flags by alloc_super(). CIFS has + * historically forced SB_NODIRATIME | SB_NOATIME on every mount and + * ignored the caller-supplied SB_* flags. Preserve that behaviour by + * overriding fc->sb_flags around the sget_fc() call. + * + * Hand cifs_sb to sget_fc() via fc->s_fs_info; sget_fc() copies it + * onto sb->s_fs_info before running set() and clears fc->s_fs_info + * on successful publish. Pass the rest of the per-mount context to + * cifs_match_super() through fc->sget_key. + */ + saved_sb_flags = fc->sb_flags; + fc->sb_flags = SB_NODIRATIME | SB_NOATIME; + fc->s_fs_info = cifs_sb; + fc->sget_key = &mnt_data; + sb = sget_fc(fc, cifs_match_super, set_anon_super_fc); + fc->sget_key = NULL; + fc->s_fs_info = NULL; + fc->sb_flags = saved_sb_flags; if (IS_ERR(sb)) { cifs_umount(cifs_sb); return ERR_CAST(sb); diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index c455b15f2778..0a93f48924a5 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -144,8 +144,9 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file, long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg); void cifs_setsize(struct inode *inode, loff_t offset); +struct fs_context; struct smb3_fs_context; -struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, int flags, +struct dentry *cifs_smb3_do_mount(struct fs_context *fc, struct smb3_fs_context *old_ctx); char *cifs_silly_fullpath(struct dentry *dentry); diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index 4a25afda9448..a39572cbaadb 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -19,6 +19,7 @@ struct statfs; struct smb_rqst; struct smb3_fs_context; +struct fs_context; /* ***************************************************************** @@ -236,7 +237,7 @@ void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx); int cifs_mount_get_session(struct cifs_mount_ctx *mnt_ctx); int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx); int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx); -int cifs_match_super(struct super_block *sb, void *data); +int cifs_match_super(struct super_block *sb, struct fs_context *fc); int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx); void cifs_umount(struct cifs_sb_info *cifs_sb); void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index dcde25da468d..79762e6bbe50 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -6,6 +6,7 @@ * */ #include +#include #include #include #include @@ -2991,9 +2992,9 @@ static int match_prepath(struct super_block *sb, } int -cifs_match_super(struct super_block *sb, void *data) +cifs_match_super(struct super_block *sb, struct fs_context *fc) { - struct cifs_mnt_data *mnt_data = data; + struct cifs_mnt_data *mnt_data = fc->sget_key; struct smb3_fs_context *ctx; struct cifs_sb_info *cifs_sb; struct TCP_Server_Info *tcp_srv; diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index b9544eb0381b..6aba4e1c9c27 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -920,7 +920,7 @@ static int smb3_get_tree_common(struct fs_context *fc) struct dentry *root; int rc = 0; - root = cifs_smb3_do_mount(fc->fs_type, 0, ctx); + root = cifs_smb3_do_mount(fc, ctx); if (IS_ERR(root)) return PTR_ERR(root); From 12863b36bfe4ac0aa93e984c72b2bcd8f08402aa Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 29 May 2026 10:43:43 +0200 Subject: [PATCH 4/4] fs: retire sget() sget() and sget_fc() have lived side by side as near-duplicate find-or-create-and-publish helpers for the legacy and fs_context mount APIs. The three remaining in-tree callers (CIFS plus the ext4 extents and mballoc KUnit tests) have all been moved to sget_fc(). Nothing calls sget() anymore. Delete sget() from fs/super.c and the prototype in . Update the two comments that referred to "sget()" or "sget{_fc}()" to just say "sget_fc()". This removes ~60 lines of code that only existed to be kept in lockstep with sget_fc() on every superblock publish-path change. Link: https://patch.msgid.link/20260529-work-sget-v2-4-57bbe08604e4@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/btrfs/super.c | 2 +- fs/super.c | 71 ++++------------------------------------------ include/linux/fs.h | 4 --- 3 files changed, 6 insertions(+), 71 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index b26aa9169e83..636154861d7c 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2052,7 +2052,7 @@ static int btrfs_get_tree_subvol(struct fs_context *fc) * then open_ctree will properly initialize the file system specific * settings later. btrfs_init_fs_info initializes the static elements * of the fs_info (locks and such) to make cleanup easier if we find a - * superblock with our given fs_devices later on at sget() time. + * superblock with our given fs_devices later on at sget_fc() time. */ fs_info = kvzalloc_obj(struct btrfs_fs_info); if (!fs_info) diff --git a/fs/super.c b/fs/super.c index 378e81efe643..5fe8cea9f8fe 100644 --- a/fs/super.c +++ b/fs/super.c @@ -328,7 +328,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags, init_rwsem(&s->s_umount); lockdep_set_class(&s->s_umount, &type->s_umount_key); /* - * sget() can have s_umount recursion. + * sget_fc() can have s_umount recursion. * * When it cannot find a suitable sb, it allocates a new * one (this one), and tries again to find a suitable old @@ -439,7 +439,7 @@ static void kill_super_notify(struct super_block *sb) /* * Remove it from @fs_supers so it isn't found by new - * sget{_fc}() walkers anymore. Any concurrent mounter still + * sget_fc() walkers anymore. Any concurrent mounter still * managing to grab a temporary reference is guaranteed to * already see SB_DYING and will wait until we notify them about * SB_DEAD. @@ -517,7 +517,7 @@ EXPORT_SYMBOL(deactivate_super); * @sb: superblock to acquire * * Acquire a temporary reference on a superblock and try to trade it for - * an active reference. This is used in sget{_fc}() to wait for a + * an active reference. This is used in sget_fc() to wait for a * superblock to either become SB_BORN or for it to pass through * sb->kill() and be marked as SB_DEAD. * @@ -673,11 +673,11 @@ void generic_shutdown_super(struct super_block *sb) /* * Broadcast to everyone that grabbed a temporary reference to this * superblock before we removed it from @fs_supers that the superblock - * is dying. Every walker of @fs_supers outside of sget{_fc}() will now + * is dying. Every walker of @fs_supers outside of sget_fc() will now * discard this superblock and treat it as dead. * * We leave the superblock on @fs_supers so it can be found by - * sget{_fc}() until we passed sb->kill_sb(). + * sget_fc() until we passed sb->kill_sb(). */ super_wake(sb, SB_DYING); super_unlock_excl(sb); @@ -808,67 +808,6 @@ struct super_block *sget_fc(struct fs_context *fc, } EXPORT_SYMBOL(sget_fc); -/** - * sget - find or create a superblock - * @type: filesystem type superblock should belong to - * @test: comparison callback - * @set: setup callback - * @flags: mount flags - * @data: argument to each of them - */ -struct super_block *sget(struct file_system_type *type, - int (*test)(struct super_block *,void *), - int (*set)(struct super_block *,void *), - int flags, - void *data) -{ - struct user_namespace *user_ns = current_user_ns(); - struct super_block *s = NULL; - struct super_block *old; - int err; - -retry: - spin_lock(&sb_lock); - if (test) { - hlist_for_each_entry(old, &type->fs_supers, s_instances) { - if (!test(old, data)) - continue; - if (user_ns != old->s_user_ns) { - spin_unlock(&sb_lock); - destroy_unused_super(s); - return ERR_PTR(-EBUSY); - } - if (!grab_super(old)) - goto retry; - destroy_unused_super(s); - return old; - } - } - if (!s) { - spin_unlock(&sb_lock); - s = alloc_super(type, flags, user_ns); - if (!s) - return ERR_PTR(-ENOMEM); - goto retry; - } - - err = set(s, data); - if (err) { - spin_unlock(&sb_lock); - destroy_unused_super(s); - return ERR_PTR(err); - } - s->s_type = type; - strscpy(s->s_id, type->name, sizeof(s->s_id)); - list_add_tail(&s->s_list, &super_blocks); - hlist_add_head(&s->s_instances, &type->fs_supers); - spin_unlock(&sb_lock); - get_filesystem(type); - shrinker_register(s->s_shrink); - return s; -} -EXPORT_SYMBOL(sget); - void drop_super(struct super_block *sb) { super_unlock_shared(sb); diff --git a/include/linux/fs.h b/include/linux/fs.h index 11559c513dfb..6dbe3218dc1e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2327,10 +2327,6 @@ void free_anon_bdev(dev_t); struct super_block *sget_fc(struct fs_context *fc, int (*test)(struct super_block *, struct fs_context *), int (*set)(struct super_block *, struct fs_context *)); -struct super_block *sget(struct file_system_type *type, - int (*test)(struct super_block *,void *), - int (*set)(struct super_block *,void *), - int flags, void *data); struct super_block *sget_dev(struct fs_context *fc, dev_t dev); /* Alas, no aliases. Too much hassle with bringing module.h everywhere */