Linux filesystem development
 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox