[PATCH V2] devtmpfs: restore mount option reconfiguration on get_tree
From: Eric Sandeen
Date: Wed Aug 12 2026 - 17:44:56 EST
commit cb0e0a8bf4e1 ("devtmpfs: replace ->mount with ->get_tree in
public instance") converted the public devtmpfs instance to the new
mount API but broke the ability to reconfigure the shared superblock
options via subsequent mount() calls, as had been fixed once in
a6097180d884 ("devtmpfs regression fix: reconfigure on each mount")
This makes boot-time defaults permanent.
Fix this using an approach suggested by Al Viro: at init_fs_context
time, create a reconfigure-mode context via fs_context_for_reconfigure()
and save it in fc->fs_private. The devtmpfs parse ops can then use
this context to parse options appropriately for the underlying filesystem.
This also removes the devtmpfs_configure_context() one-time ops-copying
approach and the global mutable ops table it required.
Fixes: cb0e0a8bf4e1 ("devtmpfs: replace ->mount with ->get_tree in public instance")
Reported-by: Simon Liebold <simonlie@xxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
Suggested-by: Al Viro <viro@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Eric Sandeen <sandeen@xxxxxxxxxx>
---
I think this might be a better approach; more involved, but it uses the
right type of context for reconfigure and avoids the nasty ops copying
I wrote the first time. :(
First, sorry for breaking this. Second, Al please yell if I have mis-attributed
or mis-implemented your suggestion from months back. Third, I have done only
light testing on this but it seems ok, built and booted with CONFIG_TMPFS=y
and confirmed that a fresh "mount" with new options takes hold, and confirmed
it builds without CONFIG_TMPFS configured though I don't have a system
that wants to boot that config.
Thoughts?
drivers/base/devtmpfs.c | 108 ++++++++++++++++++++++++----------------
1 file changed, 64 insertions(+), 44 deletions(-)
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index b1c4ceb65026..c32d3b963f2e 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -72,39 +72,90 @@ static struct file_system_type internal_fs_type = {
.kill_sb = kill_anon_super,
};
-/* Simply take a ref on the existing mount */
+struct devtmpfs_context {
+ struct fs_context *fc;
+};
+
+static void devtmpfs_free(struct fs_context *fc)
+{
+ struct devtmpfs_context *ctx = fc->fs_private;
+
+ if (ctx) {
+ put_fs_context(ctx->fc);
+ kfree(ctx);
+ }
+}
+
+static int devtmpfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+ struct devtmpfs_context *ctx = fc->fs_private;
+
+ return ctx->fc->ops->parse_param(ctx->fc, param);
+}
+
+static int devtmpfs_parse_monolithic(struct fs_context *fc, void *data)
+{
+ struct devtmpfs_context *ctx = fc->fs_private;
+
+ if (ctx->fc->ops->parse_monolithic)
+ return ctx->fc->ops->parse_monolithic(ctx->fc, data);
+ return generic_parse_monolithic(ctx->fc, data);
+}
+
static int devtmpfs_get_tree(struct fs_context *fc)
{
+ struct devtmpfs_context *ctx = fc->fs_private;
struct super_block *sb = mnt->mnt_sb;
+ int err;
atomic_inc(&sb->s_active);
down_write(&sb->s_umount);
+
+ if (ctx->fc->ops->reconfigure) {
+ err = ctx->fc->ops->reconfigure(ctx->fc);
+ if (err) {
+ deactivate_locked_super(sb);
+ return err;
+ }
+ }
+
fc->root = dget(sb->s_root);
return 0;
}
-/* Ops are filled in during init depending on underlying shmem or ramfs type */
-static struct fs_context_operations devtmpfs_context_ops = {};
+static const struct fs_context_operations devtmpfs_context_ops = {
+ .free = devtmpfs_free,
+ .parse_param = devtmpfs_parse_param,
+ .parse_monolithic = devtmpfs_parse_monolithic,
+ .get_tree = devtmpfs_get_tree,
+};
-/* Call the underlying initialization and set to our ops */
static int devtmpfs_init_fs_context(struct fs_context *fc)
{
- int ret;
-#ifdef CONFIG_TMPFS
- ret = shmem_init_fs_context(fc);
-#else
- ret = ramfs_init_fs_context(fc);
-#endif
- if (ret < 0)
- return ret;
+ struct devtmpfs_context *ctx;
+ int err;
+
+ ctx = kzalloc_obj(struct devtmpfs_context);
+ if (!ctx)
+ return -ENOMEM;
+
+ /* Each mount will reconfigure the shared superblock w/ new options */
+ ctx->fc = fs_context_for_reconfigure(mnt->mnt_root,
+ mnt->mnt_sb->s_flags, MS_RMT_MASK);
+ if (IS_ERR(ctx->fc)) {
+ err = PTR_ERR(ctx->fc);
+ kfree(ctx);
+ return err;
+ }
+ fc->fs_private = ctx;
fc->ops = &devtmpfs_context_ops;
return 0;
}
static struct file_system_type dev_fs_type = {
- .name = "devtmpfs",
+ .name = "devtmpfs",
.init_fs_context = devtmpfs_init_fs_context,
};
@@ -442,31 +493,6 @@ static int __ref devtmpfsd(void *p)
return 0;
}
-/*
- * Get the underlying (shmem/ramfs) context ops to build ours
- */
-static int devtmpfs_configure_context(void)
-{
- struct fs_context *fc;
-
- fc = fs_context_for_reconfigure(mnt->mnt_root, mnt->mnt_sb->s_flags,
- MS_RMT_MASK);
- if (IS_ERR(fc))
- return PTR_ERR(fc);
-
- /* Set up devtmpfs_context_ops based on underlying type */
- devtmpfs_context_ops.free = fc->ops->free;
- devtmpfs_context_ops.dup = fc->ops->dup;
- devtmpfs_context_ops.parse_param = fc->ops->parse_param;
- devtmpfs_context_ops.parse_monolithic = fc->ops->parse_monolithic;
- devtmpfs_context_ops.get_tree = &devtmpfs_get_tree;
- devtmpfs_context_ops.reconfigure = fc->ops->reconfigure;
-
- put_fs_context(fc);
-
- return 0;
-}
-
/*
* Create devtmpfs instance, driver-core devices will add their device
* nodes here.
@@ -482,12 +508,6 @@ int __init devtmpfs_init(void)
return PTR_ERR(mnt);
}
- err = devtmpfs_configure_context();
- if (err) {
- pr_err("unable to configure devtmpfs type %d\n", err);
- return err;
- }
-
err = register_filesystem(&dev_fs_type);
if (err) {
pr_err("unable to register devtmpfs type %d\n", err);
--
2.55.0