All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v3 00/12] namespace-aware configfs
@ 2026-06-23  9:15 Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 01/12] fs/configfs: rework configfs_is_root() Hannes Reinecke
                   ` (12 more replies)
  0 siblings, 13 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

Hey all,

during discussions at LSF/MM I thought it would be quite helpful
to make configfs namespace aware, such that one could do:

unshare --mnt
mount -t configfs none /sys/kernel/config

and get a new instance of configfs. This will allow to have distinct
configurations for each namespace.
This is particularly helpful if you want to run eg nvmet in a container;
with this patchset each container can have its own configuration, and
one can simulate a multi-node nvmet cluster using containers.

How it works:
The configfs 'root' entries are stored in 'configfs_super_info', which
can be looked up from an xarray using the (network) namespace ID as the index.
A configfs subsystem can be converted to be namespace aware by implementing
the new subsyste callbacks 'fill_subsystem()' and 'clear_subsystem', which
are responsible for populating the subsystem structure.
Upon registration via 'register_subsystem()' the subsystem is put in a
linked list within 'configfs_super_info'. So when mount() is called from
a different namespace this list is traversed, and new subsystems are created
in the new namespace using the callbacks.

Open Issues:
- I've added a new function 'mnt_clone_direct()' to clone the vfsmount
  entry (the original code just did a simple_pin_fs()). Not sure if
  that's correct. Christian?
- Ideally I would love to make the whole thing work without having
  to mount a new configfs instance from within the container.
  Again, not sure if that's possible.
- I've decided to base it off the net namespace (and not any namespace).
  That makes implementation easier, but requires the various subsystems
  to actually _use_ the network namespace.
  The current use-case (nvmet) does that, so it works for me :-)

Otherwise, as usual, commands and reviews are welcome.

Changes to the original submission:
- Changed to use 'net' namespace instead of 'mnt' namespace
- Smaller fixes as suggested by sashiko.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
Changes in v3:
- Split off nvmet patches
- kbuild robot fixes
- Simplify namespace selection
- Link to v2: https://lore.kernel.org/r/20260619-configfs-ns-v2-0-82fd7094b8dc@kernel.org

---
Hannes Reinecke (12):
      fs/configfs: rework configfs_is_root()
      fs/configfs: dynamically allocate super_info
      fs/configfs: separate out configfs_{link,unlink}_root()
      fs/configfs: add superblock as attribute to configfs_pin_fs()
      fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks
      fs/namespace: implement mnt_clone_direct()
      fs/configfs: switch to get_tree_keyed()
      fs/configfs: open-code simple_pin_fs()
      nvmet: make discovery subsystem dynamic
      nvmet: per net-namespace port list
      nvmet: make configfs setup namespace aware
      nvmet: enable transports for net namespaces

 drivers/nvme/target/configfs.c  | 206 +++++++++++++++++++++++++++------
 drivers/nvme/target/core.c      |  36 +++---
 drivers/nvme/target/discovery.c |  91 +++++++++++----
 drivers/nvme/target/loop.c      |   4 +
 drivers/nvme/target/nvmet.h     |  15 ++-
 drivers/nvme/target/rdma.c      |   6 +-
 drivers/nvme/target/tcp.c       |   6 +-
 fs/configfs/configfs_internal.h |  23 +++-
 fs/configfs/dir.c               | 188 +++++++++++++++++++++++++-----
 fs/configfs/mount.c             | 248 +++++++++++++++++++++++++++++++++-------
 fs/namespace.c                  |  11 ++
 include/linux/configfs.h        |   8 ++
 include/linux/mount.h           |   1 +
 13 files changed, 682 insertions(+), 161 deletions(-)
---
base-commit: 66affa37cfac0aec061cc4bcf4a065b0c52f7e19
change-id: 20260619-configfs-ns-b5748b366366

Best regards,
-- 
Hannes Reinecke <hare@kernel.org>



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 01/12] fs/configfs: rework configfs_is_root()
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
@ 2026-06-23  9:15 ` Hannes Reinecke
  2026-06-23 12:50   ` Breno Leitao
  2026-06-23  9:15 ` [PATCH RFC v3 02/12] fs/configfs: dynamically allocate super_info Hannes Reinecke
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

In preparation for making configfs namespace-aware rework
configfs_is_root() to not rely on static structures but
rather use the information in the structures themselves.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 fs/configfs/configfs_internal.h | 2 +-
 fs/configfs/mount.c             | 9 +++++++--
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index acdeea8e2d69ac9d621e1a42b039719811c31757..6261e1f3ec201a58a5d46b08dfbced8c2a44d82b 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -63,7 +63,7 @@ extern spinlock_t configfs_dirent_lock;
 
 extern struct kmem_cache *configfs_dir_cachep;
 
-extern int configfs_is_root(struct config_item *item);
+extern bool configfs_is_root(struct config_item *item);
 
 extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *, struct super_block *);
 extern struct inode *configfs_create(struct dentry *, umode_t mode);
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 4929f343118946eaa55a539db4192e9c6621a8dc..88da7b428e52f2f4ab139ad3907101f685d617b1 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -47,9 +47,14 @@ static struct config_group configfs_root_group = {
 	},
 };
 
-int configfs_is_root(struct config_item *item)
+bool configfs_is_root(struct config_item *item)
 {
-	return item == &configfs_root_group.cg_item;
+	struct configfs_dirent *sd;
+
+	if (!item->ci_dentry)
+		return false;
+	sd = item->ci_dentry->d_fsdata;
+	return !!(sd->s_type & CONFIGFS_ROOT);
 }
 
 static struct configfs_dirent configfs_root = {

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 02/12] fs/configfs: dynamically allocate super_info
  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  9:15 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 03/12] fs/configfs: separate out configfs_{link,unlink}_root() Hannes Reinecke
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

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



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 03/12] fs/configfs: separate out configfs_{link,unlink}_root()
  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  9:15 ` [PATCH RFC v3 02/12] fs/configfs: dynamically allocate super_info Hannes Reinecke
@ 2026-06-23  9:15 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 04/12] fs/configfs: add superblock as attribute to configfs_pin_fs() Hannes Reinecke
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

Separate out functions to link and unlink subsystem groups.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 fs/configfs/dir.c | 74 ++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 46 insertions(+), 28 deletions(-)

diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 1e9c82e02741798930b08c96fb59a2767c7e00fc..ffd0e302cff1756f2161bdb58f1d8c1ac0cf893a 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1861,32 +1861,19 @@ void configfs_unregister_default_group(struct config_group *group)
 }
 EXPORT_SYMBOL(configfs_unregister_default_group);
 
-int configfs_register_subsystem(struct configfs_subsystem *subsys)
+static int configfs_link_root(struct configfs_super_info  *info,
+			      struct configfs_subsystem *subsys,
+			      struct dentry *root)
 {
-	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) {
-		configfs_put_super_info(info);
+	if (!frag)
 		return -ENOMEM;
-	}
-
-	root = configfs_pin_fs();
-	if (IS_ERR(root)) {
-		put_fragment(frag);
-		configfs_put_super_info(info);
-		return PTR_ERR(root);
-	}
 
 	if (!group->cg_item.ci_name)
 		group->cg_item.ci_name = group->cg_item.ci_namebuf;
@@ -1924,33 +1911,48 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
 		mutex_lock(&info->subsys_mutex);
 		unlink_group(group);
 		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)
+int configfs_register_subsystem(struct configfs_subsystem *subsys)
 {
 	struct configfs_super_info *info;
+	struct dentry *root;
+	int err;
+
+	info = configfs_get_super_info(&init_net);
+	if (WARN_ON(IS_ERR(info)))
+		return PTR_ERR(info);
+
+	root = configfs_pin_fs();
+	if (IS_ERR(root)) {
+		err = PTR_ERR(root);
+		goto out_put;
+	}
+
+	err = configfs_link_root(info, subsys, root);
+	if (err)
+		configfs_release_fs();
+
+out_put:
+	configfs_put_super_info(info);
+	return err;
+}
+
+static bool configfs_unlink_root(struct configfs_subsystem *subsys)
+{
 	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;
+		return false;
 	}
 
 	down_write(&frag->frag_sem);
@@ -1978,6 +1980,22 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
 	inode_unlock(d_inode(root));
 
 	dput(dentry);
+	return true;
+}
+
+void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
+{
+	struct configfs_super_info *info;
+	struct config_group *group = &subsys->su_group;
+
+	info = configfs_get_super_info(&init_net);
+	if (WARN_ON(IS_ERR(info)))
+		return;
+
+	if (!configfs_unlink_root(subsys)) {
+		configfs_put_super_info(info);
+		return;
+	}
 
 	mutex_lock(&info->subsys_mutex);
 	unlink_group(group);

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 04/12] fs/configfs: add superblock as attribute to configfs_pin_fs()
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (2 preceding siblings ...)
  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 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 05/12] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks Hannes Reinecke
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

Each namspace might have its own superblock, so add it as an argument
to configfs_pin_fs() to ensure that the correct filesystem instance
will be pinned.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 fs/configfs/configfs_internal.h |  4 ++--
 fs/configfs/dir.c               | 10 +++++-----
 fs/configfs/mount.c             | 20 +++++++++++++-------
 3 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index 94b9e306709150e7a123c46bb359077c6dc917ad..a4f10dd8ce0f46873d2053860a9fd25f3ee4c809 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -90,8 +90,8 @@ extern const unsigned char * configfs_get_name(struct configfs_dirent *sd);
 extern int configfs_setattr(struct mnt_idmap *idmap,
 			    struct dentry *dentry, struct iattr *iattr);
 
-extern struct dentry *configfs_pin_fs(void);
-extern void configfs_release_fs(void);
+extern struct dentry *configfs_pin_fs(struct super_block *sb);
+extern void configfs_release_fs(struct super_block *sb);
 extern struct configfs_super_info *configfs_get_super_info(struct net *net_ns);
 extern void configfs_put_super_info(struct configfs_super_info *info);
 
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index ffd0e302cff1756f2161bdb58f1d8c1ac0cf893a..64635c58c0f33ec2d722b0555d3a9153f28bb200 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1144,7 +1144,7 @@ int configfs_depend_item(struct configfs_subsystem *subsys,
 	 * Pin the configfs filesystem.  This means we can safely access
 	 * the root of the configfs filesystem.
 	 */
-	root = configfs_pin_fs();
+	root = configfs_pin_fs(NULL);
 	if (IS_ERR(root))
 		return PTR_ERR(root);
 
@@ -1171,7 +1171,7 @@ int configfs_depend_item(struct configfs_subsystem *subsys,
 	 * If we succeeded, the fs is pinned via other methods.  If not,
 	 * we're done with it anyway.  So release_fs() is always right.
 	 */
-	configfs_release_fs();
+	configfs_release_fs(NULL);
 
 	return ret;
 }
@@ -1926,7 +1926,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
 	if (WARN_ON(IS_ERR(info)))
 		return PTR_ERR(info);
 
-	root = configfs_pin_fs();
+	root = configfs_pin_fs(NULL);
 	if (IS_ERR(root)) {
 		err = PTR_ERR(root);
 		goto out_put;
@@ -1934,7 +1934,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
 
 	err = configfs_link_root(info, subsys, root);
 	if (err)
-		configfs_release_fs();
+		configfs_release_fs(NULL);
 
 out_put:
 	configfs_put_super_info(info);
@@ -2000,7 +2000,7 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
 	mutex_lock(&info->subsys_mutex);
 	unlink_group(group);
 	mutex_unlock(&info->subsys_mutex);
-	configfs_release_fs();
+	configfs_release_fs(NULL);
 	configfs_put_super_info(info);
 }
 
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 067bb1099a52ca2216d1b2881429a3fd1811c706..8b26ceb7b0d4a03a17c251de42902aaabef5a652 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -192,18 +192,24 @@ static struct file_system_type configfs_fs_type = {
 };
 MODULE_ALIAS_FS("configfs");
 
-struct dentry *configfs_pin_fs(void)
+struct dentry *configfs_pin_fs(struct super_block *sb)
 {
-	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;
+	struct configfs_super_info *info = configfs_root;
+	int err;
+
+	err = simple_pin_fs(&configfs_fs_type, &info->mnt, &info->mnt_count);
+	if (err)
+		return ERR_PTR(err);
+
+	return info->mnt->mnt_root;
 }
 
-void configfs_release_fs(void)
+void configfs_release_fs(struct super_block *sb)
 {
-	simple_release_fs(&configfs_root->mnt, &configfs_root->mnt_count);
-}
+	struct configfs_super_info *info = configfs_root;
 
+	simple_release_fs(&info->mnt, &info->mnt_count);
+}
 
 static int __init configfs_init(void)
 {

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 05/12] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (3 preceding siblings ...)
  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 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 06/12] fs/namespace: implement mnt_clone_direct() Hannes Reinecke
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

When creating a new per-namespace filesystem static initialisation
doesn't work, so implement a 'fill_subsystem' and 'clear_subsystem'
callbacks to 'struct configfs_subsystem' to allow for every driver
to populate the subsystem structure dynamicall.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 fs/configfs/configfs_internal.h |   4 ++
 fs/configfs/dir.c               | 104 +++++++++++++++++++++++++++++++++++++++-
 fs/configfs/mount.c             |   4 ++
 include/linux/configfs.h        |   5 ++
 4 files changed, 116 insertions(+), 1 deletion(-)

diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index a4f10dd8ce0f46873d2053860a9fd25f3ee4c809..35eddc274b88796442c080fe4fe8a6ea68762185 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -94,6 +94,10 @@ extern struct dentry *configfs_pin_fs(struct super_block *sb);
 extern void configfs_release_fs(struct super_block *sb);
 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 void configfs_link_subsystems(struct super_block *sb,
+				     struct configfs_super_info *info);
+extern void configfs_unlink_subsystems(struct super_block *sb,
+				       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 64635c58c0f33ec2d722b0555d3a9153f28bb200..3d39a6c1a4163632c9acdc8cf831add40c9b430e 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1932,15 +1932,95 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
 		goto out_put;
 	}
 
+	if (subsys->fill_subsystem) {
+		INIT_LIST_HEAD(&subsys->su_link);
+		mutex_init(&subsys->su_mutex);
+		config_group_init(&subsys->su_group);
+		err = subsys->fill_subsystem(subsys, info->net_ns);
+		if (err)
+			goto out_release;
+	}
+
 	err = configfs_link_root(info, subsys, root);
+	if (err) {
+		if (subsys->clear_subsystem)
+			subsys->clear_subsystem(subsys, info->net_ns);
+		goto out_release;
+	}
+
+	mutex_lock(&info->subsys_mutex);
+	list_add(&subsys->su_link, &info->subsys_list);
+	mutex_unlock(&info->subsys_mutex);
+
+out_release:
 	if (err)
 		configfs_release_fs(NULL);
-
 out_put:
 	configfs_put_super_info(info);
 	return err;
 }
 
+void configfs_link_subsystems(struct super_block *sb,
+			      struct configfs_super_info *info)
+{
+	struct configfs_subsystem *s;
+	struct configfs_super_info *parent = configfs_get_super_info(&init_net);
+	LIST_HEAD(subsys_list);
+
+	if (WARN_ON(IS_ERR(parent)))
+		return;
+	if ((info->net_ns == &init_net) || WARN_ON(parent == info)) {
+		configfs_put_super_info(parent);
+		return;
+	}
+	mutex_lock(&parent->subsys_mutex);
+	list_splice_init(&parent->subsys_list, &subsys_list);
+	mutex_unlock(&parent->subsys_mutex);
+	list_for_each_entry(s, &subsys_list, su_link) {
+		struct configfs_subsystem *subsys;
+		struct dentry *root;
+		int err;
+
+		if (!s->fill_subsystem)
+			continue;
+		subsys = kzalloc_obj(*subsys);
+		if (!subsys)
+			continue;
+		subsys->fill_subsystem = s->fill_subsystem;
+		subsys->clear_subsystem = s->clear_subsystem;
+		INIT_LIST_HEAD(&subsys->su_link);
+		mutex_init(&subsys->su_mutex);
+		config_group_init(&subsys->su_group);
+		err = subsys->fill_subsystem(subsys, info->net_ns);
+		if (err) {
+			kfree(subsys);
+			continue;
+		}
+		root = configfs_pin_fs(sb);
+		if (IS_ERR(root)) {
+			if (subsys->clear_subsystem)
+				subsys->clear_subsystem(subsys, info->net_ns);
+			kfree(subsys);
+			continue;
+		}
+		err = configfs_link_root(info, subsys, root);
+		if (err) {
+			configfs_release_fs(sb);
+			if (subsys->clear_subsystem)
+				subsys->clear_subsystem(subsys, info->net_ns);
+			kfree(subsys);
+			continue;
+		}
+		mutex_lock(&info->subsys_mutex);
+		list_add(&subsys->su_link, &info->subsys_list);
+		mutex_unlock(&info->subsys_mutex);
+	}
+	mutex_lock(&parent->subsys_mutex);
+	list_splice(&subsys_list, &parent->subsys_list);
+	mutex_unlock(&parent->subsys_mutex);
+	configfs_put_super_info(parent);
+}
+
 static bool configfs_unlink_root(struct configfs_subsystem *subsys)
 {
 	struct config_group *group = &subsys->su_group;
@@ -1983,6 +2063,25 @@ static bool configfs_unlink_root(struct configfs_subsystem *subsys)
 	return true;
 }
 
+void configfs_unlink_subsystems(struct super_block *sb,
+				struct configfs_super_info *info)
+{
+	struct configfs_subsystem *subsys, *s;
+
+	mutex_lock(&info->subsys_mutex);
+	list_for_each_entry_safe(subsys, s, &info->subsys_list, su_link) {
+		list_del_init(&subsys->su_link);
+		if (!configfs_unlink_root(subsys))
+			continue;
+		if (subsys->clear_subsystem)
+			subsys->clear_subsystem(subsys, info->net_ns);
+		unlink_group(&subsys->su_group);
+		configfs_release_fs(sb);
+		kfree(subsys);
+	}
+	mutex_unlock(&info->subsys_mutex);
+}
+
 void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
 {
 	struct configfs_super_info *info;
@@ -1998,6 +2097,9 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
 	}
 
 	mutex_lock(&info->subsys_mutex);
+	list_del_init(&subsys->su_link);
+	if (subsys->clear_subsystem)
+		subsys->clear_subsystem(subsys, info->net_ns);
 	unlink_group(group);
 	mutex_unlock(&info->subsys_mutex);
 	configfs_release_fs(NULL);
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 8b26ceb7b0d4a03a17c251de42902aaabef5a652..d36fc4a6f6439bea645e62b8423b518a5d7d51c1 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -157,6 +157,9 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	sb->s_root = root;
 	set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
 	sb->s_d_flags |= DCACHE_DONTCACHE;
+
+	configfs_link_subsystems(sb, info);
+
 	return 0;
 }
 
@@ -179,6 +182,7 @@ static void configfs_kill_sb(struct super_block *sb)
 {
 	struct configfs_super_info *info = sb->s_fs_info;
 
+	configfs_unlink_subsystems(sb, info);
 	kill_anon_super(sb);
 	configfs_put_super_info(info);
 	sb->s_fs_info = NULL;
diff --git a/include/linux/configfs.h b/include/linux/configfs.h
index 19165e36da810c75500bb584702ae3a594c3510d..893ae778585fb8be60026c661cb1ef63980ff0e3 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -227,8 +227,13 @@ struct configfs_group_operations {
 };
 
 struct configfs_subsystem {
+	struct list_head	su_link;
 	struct config_group	su_group;
 	struct mutex		su_mutex;
+	int (*fill_subsystem)(struct configfs_subsystem *subsys,
+			      struct net *net_ns);
+	void (*clear_subsystem)(struct configfs_subsystem *subsys,
+				struct net *net_ns);
 };
 
 static inline struct configfs_subsystem *to_configfs_subsystem(struct config_group *group)

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 06/12] fs/namespace: implement mnt_clone_direct()
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (4 preceding siblings ...)
  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 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 07/12] fs/configfs: switch to get_tree_keyed() Hannes Reinecke
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

Implement mnt_clone_direct() to just clone an existing vfsmount.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 fs/namespace.c        | 11 +++++++++++
 include/linux/mount.h |  1 +
 2 files changed, 12 insertions(+)

diff --git a/fs/namespace.c b/fs/namespace.c
index 3d5cd5bf3b05376c867281541100c6a5e599d387..e7abbf632cc70d70547e4a2483f2217c95cc0c48 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1482,6 +1482,17 @@ struct vfsmount *mnt_clone_internal(const struct path *path)
 	return &p->mnt;
 }
 
+struct vfsmount *mnt_clone_direct(struct vfsmount *mnt, struct dentry *dentry)
+{
+	struct mount *p;
+
+	p = clone_mnt(real_mount(mnt), dentry, CL_SLAVE);
+	if (IS_ERR(p))
+		return ERR_CAST(p);
+	p->mnt.mnt_flags |= MNT_INTERNAL;
+	return &p->mnt;
+}
+
 /*
  * Returns the mount which either has the specified mnt_id, or has the next
  * smallest id afer the specified one.
diff --git a/include/linux/mount.h b/include/linux/mount.h
index acfe7ef86a1b3fdd70ab460e6e731d3c532b6487..7e7e7d66c3024b907497954a247c7e35dbcfc946 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -75,6 +75,7 @@ extern void mnt_drop_write_file(struct file *file);
 extern void mntput(struct vfsmount *mnt);
 extern struct vfsmount *mntget(struct vfsmount *mnt);
 extern void mnt_make_shortterm(struct vfsmount *mnt);
+extern struct vfsmount *mnt_clone_direct(struct vfsmount *mnt, struct dentry *dentry);
 extern struct vfsmount *mnt_clone_internal(const struct path *path);
 extern bool __mnt_is_readonly(const struct vfsmount *mnt);
 extern bool mnt_may_suid(struct vfsmount *mnt);

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 07/12] fs/configfs: switch to get_tree_keyed()
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (5 preceding siblings ...)
  2026-06-23  9:15 ` [PATCH RFC v3 06/12] fs/namespace: implement mnt_clone_direct() Hannes Reinecke
@ 2026-06-23  9:15 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 08/12] fs/configfs: open-code simple_pin_fs() Hannes Reinecke
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

Switch to get_tree_keyed() to instatiate a new superblock whenever
the mount namespace changed.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 fs/configfs/mount.c | 31 ++++++++++++++++++++++++++-----
 1 file changed, 26 insertions(+), 5 deletions(-)

diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index d36fc4a6f6439bea645e62b8423b518a5d7d51c1..bcc403f96d25d8385f6c903d40fa752da2610979 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -123,11 +123,10 @@ void configfs_put_super_info(struct configfs_super_info *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 configfs_super_info *info = sb->s_fs_info;
 	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;
@@ -138,7 +137,6 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
 				   &info->root, sb);
 	if (IS_ERR(inode)) {
 		pr_debug("could not get root inode\n");
-		configfs_put_super_info(info);
 		return PTR_ERR(inode);
 	}
 	inode->i_op = &configfs_root_inode_operations;
@@ -149,7 +147,6 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	root = d_make_root(inode);
 	if (!root) {
 		pr_debug("%s: could not get root dentry!\n",__func__);
-		configfs_put_super_info(info);
 		return -ENOMEM;
 	}
 	info->group.cg_item.ci_dentry = root;
@@ -165,15 +162,38 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
 
 static int configfs_get_tree(struct fs_context *fc)
 {
-	return get_tree_single(fc, configfs_fill_super);
+	struct net *net_ns = fc->fs_private;
+	struct configfs_super_info *info;
+	int err;
+
+	info = configfs_get_super_info(net_ns);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+
+	err = get_tree_keyed(fc, configfs_fill_super, info);
+	if (err && fc->s_fs_info)
+		configfs_put_super_info(info);
+	return err;
+}
+
+static void configfs_fs_context_free(struct fs_context *fc)
+{
+	struct net *net_ns = fc->fs_private;
+
+	fc->fs_private = NULL;
+	put_net(net_ns);
 }
 
 static const struct fs_context_operations configfs_context_ops = {
 	.get_tree	= configfs_get_tree,
+	.free		= configfs_fs_context_free,
 };
 
 static int configfs_init_fs_context(struct fs_context *fc)
 {
+	struct net *net_ns = get_net(current->nsproxy->net_ns);
+
+	fc->fs_private = net_ns;
 	fc->ops = &configfs_context_ops;
 	return 0;
 }
@@ -193,6 +213,7 @@ static struct file_system_type configfs_fs_type = {
 	.name		= "configfs",
 	.init_fs_context = configfs_init_fs_context,
 	.kill_sb	= configfs_kill_sb,
+	.fs_flags	= FS_USERNS_MOUNT,
 };
 MODULE_ALIAS_FS("configfs");
 

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 08/12] fs/configfs: open-code simple_pin_fs()
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (6 preceding siblings ...)
  2026-06-23  9:15 ` [PATCH RFC v3 07/12] fs/configfs: switch to get_tree_keyed() Hannes Reinecke
@ 2026-06-23  9:15 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 09/12] nvmet: make discovery subsystem dynamic Hannes Reinecke
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

simple_pin_fs() is protected by an internal spin lock, to
protect against concurrent use of simple_release_fs().
But as we also need to clone the mountpoint we cannot
easily expand the scope of the lock as it's internal to
fs/libfs. So open-code simple_pin_fs() and simple_release_fs()
to be able to use a lock covering all use-cases.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 fs/configfs/mount.c | 56 +++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 48 insertions(+), 8 deletions(-)

diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index bcc403f96d25d8385f6c903d40fa752da2610979..095a480b6ea27db9a43b067af7e6352fd0ae50ee 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -25,6 +25,7 @@
 struct kmem_cache *configfs_dir_cachep;
 static DEFINE_XARRAY(configfs_super_xa);
 static struct configfs_super_info *configfs_root;
+static DEFINE_SPINLOCK(configfs_pin_lock);
 
 static u64 configfs_ns_id(struct net *net_ns)
 {
@@ -219,21 +220,60 @@ MODULE_ALIAS_FS("configfs");
 
 struct dentry *configfs_pin_fs(struct super_block *sb)
 {
-	struct configfs_super_info *info = configfs_root;
-	int err;
-
-	err = simple_pin_fs(&configfs_fs_type, &info->mnt, &info->mnt_count);
-	if (err)
-		return ERR_PTR(err);
+	struct configfs_super_info *info;
 
+	spin_lock(&configfs_pin_lock);
+	if (sb) {
+		struct configfs_super_info *root = configfs_root;
+		struct dentry *dentry = sb->s_root;
+
+		info = sb->s_fs_info;
+		if (!info->mnt) {
+			struct vfsmount *mnt;
+
+			spin_unlock(&configfs_pin_lock);
+			mnt = mnt_clone_direct(root->mnt, dentry);
+			if (IS_ERR(mnt))
+				return ERR_CAST(mnt);
+			spin_lock(&configfs_pin_lock);
+			info->mnt = mnt;
+		} else {
+			mntget(info->mnt);
+		}
+	} else {
+		info = configfs_root;
+		if (!info->mnt) {
+			struct vfsmount *mnt;
+
+			spin_unlock(&configfs_pin_lock);
+			mnt = vfs_kern_mount(&configfs_fs_type, SB_KERNMOUNT,
+					     configfs_fs_type.name, NULL);
+			if (IS_ERR(mnt))
+				return ERR_CAST(mnt);
+			spin_lock(&configfs_pin_lock);
+			info->mnt = mnt;
+		} else {
+			mntget(info->mnt);
+		}
+	}
+	info->mnt_count++;
+	spin_unlock(&configfs_pin_lock);
 	return info->mnt->mnt_root;
 }
 
 void configfs_release_fs(struct super_block *sb)
 {
 	struct configfs_super_info *info = configfs_root;
-
-	simple_release_fs(&info->mnt, &info->mnt_count);
+	struct vfsmount *mnt;
+
+	spin_lock(&configfs_pin_lock);
+	if (sb)
+		info = sb->s_fs_info;
+	mnt = info->mnt;
+	if (--info->mnt_count)
+		info->mnt = NULL;
+	spin_unlock(&configfs_pin_lock);
+	mntput(mnt);
 }
 
 static int __init configfs_init(void)

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 09/12] nvmet: make discovery subsystem dynamic
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (7 preceding siblings ...)
  2026-06-23  9:15 ` [PATCH RFC v3 08/12] fs/configfs: open-code simple_pin_fs() Hannes Reinecke
@ 2026-06-23  9:15 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 10/12] nvmet: per net-namespace port list Hannes Reinecke
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

In preparation to make nvmet namespace-aware convert the
discovery subsystem to dynamic allocation.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/nvme/target/configfs.c  | 33 +++++++++++++---
 drivers/nvme/target/core.c      | 30 ++++++--------
 drivers/nvme/target/discovery.c | 86 +++++++++++++++++++++++++++++------------
 drivers/nvme/target/nvmet.h     | 11 ++++--
 4 files changed, 108 insertions(+), 52 deletions(-)

diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index b88f897f06e25e3e604615c75dc55cc5b2a1f1a1..6030647cee937195d772bcf4b92cb37b6ebf9f08 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -51,6 +51,16 @@ static const struct nvmet_type_name_map nvmet_addr_family[] = {
 	{ NVMF_ADDR_FAMILY_LOOP,	"loop" },
 };
 
+u64 nvmet_get_ns_id(struct net *net_ns)
+{
+	struct ns_common *ns;
+
+	if (!net_ns)
+		return 0;
+	ns = to_ns_common(net_ns);
+	return net_ns == &init_net ? 0 : ns->ns_id;
+}
+
 static bool nvmet_is_port_enabled(struct nvmet_port *p, const char *caller)
 {
 	if (p->enabled)
@@ -1734,14 +1744,17 @@ static const struct config_item_type nvmet_subsys_type = {
 static struct config_group *nvmet_subsys_make(struct config_group *group,
 		const char *name)
 {
-	struct nvmet_subsys *subsys;
+	struct nvmet_subsys *subsys, *disc_subsys;
 
 	if (sysfs_streq(name, NVME_DISC_SUBSYS_NAME)) {
 		pr_err("can't create discovery subsystem through configfs\n");
 		return ERR_PTR(-EINVAL);
 	}
 
-	if (sysfs_streq(name, nvmet_disc_subsys->subsysnqn)) {
+	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	if (!disc_subsys)
+		return ERR_PTR(-ENOENT);
+	if (sysfs_streq(name, disc_subsys->subsysnqn)) {
 		pr_err("can't create subsystem using unique discovery NQN\n");
 		return ERR_PTR(-EINVAL);
 	}
@@ -2308,12 +2321,17 @@ static struct config_group nvmet_hosts_group;
 static ssize_t nvmet_root_discovery_nqn_show(struct config_item *item,
 					     char *page)
 {
-	return snprintf(page, PAGE_SIZE, "%s\n", nvmet_disc_subsys->subsysnqn);
+	struct nvmet_subsys *disc_subsys = nvmet_get_disc_subsys(&init_net);
+
+	if (!disc_subsys)
+		return -ENOENT;
+	return snprintf(page, PAGE_SIZE, "%s\n", disc_subsys->subsysnqn);
 }
 
 static ssize_t nvmet_root_discovery_nqn_store(struct config_item *item,
 		const char *page, size_t count)
 {
+	struct nvmet_subsys *disc_subsys;
 	struct list_head *entry;
 	char *old_nqn, *new_nqn;
 	size_t len;
@@ -2338,12 +2356,15 @@ static ssize_t nvmet_root_discovery_nqn_store(struct config_item *item,
 			return -EINVAL;
 		}
 	}
-	old_nqn = nvmet_disc_subsys->subsysnqn;
-	nvmet_disc_subsys->subsysnqn = new_nqn;
+	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	if (disc_subsys) {
+		old_nqn = disc_subsys->subsysnqn;
+		disc_subsys->subsysnqn = new_nqn;
+	}
 	up_write(&nvmet_config_sem);
 
 	kfree(old_nqn);
-	return len;
+	return disc_subsys ? len : -ENOENT;
 }
 
 CONFIGFS_ATTR(nvmet_root_, discovery_nqn);
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 62dd59b9aa4f1c9bfd32361e3f06ab1520424ce8..1831a218319b84a1e1a1cfba7fb48e3ad40b09f7 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -1798,22 +1798,27 @@ static struct nvmet_subsys *nvmet_find_get_subsys(struct nvmet_port *port,
 		const char *subsysnqn)
 {
 	struct nvmet_subsys_link *p;
+	struct nvmet_subsys *disc_subsys;
 
 	if (!port)
 		return NULL;
 
+	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	if (!disc_subsys)
+		return NULL;
+
 	if (!strcmp(NVME_DISC_SUBSYS_NAME, subsysnqn)) {
-		if (!kref_get_unless_zero(&nvmet_disc_subsys->ref))
+		if (!kref_get_unless_zero(&disc_subsys->ref))
 			return NULL;
-		return nvmet_disc_subsys;
+		return disc_subsys;
 	}
 
 	down_read(&nvmet_config_sem);
-	if (!strncmp(nvmet_disc_subsys->subsysnqn, subsysnqn,
+	if (!strncmp(disc_subsys->subsysnqn, subsysnqn,
 				NVMF_NQN_SIZE)) {
-		if (kref_get_unless_zero(&nvmet_disc_subsys->ref)) {
+		if (kref_get_unless_zero(&disc_subsys->ref)) {
 			up_read(&nvmet_config_sem);
-			return nvmet_disc_subsys;
+			return disc_subsys;
 		}
 	}
 	list_for_each_entry(p, &port->subsystems, entry) {
@@ -1888,14 +1893,8 @@ struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn,
 	INIT_LIST_HEAD(&subsys->ctrls);
 	INIT_LIST_HEAD(&subsys->hosts);
 
-	ret = nvmet_debugfs_subsys_setup(subsys);
-	if (ret)
-		goto free_subsysnqn;
-
 	return subsys;
 
-free_subsysnqn:
-	kfree(subsys->subsysnqn);
 free_fr:
 	kfree(subsys->firmware_rev);
 free_mn:
@@ -1976,18 +1975,12 @@ static int __init nvmet_init(void)
 	if (error)
 		goto out_free_nvmet_aen_work_queue;
 
-	error = nvmet_init_discovery();
-	if (error)
-		goto out_exit_debugfs;
-
 	error = nvmet_init_configfs();
 	if (error)
-		goto out_exit_discovery;
+		goto out_exit_debugfs;
 
 	return 0;
 
-out_exit_discovery:
-	nvmet_exit_discovery();
 out_exit_debugfs:
 	nvmet_exit_debugfs();
 out_free_nvmet_aen_work_queue:
@@ -2006,7 +1999,6 @@ static int __init nvmet_init(void)
 static void __exit nvmet_exit(void)
 {
 	nvmet_exit_configfs();
-	nvmet_exit_discovery();
 	nvmet_exit_debugfs();
 	ida_destroy(&cntlid_ida);
 	destroy_workqueue(nvmet_aen_wq);
diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c
index 114869d16a1f3ce8740a2126a2cb491ec8007ed7..4a0e5929125ece4c98bf25918ce82b66d4140228 100644
--- a/drivers/nvme/target/discovery.c
+++ b/drivers/nvme/target/discovery.c
@@ -8,9 +8,44 @@
 #include <generated/utsrelease.h>
 #include "nvmet.h"
 
-struct nvmet_subsys *nvmet_disc_subsys;
+static DEFINE_XARRAY(nvmet_disc_xa);
 
-static u64 nvmet_genctr;
+struct nvmet_subsys *nvmet_get_disc_subsys(struct net *net_ns)
+{
+	struct nvmet_subsys *subsys;
+	u64 ns_id = nvmet_get_ns_id(net_ns);
+
+	subsys = xa_load(&nvmet_disc_xa, ns_id);
+	return subsys;
+}
+
+int nvmet_add_disc_subsys(struct net *net_ns)
+{
+	struct nvmet_subsys *disc_subsys;
+	u64 ns_id = nvmet_get_ns_id(net_ns);
+	int err;
+
+	disc_subsys = nvmet_subsys_alloc(NVME_DISC_SUBSYS_NAME,
+					 NVME_NQN_CURR);
+	if (IS_ERR(disc_subsys))
+		return PTR_ERR(disc_subsys);
+	err = xa_insert(&nvmet_disc_xa, ns_id,
+			disc_subsys, GFP_KERNEL);
+	if (err < 0)
+		nvmet_subsys_put(disc_subsys);
+
+	return err;
+}
+
+void nvmet_del_disc_subsys(struct net *net_ns)
+{
+	struct nvmet_subsys *disc_subsys;
+	u64 ns_id = nvmet_get_ns_id(net_ns);
+
+	disc_subsys = xa_erase(&nvmet_disc_xa, ns_id);
+	if (disc_subsys)
+		nvmet_subsys_put(disc_subsys);
+}
 
 static void __nvmet_disc_changed(struct nvmet_port *port,
 				 struct nvmet_ctrl *ctrl)
@@ -29,18 +64,22 @@ void nvmet_port_disc_changed(struct nvmet_port *port,
 			     struct nvmet_subsys *subsys)
 {
 	struct nvmet_ctrl *ctrl;
+	struct nvmet_subsys *disc_subsys;
 
 	lockdep_assert_held(&nvmet_config_sem);
-	nvmet_genctr++;
+	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	if (WARN_ON(!disc_subsys))
+		return;
+	disc_subsys->genctr++;
 
-	mutex_lock(&nvmet_disc_subsys->lock);
-	list_for_each_entry(ctrl, &nvmet_disc_subsys->ctrls, subsys_entry) {
+	mutex_lock(&disc_subsys->lock);
+	list_for_each_entry(ctrl, &disc_subsys->ctrls, subsys_entry) {
 		if (subsys && !nvmet_host_allowed(subsys, ctrl->hostnqn))
 			continue;
 
 		__nvmet_disc_changed(port, ctrl);
 	}
-	mutex_unlock(&nvmet_disc_subsys->lock);
+	mutex_unlock(&disc_subsys->lock);
 
 	/* If transport can signal change, notify transport */
 	if (port->tr_ops && port->tr_ops->discovery_chg)
@@ -52,15 +91,19 @@ static void __nvmet_subsys_disc_changed(struct nvmet_port *port,
 					struct nvmet_host *host)
 {
 	struct nvmet_ctrl *ctrl;
+	struct nvmet_subsys *disc_subsys;
 
-	mutex_lock(&nvmet_disc_subsys->lock);
-	list_for_each_entry(ctrl, &nvmet_disc_subsys->ctrls, subsys_entry) {
+	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	if (WARN_ON(!disc_subsys))
+		return;
+	mutex_lock(&disc_subsys->lock);
+	list_for_each_entry(ctrl, &disc_subsys->ctrls, subsys_entry) {
 		if (host && strcmp(nvmet_host_name(host), ctrl->hostnqn))
 			continue;
 
 		__nvmet_disc_changed(port, ctrl);
 	}
-	mutex_unlock(&nvmet_disc_subsys->lock);
+	mutex_unlock(&disc_subsys->lock);
 }
 
 void nvmet_subsys_disc_changed(struct nvmet_subsys *subsys,
@@ -68,9 +111,13 @@ void nvmet_subsys_disc_changed(struct nvmet_subsys *subsys,
 {
 	struct nvmet_port *port;
 	struct nvmet_subsys_link *s;
+	struct nvmet_subsys *disc_subsys;
 
 	lockdep_assert_held(&nvmet_config_sem);
-	nvmet_genctr++;
+	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	if (WARN_ON(!disc_subsys))
+		return;
+	disc_subsys->genctr++;
 
 	list_for_each_entry(port, nvmet_ports, global_entry)
 		list_for_each_entry(s, &port->subsystems, entry) {
@@ -173,6 +220,7 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
 	u16 status = 0;
 	void *buffer;
 	char traddr[NVMF_TRADDR_SIZE];
+	struct nvmet_subsys *disc_subsys;
 
 	if (!nvmet_check_transfer_len(req, data_len))
 		return;
@@ -207,10 +255,12 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
 	}
 	hdr = buffer;
 
+	disc_subsys = nvmet_get_disc_subsys(&init_net);
+
 	nvmet_set_disc_traddr(req, req->port, traddr);
 
 	nvmet_format_discovery_entry(hdr, req->port,
-				     nvmet_disc_subsys->subsysnqn,
+				     disc_subsys->subsysnqn,
 				     traddr, NVME_NQN_CURR, numrec);
 	numrec++;
 
@@ -235,7 +285,7 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
 		numrec++;
 	}
 
-	hdr->genctr = cpu_to_le64(nvmet_genctr);
+	hdr->genctr = cpu_to_le64(disc_subsys->genctr);
 	hdr->numrec = cpu_to_le64(numrec);
 	hdr->recfmt = cpu_to_le16(0);
 
@@ -428,15 +478,3 @@ u16 nvmet_parse_discovery_cmd(struct nvmet_req *req)
 	}
 
 }
-
-int __init nvmet_init_discovery(void)
-{
-	nvmet_disc_subsys =
-		nvmet_subsys_alloc(NVME_DISC_SUBSYS_NAME, NVME_NQN_CURR);
-	return PTR_ERR_OR_ZERO(nvmet_disc_subsys);
-}
-
-void nvmet_exit_discovery(void)
-{
-	nvmet_subsys_put(nvmet_disc_subsys);
-}
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 3305a88684ecab5d59c53d7f975d1b0a1cb213a2..2bbee443ce7c7a13be15976c18ab003265a366cb 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -325,6 +325,9 @@ struct nvmet_subsys {
 	u16			cntlid_min;
 	u16			cntlid_max;
 
+	/* For discovery subsystems */
+	u64 genctr;
+
 	struct list_head	ctrls;
 
 	struct list_head	hosts;
@@ -696,11 +699,13 @@ void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,
 
 int __init nvmet_init_configfs(void);
 void __exit nvmet_exit_configfs(void);
+extern struct list_head *nvmet_get_port_list(struct net *net_ns);
 
-int __init nvmet_init_discovery(void);
-void nvmet_exit_discovery(void);
+extern u64 nvmet_get_ns_id(struct net *net_ns);
+extern int nvmet_add_disc_subsys(struct net *net_ns);
+extern void nvmet_del_disc_subsys(struct net *net_ns);
+extern struct nvmet_subsys *nvmet_get_disc_subsys(struct net *net_ns);
 
-extern struct nvmet_subsys *nvmet_disc_subsys;
 extern struct rw_semaphore nvmet_config_sem;
 
 extern u32 nvmet_ana_group_enabled[NVMET_MAX_ANAGRPS + 1];

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 10/12] nvmet: per net-namespace port list
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (8 preceding siblings ...)
  2026-06-23  9:15 ` [PATCH RFC v3 09/12] nvmet: make discovery subsystem dynamic Hannes Reinecke
@ 2026-06-23  9:15 ` Hannes Reinecke
  2026-06-23  9:15 ` [PATCH RFC v3 11/12] nvmet: make configfs setup namespace aware Hannes Reinecke
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

The port list needs to be per net-namespace, as each network namespace
has a different view on the existing IP addresses etc.
So replace the global list with an xarray of lists indexed by the
network namespace id.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/nvme/target/configfs.c  | 61 ++++++++++++++++++++++++++++++++++++++---
 drivers/nvme/target/discovery.c |  4 ++-
 drivers/nvme/target/nvmet.h     |  1 -
 3 files changed, 60 insertions(+), 6 deletions(-)

diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 6030647cee937195d772bcf4b92cb37b6ebf9f08..9c761a7f98496d59aa993e3663f0b3da63b6d3c1 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -25,8 +25,7 @@
 static const struct config_item_type nvmet_host_type;
 static const struct config_item_type nvmet_subsys_type;
 
-static LIST_HEAD(nvmet_ports_list);
-struct list_head *nvmet_ports = &nvmet_ports_list;
+static DEFINE_XARRAY(nvmet_ports_xa);
 
 struct nvmet_type_name_map {
 	u8		type;
@@ -61,6 +60,60 @@ u64 nvmet_get_ns_id(struct net *net_ns)
 	return net_ns == &init_net ? 0 : ns->ns_id;
 }
 
+struct list_head *nvmet_get_port_list(struct net *net_ns)
+{
+	struct list_head *port_list;
+	u64 ns_id = nvmet_get_ns_id(net_ns);
+
+	port_list = xa_load(&nvmet_ports_xa, ns_id);
+	return port_list;
+}
+
+static int nvmet_add_port_list(struct nvmet_port *p)
+{
+	u64 ns_id = nvmet_get_ns_id(&init_net);
+	struct list_head *port_list;
+	int err = 0;
+
+	xa_lock(&nvmet_ports_xa);
+	port_list = xa_load(&nvmet_ports_xa, ns_id);
+	if (!port_list) {
+		port_list = kzalloc_obj(*port_list);
+		if (!port_list) {
+			err = -ENOMEM;
+			goto out_unlock;
+		}
+		INIT_LIST_HEAD(port_list);
+		err = __xa_insert(&nvmet_ports_xa, ns_id,
+				  port_list, GFP_KERNEL);
+		if (err < 0) {
+			kfree(port_list);
+			goto out_unlock;
+		}
+	}
+	list_add(&p->global_entry, port_list);
+out_unlock:
+	xa_unlock(&nvmet_ports_xa);
+	return err;
+}
+
+static void nvmet_del_port_list(struct nvmet_port *p)
+{
+	struct list_head *port_list;
+	u64 ns_id = nvmet_get_ns_id(&init_net);
+
+	xa_lock(&nvmet_ports_xa);
+	port_list = xa_load(&nvmet_ports_xa, ns_id);
+	if (!WARN_ON(!port_list)) {
+		list_del_init(&p->global_entry);
+		if (list_empty(port_list)) {
+			__xa_erase(&nvmet_ports_xa, ns_id);
+			kfree(port_list);
+		}
+	}
+	xa_unlock(&nvmet_ports_xa);
+}
+
 static bool nvmet_is_port_enabled(struct nvmet_port *p, const char *caller)
 {
 	if (p->enabled)
@@ -2017,7 +2070,7 @@ static void nvmet_port_release(struct config_item *item)
 
 	/* Let inflight controllers teardown complete */
 	flush_workqueue(nvmet_wq);
-	list_del(&port->global_entry);
+	nvmet_del_port_list(port);
 
 	key_put(port->keyring);
 	kfree(port->ana_state);
@@ -2085,7 +2138,7 @@ static struct config_group *nvmet_ports_make(struct config_group *group,
 			port->ana_state[i] = NVME_ANA_INACCESSIBLE;
 	}
 
-	list_add(&port->global_entry, &nvmet_ports_list);
+	nvmet_add_port_list(port);
 
 	INIT_LIST_HEAD(&port->entry);
 	INIT_LIST_HEAD(&port->subsystems);
diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c
index 4a0e5929125ece4c98bf25918ce82b66d4140228..e929402314f66b635e330980d4d771b9474e7e3d 100644
--- a/drivers/nvme/target/discovery.c
+++ b/drivers/nvme/target/discovery.c
@@ -111,6 +111,7 @@ void nvmet_subsys_disc_changed(struct nvmet_subsys *subsys,
 {
 	struct nvmet_port *port;
 	struct nvmet_subsys_link *s;
+	struct list_head *port_list;
 	struct nvmet_subsys *disc_subsys;
 
 	lockdep_assert_held(&nvmet_config_sem);
@@ -119,7 +120,8 @@ void nvmet_subsys_disc_changed(struct nvmet_subsys *subsys,
 		return;
 	disc_subsys->genctr++;
 
-	list_for_each_entry(port, nvmet_ports, global_entry)
+	port_list = nvmet_get_port_list(&init_net);
+	list_for_each_entry(port, port_list, global_entry)
 		list_for_each_entry(s, &port->subsystems, entry) {
 			if (s->subsys != subsys)
 				continue;
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 2bbee443ce7c7a13be15976c18ab003265a366cb..fc871b2b777d633dd64b6d339e124668fd760f8b 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -665,7 +665,6 @@ u16 nvmet_zero_sgl(struct nvmet_req *req, off_t off, size_t len);
 u32 nvmet_get_log_page_len(struct nvme_command *cmd);
 u64 nvmet_get_log_page_offset(struct nvme_command *cmd);
 
-extern struct list_head *nvmet_ports;
 void nvmet_port_disc_changed(struct nvmet_port *port,
 		struct nvmet_subsys *subsys);
 void nvmet_subsys_disc_changed(struct nvmet_subsys *subsys,

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 11/12] nvmet: make configfs setup namespace aware
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (9 preceding siblings ...)
  2026-06-23  9:15 ` [PATCH RFC v3 10/12] nvmet: per net-namespace port list Hannes Reinecke
@ 2026-06-23  9:15 ` 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
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

Implement 'fill_subsystem' and 'clear_subsystem' callbacks to make
nvmet configfs namespace aware.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/nvme/target/configfs.c | 103 +++++++++++++++++++++++++++++++----------
 1 file changed, 78 insertions(+), 25 deletions(-)

diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 9c761a7f98496d59aa993e3663f0b3da63b6d3c1..0c0f7719336d80e57a007e74ed542f6276f57fa0 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -1780,6 +1780,7 @@ static void nvmet_subsys_release(struct config_item *item)
 {
 	struct nvmet_subsys *subsys = to_subsys(item);
 
+	configfs_remove_default_groups(&subsys->group);
 	nvmet_subsys_del_ctrls(subsys);
 	nvmet_subsys_put(subsys);
 }
@@ -2072,6 +2073,7 @@ static void nvmet_port_release(struct config_item *item)
 	flush_workqueue(nvmet_wq);
 	nvmet_del_port_list(port);
 
+	configfs_remove_default_groups(&port->group);
 	key_put(port->keyring);
 	kfree(port->ana_state);
 	kfree(port);
@@ -2185,9 +2187,6 @@ static const struct config_item_type nvmet_ports_type = {
 	.ct_owner		= THIS_MODULE,
 };
 
-static struct config_group nvmet_subsystems_group;
-static struct config_group nvmet_ports_group;
-
 #ifdef CONFIG_NVME_TARGET_AUTH
 static ssize_t nvmet_host_dhchap_key_show(struct config_item *item,
 		char *page)
@@ -2369,8 +2368,6 @@ static const struct config_item_type nvmet_hosts_type = {
 	.ct_owner		= THIS_MODULE,
 };
 
-static struct config_group nvmet_hosts_group;
-
 static ssize_t nvmet_root_discovery_nqn_show(struct config_item *item,
 					     char *page)
 {
@@ -2384,9 +2381,11 @@ static ssize_t nvmet_root_discovery_nqn_show(struct config_item *item,
 static ssize_t nvmet_root_discovery_nqn_store(struct config_item *item,
 		const char *page, size_t count)
 {
+	struct config_item *subsystems_item;
+	struct config_group *subsystems_group;
 	struct nvmet_subsys *disc_subsys;
 	struct list_head *entry;
-	char *old_nqn, *new_nqn;
+	char *old_nqn = NULL, *new_nqn;
 	size_t len;
 
 	len = strcspn(page, "\n");
@@ -2398,7 +2397,14 @@ static ssize_t nvmet_root_discovery_nqn_store(struct config_item *item,
 		return -ENOMEM;
 
 	down_write(&nvmet_config_sem);
-	list_for_each(entry, &nvmet_subsystems_group.cg_children) {
+	subsystems_item = config_group_find_item(to_config_group(item), "subsystems");
+	if (WARN_ON(!subsystems_item)) {
+		kfree(new_nqn);
+		up_write(&nvmet_config_sem);
+		return -EINVAL;
+	}
+	subsystems_group = to_config_group(subsystems_item);
+	list_for_each(entry, &subsystems_group->cg_children) {
 		struct config_item *item =
 			container_of(entry, struct config_item, ci_entry);
 
@@ -2432,6 +2438,69 @@ static const struct config_item_type nvmet_root_type = {
 	.ct_owner		= THIS_MODULE,
 };
 
+static int nvmet_configfs_fill_subsystem(struct configfs_subsystem *subsys,
+					 struct net *net_ns)
+{
+	struct config_group *subsystems_group, *ports_group, *hosts_group;
+	int err;
+
+	err = nvmet_add_disc_subsys(net_ns);
+	if (err < 0)
+		return err;
+
+	subsystems_group = kzalloc_obj(*subsystems_group);
+	if (!subsystems_group) {
+		nvmet_del_disc_subsys(net_ns);
+		return -ENOMEM;
+	}
+	config_group_init_type_name(subsystems_group,
+			"subsystems", &nvmet_subsystems_type);
+	configfs_add_default_group(subsystems_group,
+			&subsys->su_group);
+
+	ports_group = kzalloc_obj(*ports_group);
+	if (!ports_group) {
+		kfree(subsystems_group);
+		nvmet_del_disc_subsys(net_ns);
+		return -ENOMEM;
+	}
+	config_group_init_type_name(ports_group,
+			"ports", &nvmet_ports_type);
+	configfs_add_default_group(ports_group,
+			&subsys->su_group);
+
+	hosts_group = kzalloc_obj(*hosts_group);
+	if (!hosts_group) {
+		kfree(ports_group);
+		kfree(subsystems_group);
+		nvmet_del_disc_subsys(net_ns);
+		return -ENOMEM;
+	}
+	config_group_init_type_name(hosts_group,
+			"hosts", &nvmet_hosts_type);
+	configfs_add_default_group(hosts_group,
+			&subsys->su_group);
+	strscpy(subsys->su_group.cg_item.ci_namebuf, "nvmet",
+		sizeof(subsys->su_group.cg_item.ci_namebuf));
+	subsys->su_group.cg_item.ci_type = &nvmet_root_type;
+
+	return 0;
+}
+
+static void nvmet_configfs_clear_subsystem(struct configfs_subsystem *subsys,
+					   struct net *net_ns)
+{
+	struct config_group *g, *n;
+
+	list_for_each_entry_safe(g, n, &subsys->su_group.default_groups,
+				 group_entry) {
+		list_del(&g->group_entry);
+		config_item_put(&g->cg_item);
+		kfree(g);
+	}
+	nvmet_del_disc_subsys(net_ns);
+}
+
 static struct configfs_subsystem nvmet_configfs_subsystem = {
 	.su_group = {
 		.cg_item = {
@@ -2439,30 +2508,14 @@ static struct configfs_subsystem nvmet_configfs_subsystem = {
 			.ci_type	= &nvmet_root_type,
 		},
 	},
+	.fill_subsystem = nvmet_configfs_fill_subsystem,
+	.clear_subsystem = nvmet_configfs_clear_subsystem,
 };
 
 int __init nvmet_init_configfs(void)
 {
 	int ret;
 
-	config_group_init(&nvmet_configfs_subsystem.su_group);
-	mutex_init(&nvmet_configfs_subsystem.su_mutex);
-
-	config_group_init_type_name(&nvmet_subsystems_group,
-			"subsystems", &nvmet_subsystems_type);
-	configfs_add_default_group(&nvmet_subsystems_group,
-			&nvmet_configfs_subsystem.su_group);
-
-	config_group_init_type_name(&nvmet_ports_group,
-			"ports", &nvmet_ports_type);
-	configfs_add_default_group(&nvmet_ports_group,
-			&nvmet_configfs_subsystem.su_group);
-
-	config_group_init_type_name(&nvmet_hosts_group,
-			"hosts", &nvmet_hosts_type);
-	configfs_add_default_group(&nvmet_hosts_group,
-			&nvmet_configfs_subsystem.su_group);
-
 	ret = configfs_register_subsystem(&nvmet_configfs_subsystem);
 	if (ret) {
 		pr_err("configfs_register_subsystem: %d\n", ret);

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v3 12/12] nvmet: enable transports for net namespaces
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (10 preceding siblings ...)
  2026-06-23  9:15 ` [PATCH RFC v3 11/12] nvmet: make configfs setup namespace aware Hannes Reinecke
@ 2026-06-23  9:15 ` Hannes Reinecke
  2026-06-23 14:37 ` [syzbot ci] Re: namespace-aware configfs syzbot ci
  12 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-23  9:15 UTC (permalink / raw)
  To: Andreas Hindborg, Breno Leitao, Alexander Viro, Christian Brauner,
	Jan Kara, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
  Cc: linux-kernel, linux-fsdevel, linux-nvme, Hannes Reinecke

Add a net_ns pointer to the port and modify the transport target
drivers to select the corrent namespaces when creating ports.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/nvme/target/configfs.c  | 19 +++++++++++++------
 drivers/nvme/target/core.c      | 12 ++++++++++--
 drivers/nvme/target/discovery.c | 13 +++++++------
 drivers/nvme/target/loop.c      |  4 ++++
 drivers/nvme/target/nvmet.h     |  3 ++-
 drivers/nvme/target/rdma.c      |  6 +++---
 drivers/nvme/target/tcp.c       |  6 +++---
 fs/configfs/mount.c             | 16 ++++++++++++++++
 include/linux/configfs.h        |  2 ++
 9 files changed, 60 insertions(+), 21 deletions(-)

diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 0c0f7719336d80e57a007e74ed542f6276f57fa0..7636ac3773b4002a4d68aa9d1e666d01129ced55 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -71,7 +71,7 @@ struct list_head *nvmet_get_port_list(struct net *net_ns)
 
 static int nvmet_add_port_list(struct nvmet_port *p)
 {
-	u64 ns_id = nvmet_get_ns_id(&init_net);
+	u64 ns_id = nvmet_get_ns_id(p->net_ns);
 	struct list_head *port_list;
 	int err = 0;
 
@@ -100,7 +100,7 @@ static int nvmet_add_port_list(struct nvmet_port *p)
 static void nvmet_del_port_list(struct nvmet_port *p)
 {
 	struct list_head *port_list;
-	u64 ns_id = nvmet_get_ns_id(&init_net);
+	u64 ns_id = nvmet_get_ns_id(p->net_ns);
 
 	xa_lock(&nvmet_ports_xa);
 	port_list = xa_load(&nvmet_ports_xa, ns_id);
@@ -1799,13 +1799,14 @@ static struct config_group *nvmet_subsys_make(struct config_group *group,
 		const char *name)
 {
 	struct nvmet_subsys *subsys, *disc_subsys;
+	struct net *net_ns = configfs_ns_from_group(group);
 
 	if (sysfs_streq(name, NVME_DISC_SUBSYS_NAME)) {
 		pr_err("can't create discovery subsystem through configfs\n");
 		return ERR_PTR(-EINVAL);
 	}
 
-	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	disc_subsys = nvmet_get_disc_subsys(net_ns);
 	if (!disc_subsys)
 		return ERR_PTR(-ENOENT);
 	if (sysfs_streq(name, disc_subsys->subsysnqn)) {
@@ -1813,7 +1814,7 @@ static struct config_group *nvmet_subsys_make(struct config_group *group,
 		return ERR_PTR(-EINVAL);
 	}
 
-	subsys = nvmet_subsys_alloc(name, NVME_NQN_NVME);
+	subsys = nvmet_subsys_alloc(name, NVME_NQN_NVME, net_ns);
 	if (IS_ERR(subsys))
 		return ERR_CAST(subsys);
 
@@ -2125,6 +2126,10 @@ static struct config_group *nvmet_ports_make(struct config_group *group,
 		return ERR_PTR(-ENOMEM);
 	}
 
+	port->net_ns = configfs_ns_from_group(group);
+	if (!port->net_ns)
+		port->net_ns = get_net(&init_net);
+
 	if (IS_ENABLED(CONFIG_NVME_TARGET_TCP_TLS) && nvme_keyring_id()) {
 		port->keyring = key_lookup(nvme_keyring_id());
 		if (IS_ERR(port->keyring)) {
@@ -2371,7 +2376,8 @@ static const struct config_item_type nvmet_hosts_type = {
 static ssize_t nvmet_root_discovery_nqn_show(struct config_item *item,
 					     char *page)
 {
-	struct nvmet_subsys *disc_subsys = nvmet_get_disc_subsys(&init_net);
+	struct net *net_ns = configfs_ns_from_group(to_config_group(item));
+	struct nvmet_subsys *disc_subsys = nvmet_get_disc_subsys(net_ns);
 
 	if (!disc_subsys)
 		return -ENOENT;
@@ -2383,6 +2389,7 @@ static ssize_t nvmet_root_discovery_nqn_store(struct config_item *item,
 {
 	struct config_item *subsystems_item;
 	struct config_group *subsystems_group;
+	struct net *net_ns = configfs_ns_from_group(to_config_group(item));
 	struct nvmet_subsys *disc_subsys;
 	struct list_head *entry;
 	char *old_nqn = NULL, *new_nqn;
@@ -2415,7 +2422,7 @@ static ssize_t nvmet_root_discovery_nqn_store(struct config_item *item,
 			return -EINVAL;
 		}
 	}
-	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	disc_subsys = nvmet_get_disc_subsys(net_ns);
 	if (disc_subsys) {
 		old_nqn = disc_subsys->subsysnqn;
 		disc_subsys->subsysnqn = new_nqn;
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 1831a218319b84a1e1a1cfba7fb48e3ad40b09f7..39989b8bae92f8e5b384a93c891b9f3e9900305e 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -1803,7 +1803,7 @@ static struct nvmet_subsys *nvmet_find_get_subsys(struct nvmet_port *port,
 	if (!port)
 		return NULL;
 
-	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	disc_subsys = nvmet_get_disc_subsys(port->net_ns);
 	if (!disc_subsys)
 		return NULL;
 
@@ -1835,7 +1835,7 @@ static struct nvmet_subsys *nvmet_find_get_subsys(struct nvmet_port *port,
 }
 
 struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn,
-		enum nvme_subsys_type type)
+		enum nvme_subsys_type type, struct net *net_ns)
 {
 	struct nvmet_subsys *subsys;
 	char serial[NVMET_SN_MAX_SIZE / 2];
@@ -1893,8 +1893,16 @@ struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn,
 	INIT_LIST_HEAD(&subsys->ctrls);
 	INIT_LIST_HEAD(&subsys->hosts);
 
+	/* Disable debugfs for non-initial namespaces */
+	if (net_ns == &init_net) {
+		ret = nvmet_debugfs_subsys_setup(subsys);
+		if (ret)
+			goto free_subsysnqn;
+	}
 	return subsys;
 
+free_subsysnqn:
+	kfree(subsys->subsysnqn);
 free_fr:
 	kfree(subsys->firmware_rev);
 free_mn:
diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c
index e929402314f66b635e330980d4d771b9474e7e3d..b4984fc65ba787456186a0c18ff8586f388fa50d 100644
--- a/drivers/nvme/target/discovery.c
+++ b/drivers/nvme/target/discovery.c
@@ -26,7 +26,7 @@ int nvmet_add_disc_subsys(struct net *net_ns)
 	int err;
 
 	disc_subsys = nvmet_subsys_alloc(NVME_DISC_SUBSYS_NAME,
-					 NVME_NQN_CURR);
+					 NVME_NQN_CURR, net_ns);
 	if (IS_ERR(disc_subsys))
 		return PTR_ERR(disc_subsys);
 	err = xa_insert(&nvmet_disc_xa, ns_id,
@@ -67,7 +67,7 @@ void nvmet_port_disc_changed(struct nvmet_port *port,
 	struct nvmet_subsys *disc_subsys;
 
 	lockdep_assert_held(&nvmet_config_sem);
-	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	disc_subsys = nvmet_get_disc_subsys(port->net_ns);
 	if (WARN_ON(!disc_subsys))
 		return;
 	disc_subsys->genctr++;
@@ -93,7 +93,7 @@ static void __nvmet_subsys_disc_changed(struct nvmet_port *port,
 	struct nvmet_ctrl *ctrl;
 	struct nvmet_subsys *disc_subsys;
 
-	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	disc_subsys = nvmet_get_disc_subsys(port->net_ns);
 	if (WARN_ON(!disc_subsys))
 		return;
 	mutex_lock(&disc_subsys->lock);
@@ -113,14 +113,15 @@ void nvmet_subsys_disc_changed(struct nvmet_subsys *subsys,
 	struct nvmet_subsys_link *s;
 	struct list_head *port_list;
 	struct nvmet_subsys *disc_subsys;
+	struct net *net_ns = configfs_ns_from_group(&subsys->group);
 
 	lockdep_assert_held(&nvmet_config_sem);
-	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	disc_subsys = nvmet_get_disc_subsys(net_ns);
 	if (WARN_ON(!disc_subsys))
 		return;
 	disc_subsys->genctr++;
 
-	port_list = nvmet_get_port_list(&init_net);
+	port_list = nvmet_get_port_list(net_ns);
 	list_for_each_entry(port, port_list, global_entry)
 		list_for_each_entry(s, &port->subsystems, entry) {
 			if (s->subsys != subsys)
@@ -257,7 +258,7 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
 	}
 	hdr = buffer;
 
-	disc_subsys = nvmet_get_disc_subsys(&init_net);
+	disc_subsys = nvmet_get_disc_subsys(req->port->net_ns);
 
 	nvmet_set_disc_traddr(req, req->port, traddr);
 
diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c
index fcb1f8186fdd0d3e2a4d32c9e529ae0cf8d9d42a..0edd690902ce4bbdb692419e718204569ba48c6e 100644
--- a/drivers/nvme/target/loop.c
+++ b/drivers/nvme/target/loop.c
@@ -9,6 +9,7 @@
 #include <linux/nvme.h>
 #include <linux/module.h>
 #include <linux/parser.h>
+#include <linux/nsproxy.h>
 #include "nvmet.h"
 #include "../host/nvme.h"
 #include "../host/fabrics.h"
@@ -538,6 +539,9 @@ static struct nvmet_port *nvme_loop_find_port(struct nvme_ctrl *ctrl)
 
 	mutex_lock(&nvme_loop_ports_mutex);
 	list_for_each_entry(p, &nvme_loop_ports, entry) {
+		/* Skip ports from non-matching namespaces */
+		if (p->net_ns != current->nsproxy->net_ns)
+			continue;
 		/* if no transport address is specified use the first port */
 		if ((ctrl->opts->mask & NVMF_OPT_TRADDR) &&
 		    strcmp(ctrl->opts->traddr, p->disc_addr.traddr))
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index fc871b2b777d633dd64b6d339e124668fd760f8b..681f935d80086b610f137bd64fa27ed16296b3f3 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -210,6 +210,7 @@ struct nvmet_port {
 	struct nvmet_ana_group		ana_default_group;
 	enum nvme_ana_state		*ana_state;
 	struct key			*keyring;
+	struct net			*net_ns;
 	void				*priv;
 	bool				enabled;
 	int				inline_data_size;
@@ -629,7 +630,7 @@ ssize_t nvmet_ctrl_host_traddr(struct nvmet_ctrl *ctrl,
 		char *traddr, size_t traddr_len);
 
 struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn,
-		enum nvme_subsys_type type);
+		enum nvme_subsys_type type, struct net *net_ns);
 void nvmet_subsys_put(struct nvmet_subsys *subsys);
 void nvmet_subsys_del_ctrls(struct nvmet_subsys *subsys);
 
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index ac26f4f774c4de62e163ded12bded70edbab68f7..cad2391a6d0a78928d76d6d10d37f3348b708136 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -1870,8 +1870,8 @@ static int nvmet_rdma_enable_port(struct nvmet_rdma_port *port)
 	struct rdma_cm_id *cm_id;
 	int ret;
 
-	cm_id = rdma_create_id(&init_net, nvmet_rdma_cm_handler, port,
-			RDMA_PS_TCP, IB_QPT_RC);
+	cm_id = rdma_create_id(port->nport->net_ns, nvmet_rdma_cm_handler,
+			port, RDMA_PS_TCP, IB_QPT_RC);
 	if (IS_ERR(cm_id)) {
 		pr_err("CM ID creation failed\n");
 		return PTR_ERR(cm_id);
@@ -1964,7 +1964,7 @@ static int nvmet_rdma_add_port(struct nvmet_port *nport)
 		nport->max_queue_size = NVME_RDMA_MAX_QUEUE_SIZE;
 	}
 
-	ret = inet_pton_with_scope(&init_net, af, nport->disc_addr.traddr,
+	ret = inet_pton_with_scope(nport->net_ns, af, nport->disc_addr.traddr,
 			nport->disc_addr.trsvcid, &port->addr);
 	if (ret) {
 		pr_err("malformed ip/port passed: %s:%s\n",
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index 15c52f1f95f1b040f380d01c0b7ad690355d91be..016bef65a76dd9004471fe8bbcd3550f5910137a 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -2074,7 +2074,7 @@ static int nvmet_tcp_add_port(struct nvmet_port *nport)
 		goto err_port;
 	}
 
-	ret = inet_pton_with_scope(&init_net, af, nport->disc_addr.traddr,
+	ret = inet_pton_with_scope(nport->net_ns, af, nport->disc_addr.traddr,
 			nport->disc_addr.trsvcid, &port->addr);
 	if (ret) {
 		pr_err("malformed ip/port passed: %s:%s\n",
@@ -2087,8 +2087,8 @@ static int nvmet_tcp_add_port(struct nvmet_port *nport)
 	if (port->nport->inline_data_size < 0)
 		port->nport->inline_data_size = NVMET_TCP_DEF_INLINE_DATA_SIZE;
 
-	ret = sock_create(port->addr.ss_family, SOCK_STREAM,
-				IPPROTO_TCP, &port->sock);
+	ret = sock_create_kern(nport->net_ns, port->addr.ss_family, SOCK_STREAM,
+			    IPPROTO_TCP, &port->sock);
 	if (ret) {
 		pr_err("failed to create a socket\n");
 		goto err_port;
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 095a480b6ea27db9a43b067af7e6352fd0ae50ee..7941257d558b72f8f341501a516ceef7c28062f3 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -276,6 +276,22 @@ void configfs_release_fs(struct super_block *sb)
 	mntput(mnt);
 }
 
+struct net *configfs_ns_from_group(struct config_group *group)
+{
+	struct configfs_super_info *info = configfs_root;
+	struct net *net_ns = NULL;
+
+	if (group) {
+		struct dentry *dentry = group->cg_item.ci_dentry;
+
+		info = dentry->d_sb->s_fs_info;
+		if (info)
+			net_ns = info->net_ns;
+	}
+	return net_ns;
+}
+EXPORT_SYMBOL_GPL(configfs_ns_from_group);
+
 static int __init configfs_init(void)
 {
 	int err = -ENOMEM;
diff --git a/include/linux/configfs.h b/include/linux/configfs.h
index 893ae778585fb8be60026c661cb1ef63980ff0e3..76e0bb1a72b9dbbbb3695cfe6b282abb77d03f63 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -258,6 +258,8 @@ configfs_register_default_group(struct config_group *parent_group,
 				const struct config_item_type *item_type);
 void configfs_unregister_default_group(struct config_group *group);
 
+struct net *configfs_ns_from_group(struct config_group *group);
+
 /* These functions can sleep and can alloc with GFP_KERNEL */
 /* WARNING: These cannot be called underneath configfs callbacks!! */
 int configfs_depend_item(struct configfs_subsystem *subsys,

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH RFC v3 01/12] fs/configfs: rework configfs_is_root()
  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
  0 siblings, 0 replies; 15+ messages in thread
From: Breno Leitao @ 2026-06-23 12:50 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: Andreas Hindborg, Alexander Viro, Christian Brauner, Jan Kara,
	Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	linux-kernel, linux-fsdevel, linux-nvme

hello hannes,

On Tue, Jun 23, 2026 at 11:15:46AM +0200, Hannes Reinecke wrote:
> In preparation for making configfs namespace-aware rework
> configfs_is_root() to not rely on static structures but
> rather use the information in the structures themselves.> 

Thanks for the cleanup.  The approach looks fine: CONFIGFS_ROOT is
only ever set on the single static configfs_root dirent.

Nit: "namespace-aware rework configfs_is_root()" reads as if a comma is
missing -- "... namespace-aware, rework configfs_is_root() ...".





> Signed-off-by: Hannes Reinecke <hare@kernel.org>
> ---
>  fs/configfs/configfs_internal.h | 2 +-
>  fs/configfs/mount.c             | 9 +++++++--
>  2 files changed, 8 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
> index acdeea8e2d69ac9d621e1a42b039719811c31757..6261e1f3ec201a58a5d46b08dfbced8c2a44d82b 100644
> --- a/fs/configfs/configfs_internal.h
> +++ b/fs/configfs/configfs_internal.h
> @@ -63,7 +63,7 @@ extern spinlock_t configfs_dirent_lock;
>  
>  extern struct kmem_cache *configfs_dir_cachep;
>  
> -extern int configfs_is_root(struct config_item *item);
> +extern bool configfs_is_root(struct config_item *item);
>  
>  extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *, struct super_block *);
>  extern struct inode *configfs_create(struct dentry *, umode_t mode);
> diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
> index 4929f343118946eaa55a539db4192e9c6621a8dc..88da7b428e52f2f4ab139ad3907101f685d617b1 100644
> --- a/fs/configfs/mount.c
> +++ b/fs/configfs/mount.c
> @@ -47,9 +47,14 @@ static struct config_group configfs_root_group = {
>  	},
>  };
>  
> -int configfs_is_root(struct config_item *item)
> +bool configfs_is_root(struct config_item *item)
>  {
> -	return item == &configfs_root_group.cg_item;
> +	struct configfs_dirent *sd;
> +
> +	if (!item->ci_dentry)
> +		return false;
> +	sd = item->ci_dentry->d_fsdata;
> +	return !!(sd->s_type & CONFIGFS_ROOT);

The old helper was a pure pointer comparison and never touched item, so it
tolerated a NULL or not-yet-attached item.  The new one dereferences
it (item). I suppose that item is never being called as NULL?!

Thanks
--breno

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [syzbot ci] Re: namespace-aware configfs
  2026-06-23  9:15 [PATCH RFC v3 00/12] namespace-aware configfs Hannes Reinecke
                   ` (11 preceding siblings ...)
  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
  12 siblings, 0 replies; 15+ messages in thread
From: syzbot ci @ 2026-06-23 14:37 UTC (permalink / raw)
  To: a.hindborg, brauner, hare, hch, jack, kch, leitao, linux-fsdevel,
	linux-kernel, linux-nvme, sagi, viro
  Cc: syzbot, syzkaller-bugs

syzbot ci has tested the following series

[v3] namespace-aware configfs
https://lore.kernel.org/all/20260623-configfs-ns-v3-0-841c100fd5dd@kernel.org
* [PATCH RFC v3 01/12] fs/configfs: rework configfs_is_root()
* [PATCH RFC v3 02/12] fs/configfs: dynamically allocate super_info
* [PATCH RFC v3 03/12] fs/configfs: separate out configfs_{link,unlink}_root()
* [PATCH RFC v3 04/12] fs/configfs: add superblock as attribute to configfs_pin_fs()
* [PATCH RFC v3 05/12] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks
* [PATCH RFC v3 06/12] fs/namespace: implement mnt_clone_direct()
* [PATCH RFC v3 07/12] fs/configfs: switch to get_tree_keyed()
* [PATCH RFC v3 08/12] fs/configfs: open-code simple_pin_fs()
* [PATCH RFC v3 09/12] nvmet: make discovery subsystem dynamic
* [PATCH RFC v3 10/12] nvmet: per net-namespace port list
* [PATCH RFC v3 11/12] nvmet: make configfs setup namespace aware
* [PATCH RFC v3 12/12] nvmet: enable transports for net namespaces

and found the following issue:
BUG: sleeping function called from invalid context in configfs_get_super_info

Full report is available here:
https://ci.syzbot.org/series/f460be5a-d520-4bc1-9cfb-6492f841629b

***

BUG: sleeping function called from invalid context in configfs_get_super_info

tree:      bpf-next
URL:       https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf-next.git
base:      66affa37cfac0aec061cc4bcf4a065b0c52f7e19
arch:      amd64
compiler:  Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
config:    https://ci.syzbot.org/builds/73803fa0-5ba4-4d03-976f-f9962ee982c8/config
syz repro: https://ci.syzbot.org/findings/4102267c-e678-4ffc-9ac0-4fb2dcbe5c0d/syz_repro

BUG: sleeping function called from invalid context at ./include/linux/sched/mm.h:323
in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 5856, name: syz.0.17
preempt_count: 1, expected: 0
RCU nest depth: 0, expected: 0
INFO: lockdep is turned off.
Preemption disabled at:
[<0000000000000000>] 0x0
CPU: 0 UID: 0 PID: 5856 Comm: syz.0.17 Tainted: G        W           syzkaller #0 PREEMPT(full) 
Tainted: [W]=WARN
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
Call Trace:
 <TASK>
 dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
 __might_resched+0x378/0x4d0 kernel/sched/core.c:9193
 might_alloc include/linux/sched/mm.h:323 [inline]
 slab_pre_alloc_hook mm/slub.c:4559 [inline]
 slab_alloc_node mm/slub.c:4916 [inline]
 __kmalloc_cache_noprof+0x74/0x660 mm/slub.c:5446
 _kmalloc_noprof include/linux/slab.h:969 [inline]
 _kzalloc_noprof include/linux/slab.h:1286 [inline]
 configfs_get_super_info+0x218/0x5e0 fs/configfs/mount.c:91
 configfs_get_tree+0x41/0xd0 fs/configfs/mount.c:170
 vfs_get_tree+0x92/0x2a0 fs/super.c:1694
 vfs_cmd_create+0xa2/0x200 fs/fsopen.c:231
 __do_sys_fsconfig fs/fsopen.c:463 [inline]
 __se_sys_fsconfig+0x6b9/0x810 fs/fsopen.c:350
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5c9db9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f5c9ea71028 EFLAGS: 00000246 ORIG_RAX: 00000000000001af
RAX: ffffffffffffffda RBX: 00007f5c9de15fa0 RCX: 00007f5c9db9ce59
RDX: 0000000000000000 RSI: 0000000000000006 RDI: 0000000000000003
RBP: 00007f5c9dc32e6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f5c9de16038 R14: 00007f5c9de15fa0 R15: 00007ffe63465d98
 </TASK>


***

If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
  Tested-by: syzbot@syzkaller.appspotmail.com

---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.

To test a patch for this bug, please reply with `#syz test`
(should be on a separate line).

The patch should be attached to the email.
Note: arguments like custom git repos and branches are not supported.

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-06-23 14:37 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH RFC v3 02/12] fs/configfs: dynamically allocate super_info Hannes Reinecke
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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.