mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Hannes Reinecke <hare@kernel.org>
To: Andreas Hindborg <a.hindborg@kernel.org>,
	 Breno Leitao <leitao@debian.org>,
	Alexander Viro <viro@zeniv.linux.org.uk>,
	 Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>,
	 Christoph Hellwig <hch@lst.de>, Sagi Grimberg <sagi@grimberg.me>,
	 Chaitanya Kulkarni <kch@nvidia.com>
Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	 linux-nvme@lists.infradead.org,
	Hannes Reinecke <hare@kernel.org>
Subject: [PATCH RFC v3 02/12] fs/configfs: dynamically allocate super_info
Date: Tue, 23 Jun 2026 11:15:47 +0200	[thread overview]
Message-ID: <20260623-configfs-ns-v3-2-841c100fd5dd@kernel.org> (raw)
In-Reply-To: <20260623-configfs-ns-v3-0-841c100fd5dd@kernel.org>

Define 'struct configfs_super_info' to hold all required structures
and allocate the filesystem contents dynamically.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 fs/configfs/configfs_internal.h |  13 ++++
 fs/configfs/dir.c               |  44 +++++++-----
 fs/configfs/mount.c             | 148 ++++++++++++++++++++++++++++++----------
 include/linux/configfs.h        |   1 +
 4 files changed, 154 insertions(+), 52 deletions(-)

diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index 6261e1f3ec201a58a5d46b08dfbced8c2a44d82b..94b9e306709150e7a123c46bb359077c6dc917ad 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;
+	int mnt_count;
+	struct net *net_ns;
+	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(struct net *net_ns);
+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 3c88f13f1ca2e9f0ee61ab5003356c070f2c05be..1e9c82e02741798930b08c96fb59a2767c7e00fc 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)
 {
@@ -1871,20 +1863,28 @@ EXPORT_SYMBOL(configfs_unregister_default_group);
 
 int configfs_register_subsystem(struct configfs_subsystem *subsys)
 {
-	int err;
+	struct configfs_super_info *info;
 	struct config_group *group = &subsys->su_group;
 	struct dentry *dentry;
 	struct dentry *root;
 	struct configfs_dirent *sd;
 	struct configfs_fragment *frag;
+	int err;
+
+	info = configfs_get_super_info(&init_net);
+	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);
 	}
 
@@ -1892,9 +1892,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);
 
@@ -1921,26 +1921,35 @@ 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;
 	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;
 
+	info = configfs_get_super_info(&init_net);
+	if (WARN_ON(IS_ERR(info))) {
+		dput(dentry);
+		return;
+	}
+
 	if (dentry->d_parent != root) {
 		pr_err("Tried to unregister non-subsystem!\n");
+		dput(dentry);
+		configfs_put_super_info(info);
 		return;
 	}
 
@@ -1970,10 +1979,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 88da7b428e52f2f4ab139ad3907101f685d617b1..067bb1099a52ca2216d1b2881429a3fd1811c706 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -22,10 +22,18 @@
 /* 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;
 
+static u64 configfs_ns_id(struct net *net_ns)
+{
+	struct ns_common *ns = net_ns ? to_ns_common(net_ns) : NULL;
+
+	if (!ns || net_ns == &init_net)
+		return 0;
+	return ns->ns_id;
+}
 
 static void configfs_free_inode(struct inode *inode)
 {
@@ -40,13 +48,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,
-	},
-};
-
 bool configfs_is_root(struct config_item *item)
 {
 	struct configfs_dirent *sd;
@@ -57,19 +58,76 @@ bool 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;
+	strscpy(info->group.cg_item.ci_namebuf, "root", 4);
+	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(struct net *net_ns)
+{
+	struct configfs_super_info *info;
+	u64 ns_id = configfs_ns_id(net_ns);
+	int err;
+
+	xa_lock(&configfs_super_xa);
+	info = xa_load(&configfs_super_xa, ns_id);
+	if (info) {
+		if (!refcount_inc_not_zero((&info->ref)))
+			info = ERR_PTR(-EBUSY);
+		xa_unlock(&configfs_super_xa);
+		return info;
+	}
+	info = kzalloc_obj(*info);
+	if (!info) {
+		xa_unlock(&configfs_super_xa);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	info->net_ns = get_net(net_ns);
+	configfs_fill_super_info(info);
+	err = __xa_insert(&configfs_super_xa, ns_id,
+			  info, GFP_KERNEL);
+	xa_unlock(&configfs_super_xa);
+	if (err < 0) {
+		put_net(info->net_ns);
+		kfree(info);
+		return ERR_PTR(err);
+	}
+	return info;
+}
+
+void configfs_put_super_info(struct configfs_super_info *info)
+{
+	u64 ns_id = configfs_ns_id(info->net_ns);
+
+	xa_lock(&configfs_super_xa);
+	if (!refcount_dec_and_test(&info->ref)) {
+		xa_unlock(&configfs_super_xa);
+		return;
+	}
+	__xa_erase(&configfs_super_xa, ns_id);
+	xa_unlock(&configfs_super_xa);
+	put_net(info->net_ns);
+	kfree(info);
+}
 
 static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
+	struct configfs_super_info *info = configfs_get_super_info(&init_net);
 	struct inode *inode;
 	struct dentry *root;
 
+	sb->s_fs_info = info;
 	sb->s_blocksize = PAGE_SIZE;
 	sb->s_blocksize_bits = PAGE_SHIFT;
 	sb->s_magic = CONFIGFS_MAGIC;
@@ -77,25 +135,25 @@ 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);
-	if (inode) {
-		inode->i_op = &configfs_root_inode_operations;
-		inode->i_fop = &configfs_dir_operations;
-		/* directory inodes start off with i_nlink == 2 (for "." entry) */
-		inc_nlink(inode);
-	} else {
+				   &info->root, sb);
+	if (IS_ERR(inode)) {
 		pr_debug("could not get root inode\n");
-		return -ENOMEM;
+		configfs_put_super_info(info);
+		return PTR_ERR(inode);
 	}
+	inode->i_op = &configfs_root_inode_operations;
+	inode->i_fop = &configfs_dir_operations;
+	/* directory inodes start off with i_nlink == 2 (for "." entry) */
+	inc_nlink(inode);
 
 	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;
+	info->group.cg_item.ci_dentry = root;
+	root->d_fsdata = &info->root;
 	sb->s_root = root;
 	set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
 	sb->s_d_flags |= DCACHE_DONTCACHE;
@@ -117,24 +175,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);
 }
 
 
@@ -148,18 +215,27 @@ static int __init configfs_init(void)
 	if (!configfs_dir_cachep)
 		goto out;
 
+	configfs_root = configfs_get_super_info(&init_net);
+	if (IS_ERR(configfs_root)) {
+		err = PTR_ERR(configfs_root);
+		goto out2;
+	}
+
 	err = sysfs_create_mount_point(kernel_kobj, "config");
 	if (err)
-		goto out2;
+		goto out3;
 
 	err = register_filesystem(&configfs_fs_type);
 	if (err)
-		goto out3;
+		goto out4;
 
 	return 0;
-out3:
+out4:
 	pr_err("Unable to register filesystem!\n");
 	sysfs_remove_mount_point(kernel_kobj, "config");
+out3:
+	configfs_put_super_info(configfs_root);
+	configfs_root = NULL;
 out2:
 	kmem_cache_destroy(configfs_dir_cachep);
 	configfs_dir_cachep = NULL;
@@ -171,6 +247,8 @@ static void __exit configfs_exit(void)
 {
 	unregister_filesystem(&configfs_fs_type);
 	sysfs_remove_mount_point(kernel_kobj, "config");
+	configfs_put_super_info(configfs_root);
+	configfs_root = NULL;
 	kmem_cache_destroy(configfs_dir_cachep);
 	configfs_dir_cachep = NULL;
 }
diff --git a/include/linux/configfs.h b/include/linux/configfs.h
index ef65c75beeaad87d33a18e86ac47bf2994705f2e..19165e36da810c75500bb584702ae3a594c3510d 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -24,6 +24,7 @@
 #include <linux/list.h>   /* struct list_head */
 #include <linux/kref.h>   /* struct kref */
 #include <linux/mutex.h>  /* struct mutex */
+#include <net/net_namespace.h> /* struct net */
 
 #define CONFIGFS_ITEM_NAME_LEN	20
 

-- 
2.51.0


  parent reply	other threads:[~2026-06-23  9:16 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 01/12] fs/configfs: rework configfs_is_root() Hannes Reinecke
2026-06-23 12:50   ` Breno Leitao
2026-06-23  9:15 ` Hannes Reinecke [this message]
2026-06-23  9:15 ` [PATCH RFC v3 03/12] fs/configfs: separate out configfs_{link,unlink}_root() Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 04/12] fs/configfs: add superblock as attribute to configfs_pin_fs() Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 05/12] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 06/12] fs/namespace: implement mnt_clone_direct() Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 07/12] fs/configfs: switch to get_tree_keyed() Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 08/12] fs/configfs: open-code simple_pin_fs() Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 09/12] nvmet: make discovery subsystem dynamic Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 10/12] nvmet: per net-namespace port list Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 11/12] nvmet: make configfs setup namespace aware Hannes Reinecke
2026-06-23  9:15 ` [PATCH RFC v3 12/12] nvmet: enable transports for net namespaces Hannes Reinecke
2026-06-23 14:37 ` [syzbot ci] Re: namespace-aware configfs syzbot ci

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=20260623-configfs-ns-v3-2-841c100fd5dd@kernel.org \
    --to=hare@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=brauner@kernel.org \
    --cc=hch@lst.de \
    --cc=jack@suse.cz \
    --cc=kch@nvidia.com \
    --cc=leitao@debian.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-nvme@lists.infradead.org \
    --cc=sagi@grimberg.me \
    --cc=viro@zeniv.linux.org.uk \
    /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