From: Hannes Reinecke <hare@kernel.org>
To: Christian Brauner <brauner@kernel.org>
Cc: Jan Kara <jack@suse.cz>, Andreas Hindborg <a.hindborg@kernel.org>,
linux-fsdevel@vger.kernel.org, linux-nvme@lists.infradead.org,
Hannes Reinecke <hare@suse.de>
Subject: [PATCH 2/8] fs/configfs: dynamically allocate super_info
Date: Sat, 13 Jun 2026 13:14:31 +0200 [thread overview]
Message-ID: <20260613111437.101763-3-hare@kernel.org> (raw)
In-Reply-To: <20260613111437.101763-1-hare@kernel.org>
From: Hannes Reinecke <hare@suse.de>
Define 'struct configfs_super_info' to hold all required structures
and allocate the filesystem contents dynamically.
Signed-off-by: Hannes Reinecke <hare@suse.de>
---
fs/configfs/configfs_internal.h | 13 ++++
fs/configfs/dir.c | 40 ++++++-----
fs/configfs/mount.c | 117 +++++++++++++++++++++++++-------
3 files changed, 127 insertions(+), 43 deletions(-)
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index acdeea8e2d69..e91f4249b83a 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -44,6 +44,17 @@ struct configfs_dirent {
struct configfs_fragment *s_frag;
};
+struct configfs_super_info {
+ struct configfs_dirent root;
+ struct config_group group;
+ struct list_head subsys_list;
+ struct mutex subsys_mutex;
+ struct vfsmount *mnt;
+ unsigned int mnt_count;
+ u64 ns_id;
+ refcount_t ref;
+};
+
#define CONFIGFS_ROOT 0x0001
#define CONFIGFS_DIR 0x0002
#define CONFIGFS_ITEM_ATTR 0x0004
@@ -81,6 +92,8 @@ extern int configfs_setattr(struct mnt_idmap *idmap,
extern struct dentry *configfs_pin_fs(void);
extern void configfs_release_fs(void);
+extern struct configfs_super_info *configfs_get_super_info(u64 ns_id);
+extern void configfs_put_super_info(struct configfs_super_info *info);
extern const struct file_operations configfs_dir_operations;
extern const struct file_operations configfs_file_operations;
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 4da2fec2b64e..57719bee0c07 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -34,14 +34,6 @@
*/
DEFINE_SPINLOCK(configfs_dirent_lock);
-/*
- * All of link_obj/unlink_obj/link_group/unlink_group require that
- * subsys->su_mutex is held.
- * But parent configfs_subsystem is NULL when config_item is root.
- * Use this mutex when config_item is root.
- */
-static DEFINE_MUTEX(configfs_subsystem_mutex);
-
static void configfs_d_iput(struct dentry * dentry,
struct inode * inode)
{
@@ -1841,20 +1833,27 @@ EXPORT_SYMBOL(configfs_unregister_default_group);
int configfs_register_subsystem(struct configfs_subsystem *subsys)
{
- int err;
+ struct configfs_super_info *info = configfs_get_super_info(0);
struct config_group *group = &subsys->su_group;
struct dentry *dentry;
struct dentry *root;
struct configfs_dirent *sd;
struct configfs_fragment *frag;
+ int err;
+
+ if (IS_ERR(info))
+ return PTR_ERR(info);
frag = new_fragment();
- if (!frag)
+ if (!frag) {
+ configfs_put_super_info(info);
return -ENOMEM;
+ }
root = configfs_pin_fs();
if (IS_ERR(root)) {
put_fragment(frag);
+ configfs_put_super_info(info);
return PTR_ERR(root);
}
@@ -1862,9 +1861,9 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
group->cg_item.ci_name = group->cg_item.ci_namebuf;
sd = root->d_fsdata;
- mutex_lock(&configfs_subsystem_mutex);
+ mutex_lock(&info->subsys_mutex);
link_group(to_config_group(sd->s_element), group);
- mutex_unlock(&configfs_subsystem_mutex);
+ mutex_unlock(&info->subsys_mutex);
inode_lock_nested(d_inode(root), I_MUTEX_PARENT);
@@ -1892,26 +1891,32 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
inode_unlock(d_inode(root));
if (err) {
- mutex_lock(&configfs_subsystem_mutex);
+ mutex_lock(&info->subsys_mutex);
unlink_group(group);
- mutex_unlock(&configfs_subsystem_mutex);
+ mutex_unlock(&info->subsys_mutex);
configfs_release_fs();
}
put_fragment(frag);
-
+ configfs_put_super_info(info);
return err;
}
void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
{
+ struct configfs_super_info *info = configfs_get_super_info(0);
struct config_group *group = &subsys->su_group;
struct dentry *dentry = dget(group->cg_item.ci_dentry);
struct dentry *root = dentry->d_sb->s_root;
struct configfs_dirent *sd = dentry->d_fsdata;
struct configfs_fragment *frag = sd->s_frag;
+ if (WARN_ON(IS_ERR(info)))
+ return;
+
if (dentry->d_parent != root) {
pr_err("Tried to unregister non-subsystem!\n");
+ dput(dentry);
+ configfs_put_super_info(info);
return;
}
@@ -1941,10 +1946,11 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
dput(dentry);
- mutex_lock(&configfs_subsystem_mutex);
+ mutex_lock(&info->subsys_mutex);
unlink_group(group);
- mutex_unlock(&configfs_subsystem_mutex);
+ mutex_unlock(&info->subsys_mutex);
configfs_release_fs();
+ configfs_put_super_info(info);
}
EXPORT_SYMBOL(configfs_register_subsystem);
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index e5c01d5e4d2d..f49c8636eccb 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -22,10 +22,9 @@
/* Random magic number */
#define CONFIGFS_MAGIC 0x62656570
-static struct vfsmount *configfs_mount = NULL;
struct kmem_cache *configfs_dir_cachep;
-static int configfs_mnt_count = 0;
-
+static DEFINE_XARRAY(configfs_super_xa);
+static struct configfs_super_info *configfs_root = NULL;
static void configfs_free_inode(struct inode *inode)
{
@@ -40,13 +39,6 @@ static const struct super_operations configfs_ops = {
.free_inode = configfs_free_inode,
};
-static struct config_group configfs_root_group = {
- .cg_item = {
- .ci_namebuf = "root",
- .ci_name = configfs_root_group.cg_item.ci_namebuf,
- },
-};
-
int configfs_is_root(struct config_item *item)
{
struct configfs_dirent *sd =
@@ -54,16 +46,67 @@ int configfs_is_root(struct config_item *item)
return sd->s_type == CONFIGFS_ROOT;
}
-static struct configfs_dirent configfs_root = {
- .s_sibling = LIST_HEAD_INIT(configfs_root.s_sibling),
- .s_children = LIST_HEAD_INIT(configfs_root.s_children),
- .s_element = &configfs_root_group.cg_item,
- .s_type = CONFIGFS_ROOT,
- .s_iattr = NULL,
-};
+static void configfs_fill_super_info(struct configfs_super_info *info)
+{
+ INIT_LIST_HEAD(&info->root.s_sibling);
+ INIT_LIST_HEAD(&info->root.s_children);
+ info->root.s_type = CONFIGFS_ROOT;
+ info->root.s_element = &info->group.cg_item;
+ strcpy(info->group.cg_item.ci_namebuf, "root");
+ info->group.cg_item.ci_name = info->group.cg_item.ci_namebuf;
+ config_group_init(&info->group);
+ INIT_LIST_HEAD(&info->subsys_list);
+ mutex_init(&info->subsys_mutex);
+ refcount_set(&info->ref, 1);
+ info->mnt_count = 0;
+}
+
+struct configfs_super_info *configfs_get_super_info(u64 ns_id)
+{
+ struct configfs_super_info *info;
+ int err;
+
+ info = xa_load(&configfs_super_xa, ns_id);
+ if (info) {
+ if (!refcount_inc_not_zero((&info->ref))) {
+ pr_warn("%s: ns %llu already freed\n",
+ __func__, ns_id);
+ return ERR_PTR(-EBUSY);
+ }
+ pr_info("%s: use ns %llu\n",
+ __func__, ns_id);
+ return info;
+ }
+ info = kzalloc_obj(*info);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ info->ns_id = ns_id;
+ configfs_fill_super_info(info);
+ err = xa_insert(&configfs_super_xa, ns_id,
+ info, GFP_KERNEL);
+ if (err < 0) {
+ kfree(info);
+ return ERR_PTR(err);
+ }
+ pr_info("%s: alloc ns %llu\n", __func__, info->ns_id);
+ return info;
+}
+
+void configfs_put_super_info(struct configfs_super_info *info)
+{
+ if (refcount_dec_and_test(&info->ref)) {
+ pr_info("%s: ns %llu free fs info\n",
+ __func__, info->ns_id);
+ xa_erase(&configfs_super_xa, info->ns_id);
+ WARN_ON(!list_empty(&info->subsys_list));
+ kfree(info);
+ }
+}
static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
+ struct configfs_super_info *info = configfs_get_super_info(0);
struct inode *inode;
struct dentry *root;
@@ -74,7 +117,7 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_time_gran = 1;
inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
- &configfs_root, sb);
+ &info->root, sb);
if (inode) {
inode->i_op = &configfs_root_inode_operations;
inode->i_fop = &configfs_dir_operations;
@@ -82,18 +125,21 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
inc_nlink(inode);
} else {
pr_debug("could not get root inode\n");
+ configfs_put_super_info(info);
return -ENOMEM;
}
root = d_make_root(inode);
if (!root) {
pr_debug("%s: could not get root dentry!\n",__func__);
+ configfs_put_super_info(info);
return -ENOMEM;
}
- config_group_init(&configfs_root_group);
- configfs_root_group.cg_item.ci_dentry = root;
- root->d_fsdata = &configfs_root;
+ config_group_init(&info->group);
+ info->group.cg_item.ci_dentry = root;
+ root->d_fsdata = &info->root;
sb->s_root = root;
+ sb->s_fs_info = info;
set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
sb->s_d_flags |= DCACHE_DONTCACHE;
return 0;
@@ -114,24 +160,33 @@ static int configfs_init_fs_context(struct fs_context *fc)
return 0;
}
+static void configfs_kill_sb(struct super_block *sb)
+{
+ struct configfs_super_info *info = sb->s_fs_info;
+
+ kill_anon_super(sb);
+ configfs_put_super_info(info);
+ sb->s_fs_info = NULL;
+}
+
static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE,
.name = "configfs",
.init_fs_context = configfs_init_fs_context,
- .kill_sb = kill_anon_super,
+ .kill_sb = configfs_kill_sb,
};
MODULE_ALIAS_FS("configfs");
struct dentry *configfs_pin_fs(void)
{
- int err = simple_pin_fs(&configfs_fs_type, &configfs_mount,
- &configfs_mnt_count);
- return err ? ERR_PTR(err) : configfs_mount->mnt_root;
+ int err = simple_pin_fs(&configfs_fs_type, &configfs_root->mnt,
+ &configfs_root->mnt_count);
+ return err ? ERR_PTR(err) : configfs_root->mnt->mnt_root;
}
void configfs_release_fs(void)
{
- simple_release_fs(&configfs_mount, &configfs_mnt_count);
+ simple_release_fs(&configfs_root->mnt, &configfs_root->mnt_count);
}
@@ -153,7 +208,16 @@ static int __init configfs_init(void)
if (err)
goto out3;
+ configfs_root = configfs_get_super_info(0);
+ if (IS_ERR(configfs_root)) {
+ err = PTR_ERR(configfs_root);
+ goto out4;
+ }
+
return 0;
+out4:
+ pr_err("Unable to get initlal root context\n");
+ unregister_filesystem(&configfs_fs_type);
out3:
pr_err("Unable to register filesystem!\n");
sysfs_remove_mount_point(kernel_kobj, "config");
@@ -166,6 +230,7 @@ static int __init configfs_init(void)
static void __exit configfs_exit(void)
{
+ configfs_put_super_info(configfs_root);
unregister_filesystem(&configfs_fs_type);
sysfs_remove_mount_point(kernel_kobj, "config");
kmem_cache_destroy(configfs_dir_cachep);
--
2.51.0
next prev parent reply other threads:[~2026-06-13 11:15 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
2026-06-13 11:14 ` [PATCH 1/8] fs/configfs: rework configfs_is_root() Hannes Reinecke
2026-06-13 11:14 ` Hannes Reinecke [this message]
2026-06-13 11:14 ` [PATCH 3/8] fs/configfs: separate out configfs_{link,unlink}_root() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 4/8] fs/namespace: implement mnt_clone_direct() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 5/8] fs/configfs: add superblock as attribute to configfs_pin_fs() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 6/8] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks Hannes Reinecke
2026-06-13 11:14 ` [PATCH 7/8] fs/configfs: switch to get_tree_keyed() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 8/8] nvmet: make configfs setup namespace aware Hannes Reinecke
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260613111437.101763-3-hare@kernel.org \
--to=hare@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=brauner@kernel.org \
--cc=hare@suse.de \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-nvme@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox