* [PATCH 1/8] fs/configfs: rework configfs_is_root()
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
@ 2026-06-13 11:14 ` Hannes Reinecke
2026-06-13 11:14 ` [PATCH 2/8] fs/configfs: dynamically allocate super_info Hannes Reinecke
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hannes Reinecke @ 2026-06-13 11:14 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Andreas Hindborg, linux-fsdevel, linux-nvme,
Hannes Reinecke
From: Hannes Reinecke <hare@suse.de>
configfs_is_root() should not check for static structures, but
use the information in the structure itself.
Signed-off-by: Hannes Reinecke <hare@suse.de>
---
fs/configfs/mount.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 4929f3431189..e5c01d5e4d2d 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -49,7 +49,9 @@ static struct config_group configfs_root_group = {
int configfs_is_root(struct config_item *item)
{
- return item == &configfs_root_group.cg_item;
+ struct configfs_dirent *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] 9+ messages in thread* [PATCH 2/8] fs/configfs: dynamically allocate super_info
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
2026-06-13 11:14 ` [PATCH 1/8] fs/configfs: rework configfs_is_root() Hannes Reinecke
@ 2026-06-13 11:14 ` Hannes Reinecke
2026-06-13 11:14 ` [PATCH 3/8] fs/configfs: separate out configfs_{link,unlink}_root() Hannes Reinecke
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hannes Reinecke @ 2026-06-13 11:14 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Andreas Hindborg, linux-fsdevel, linux-nvme,
Hannes Reinecke
From: Hannes Reinecke <hare@suse.de>
Define 'struct configfs_super_info' to hold all required structures
and allocate the filesystem contents dynamically.
Signed-off-by: Hannes Reinecke <hare@suse.de>
---
fs/configfs/configfs_internal.h | 13 ++++
fs/configfs/dir.c | 40 ++++++-----
fs/configfs/mount.c | 117 +++++++++++++++++++++++++-------
3 files changed, 127 insertions(+), 43 deletions(-)
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index acdeea8e2d69..e91f4249b83a 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -44,6 +44,17 @@ struct configfs_dirent {
struct configfs_fragment *s_frag;
};
+struct configfs_super_info {
+ struct configfs_dirent root;
+ struct config_group group;
+ struct list_head subsys_list;
+ struct mutex subsys_mutex;
+ struct vfsmount *mnt;
+ unsigned int mnt_count;
+ u64 ns_id;
+ refcount_t ref;
+};
+
#define CONFIGFS_ROOT 0x0001
#define CONFIGFS_DIR 0x0002
#define CONFIGFS_ITEM_ATTR 0x0004
@@ -81,6 +92,8 @@ extern int configfs_setattr(struct mnt_idmap *idmap,
extern struct dentry *configfs_pin_fs(void);
extern void configfs_release_fs(void);
+extern struct configfs_super_info *configfs_get_super_info(u64 ns_id);
+extern void configfs_put_super_info(struct configfs_super_info *info);
extern const struct file_operations configfs_dir_operations;
extern const struct file_operations configfs_file_operations;
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 4da2fec2b64e..57719bee0c07 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -34,14 +34,6 @@
*/
DEFINE_SPINLOCK(configfs_dirent_lock);
-/*
- * All of link_obj/unlink_obj/link_group/unlink_group require that
- * subsys->su_mutex is held.
- * But parent configfs_subsystem is NULL when config_item is root.
- * Use this mutex when config_item is root.
- */
-static DEFINE_MUTEX(configfs_subsystem_mutex);
-
static void configfs_d_iput(struct dentry * dentry,
struct inode * inode)
{
@@ -1841,20 +1833,27 @@ EXPORT_SYMBOL(configfs_unregister_default_group);
int configfs_register_subsystem(struct configfs_subsystem *subsys)
{
- int err;
+ struct configfs_super_info *info = configfs_get_super_info(0);
struct config_group *group = &subsys->su_group;
struct dentry *dentry;
struct dentry *root;
struct configfs_dirent *sd;
struct configfs_fragment *frag;
+ int err;
+
+ if (IS_ERR(info))
+ return PTR_ERR(info);
frag = new_fragment();
- if (!frag)
+ if (!frag) {
+ configfs_put_super_info(info);
return -ENOMEM;
+ }
root = configfs_pin_fs();
if (IS_ERR(root)) {
put_fragment(frag);
+ configfs_put_super_info(info);
return PTR_ERR(root);
}
@@ -1862,9 +1861,9 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
group->cg_item.ci_name = group->cg_item.ci_namebuf;
sd = root->d_fsdata;
- mutex_lock(&configfs_subsystem_mutex);
+ mutex_lock(&info->subsys_mutex);
link_group(to_config_group(sd->s_element), group);
- mutex_unlock(&configfs_subsystem_mutex);
+ mutex_unlock(&info->subsys_mutex);
inode_lock_nested(d_inode(root), I_MUTEX_PARENT);
@@ -1892,26 +1891,32 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
inode_unlock(d_inode(root));
if (err) {
- mutex_lock(&configfs_subsystem_mutex);
+ mutex_lock(&info->subsys_mutex);
unlink_group(group);
- mutex_unlock(&configfs_subsystem_mutex);
+ mutex_unlock(&info->subsys_mutex);
configfs_release_fs();
}
put_fragment(frag);
-
+ configfs_put_super_info(info);
return err;
}
void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
{
+ struct configfs_super_info *info = configfs_get_super_info(0);
struct config_group *group = &subsys->su_group;
struct dentry *dentry = dget(group->cg_item.ci_dentry);
struct dentry *root = dentry->d_sb->s_root;
struct configfs_dirent *sd = dentry->d_fsdata;
struct configfs_fragment *frag = sd->s_frag;
+ if (WARN_ON(IS_ERR(info)))
+ return;
+
if (dentry->d_parent != root) {
pr_err("Tried to unregister non-subsystem!\n");
+ dput(dentry);
+ configfs_put_super_info(info);
return;
}
@@ -1941,10 +1946,11 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
dput(dentry);
- mutex_lock(&configfs_subsystem_mutex);
+ mutex_lock(&info->subsys_mutex);
unlink_group(group);
- mutex_unlock(&configfs_subsystem_mutex);
+ mutex_unlock(&info->subsys_mutex);
configfs_release_fs();
+ configfs_put_super_info(info);
}
EXPORT_SYMBOL(configfs_register_subsystem);
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index e5c01d5e4d2d..f49c8636eccb 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -22,10 +22,9 @@
/* Random magic number */
#define CONFIGFS_MAGIC 0x62656570
-static struct vfsmount *configfs_mount = NULL;
struct kmem_cache *configfs_dir_cachep;
-static int configfs_mnt_count = 0;
-
+static DEFINE_XARRAY(configfs_super_xa);
+static struct configfs_super_info *configfs_root = NULL;
static void configfs_free_inode(struct inode *inode)
{
@@ -40,13 +39,6 @@ static const struct super_operations configfs_ops = {
.free_inode = configfs_free_inode,
};
-static struct config_group configfs_root_group = {
- .cg_item = {
- .ci_namebuf = "root",
- .ci_name = configfs_root_group.cg_item.ci_namebuf,
- },
-};
-
int configfs_is_root(struct config_item *item)
{
struct configfs_dirent *sd =
@@ -54,16 +46,67 @@ int configfs_is_root(struct config_item *item)
return sd->s_type == CONFIGFS_ROOT;
}
-static struct configfs_dirent configfs_root = {
- .s_sibling = LIST_HEAD_INIT(configfs_root.s_sibling),
- .s_children = LIST_HEAD_INIT(configfs_root.s_children),
- .s_element = &configfs_root_group.cg_item,
- .s_type = CONFIGFS_ROOT,
- .s_iattr = NULL,
-};
+static void configfs_fill_super_info(struct configfs_super_info *info)
+{
+ INIT_LIST_HEAD(&info->root.s_sibling);
+ INIT_LIST_HEAD(&info->root.s_children);
+ info->root.s_type = CONFIGFS_ROOT;
+ info->root.s_element = &info->group.cg_item;
+ strcpy(info->group.cg_item.ci_namebuf, "root");
+ info->group.cg_item.ci_name = info->group.cg_item.ci_namebuf;
+ config_group_init(&info->group);
+ INIT_LIST_HEAD(&info->subsys_list);
+ mutex_init(&info->subsys_mutex);
+ refcount_set(&info->ref, 1);
+ info->mnt_count = 0;
+}
+
+struct configfs_super_info *configfs_get_super_info(u64 ns_id)
+{
+ struct configfs_super_info *info;
+ int err;
+
+ info = xa_load(&configfs_super_xa, ns_id);
+ if (info) {
+ if (!refcount_inc_not_zero((&info->ref))) {
+ pr_warn("%s: ns %llu already freed\n",
+ __func__, ns_id);
+ return ERR_PTR(-EBUSY);
+ }
+ pr_info("%s: use ns %llu\n",
+ __func__, ns_id);
+ return info;
+ }
+ info = kzalloc_obj(*info);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ info->ns_id = ns_id;
+ configfs_fill_super_info(info);
+ err = xa_insert(&configfs_super_xa, ns_id,
+ info, GFP_KERNEL);
+ if (err < 0) {
+ kfree(info);
+ return ERR_PTR(err);
+ }
+ pr_info("%s: alloc ns %llu\n", __func__, info->ns_id);
+ return info;
+}
+
+void configfs_put_super_info(struct configfs_super_info *info)
+{
+ if (refcount_dec_and_test(&info->ref)) {
+ pr_info("%s: ns %llu free fs info\n",
+ __func__, info->ns_id);
+ xa_erase(&configfs_super_xa, info->ns_id);
+ WARN_ON(!list_empty(&info->subsys_list));
+ kfree(info);
+ }
+}
static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
+ struct configfs_super_info *info = configfs_get_super_info(0);
struct inode *inode;
struct dentry *root;
@@ -74,7 +117,7 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_time_gran = 1;
inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
- &configfs_root, sb);
+ &info->root, sb);
if (inode) {
inode->i_op = &configfs_root_inode_operations;
inode->i_fop = &configfs_dir_operations;
@@ -82,18 +125,21 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
inc_nlink(inode);
} else {
pr_debug("could not get root inode\n");
+ configfs_put_super_info(info);
return -ENOMEM;
}
root = d_make_root(inode);
if (!root) {
pr_debug("%s: could not get root dentry!\n",__func__);
+ configfs_put_super_info(info);
return -ENOMEM;
}
- config_group_init(&configfs_root_group);
- configfs_root_group.cg_item.ci_dentry = root;
- root->d_fsdata = &configfs_root;
+ config_group_init(&info->group);
+ info->group.cg_item.ci_dentry = root;
+ root->d_fsdata = &info->root;
sb->s_root = root;
+ sb->s_fs_info = info;
set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
sb->s_d_flags |= DCACHE_DONTCACHE;
return 0;
@@ -114,24 +160,33 @@ static int configfs_init_fs_context(struct fs_context *fc)
return 0;
}
+static void configfs_kill_sb(struct super_block *sb)
+{
+ struct configfs_super_info *info = sb->s_fs_info;
+
+ kill_anon_super(sb);
+ configfs_put_super_info(info);
+ sb->s_fs_info = NULL;
+}
+
static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE,
.name = "configfs",
.init_fs_context = configfs_init_fs_context,
- .kill_sb = kill_anon_super,
+ .kill_sb = configfs_kill_sb,
};
MODULE_ALIAS_FS("configfs");
struct dentry *configfs_pin_fs(void)
{
- int err = simple_pin_fs(&configfs_fs_type, &configfs_mount,
- &configfs_mnt_count);
- return err ? ERR_PTR(err) : configfs_mount->mnt_root;
+ int err = simple_pin_fs(&configfs_fs_type, &configfs_root->mnt,
+ &configfs_root->mnt_count);
+ return err ? ERR_PTR(err) : configfs_root->mnt->mnt_root;
}
void configfs_release_fs(void)
{
- simple_release_fs(&configfs_mount, &configfs_mnt_count);
+ simple_release_fs(&configfs_root->mnt, &configfs_root->mnt_count);
}
@@ -153,7 +208,16 @@ static int __init configfs_init(void)
if (err)
goto out3;
+ configfs_root = configfs_get_super_info(0);
+ if (IS_ERR(configfs_root)) {
+ err = PTR_ERR(configfs_root);
+ goto out4;
+ }
+
return 0;
+out4:
+ pr_err("Unable to get initlal root context\n");
+ unregister_filesystem(&configfs_fs_type);
out3:
pr_err("Unable to register filesystem!\n");
sysfs_remove_mount_point(kernel_kobj, "config");
@@ -166,6 +230,7 @@ static int __init configfs_init(void)
static void __exit configfs_exit(void)
{
+ configfs_put_super_info(configfs_root);
unregister_filesystem(&configfs_fs_type);
sysfs_remove_mount_point(kernel_kobj, "config");
kmem_cache_destroy(configfs_dir_cachep);
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/8] fs/configfs: separate out configfs_{link,unlink}_root()
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
2026-06-13 11:14 ` [PATCH 1/8] fs/configfs: rework configfs_is_root() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 2/8] fs/configfs: dynamically allocate super_info Hannes Reinecke
@ 2026-06-13 11:14 ` Hannes Reinecke
2026-06-13 11:14 ` [PATCH 4/8] fs/namespace: implement mnt_clone_direct() Hannes Reinecke
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hannes Reinecke @ 2026-06-13 11:14 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Andreas Hindborg, linux-fsdevel, linux-nvme,
Hannes Reinecke
From: Hannes Reinecke <hare@suse.de>
Separate out functions to link and unlink subsystem groups.
Signed-off-by: Hannes Reinecke <hare@suse.de>
---
fs/configfs/dir.c | 61 +++++++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 57719bee0c07..cf89c76ea7e6 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1831,31 +1831,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 = configfs_get_super_info(0);
struct config_group *group = &subsys->su_group;
struct dentry *dentry;
- struct dentry *root;
struct configfs_dirent *sd;
struct configfs_fragment *frag;
int err;
- if (IS_ERR(info))
- return PTR_ERR(info);
-
frag = new_fragment();
- if (!frag) {
- 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;
@@ -1894,29 +1882,46 @@ 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 = configfs_get_super_info(0);
+ struct dentry *root;
+ int err;
+
+ 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 void 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;
- if (WARN_ON(IS_ERR(info)))
- return;
-
if (dentry->d_parent != root) {
pr_err("Tried to unregister non-subsystem!\n");
dput(dentry);
- configfs_put_super_info(info);
return;
}
@@ -1945,8 +1950,18 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
inode_unlock(d_inode(root));
dput(dentry);
+}
+
+void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
+{
+ struct configfs_super_info *info = configfs_get_super_info(0);
+ struct config_group *group = &subsys->su_group;
+
+ if (WARN_ON(IS_ERR(info)))
+ return;
mutex_lock(&info->subsys_mutex);
+ configfs_unlink_root(subsys);
unlink_group(group);
mutex_unlock(&info->subsys_mutex);
configfs_release_fs();
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/8] fs/namespace: implement mnt_clone_direct()
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
` (2 preceding siblings ...)
2026-06-13 11:14 ` [PATCH 3/8] fs/configfs: separate out configfs_{link,unlink}_root() Hannes Reinecke
@ 2026-06-13 11:14 ` Hannes Reinecke
2026-06-13 11:14 ` [PATCH 5/8] fs/configfs: add superblock as attribute to configfs_pin_fs() Hannes Reinecke
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hannes Reinecke @ 2026-06-13 11:14 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Andreas Hindborg, 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 fe919abd2f01..dfde8e3c7131 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1477,6 +1477,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 acfe7ef86a1b..7e7e7d66c302 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] 9+ messages in thread* [PATCH 5/8] fs/configfs: add superblock as attribute to configfs_pin_fs()
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
` (3 preceding siblings ...)
2026-06-13 11:14 ` [PATCH 4/8] fs/namespace: implement mnt_clone_direct() Hannes Reinecke
@ 2026-06-13 11:14 ` Hannes Reinecke
2026-06-13 11:14 ` [PATCH 6/8] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks Hannes Reinecke
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hannes Reinecke @ 2026-06-13 11:14 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Andreas Hindborg, 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 | 44 +++++++++++++++++++++++++++------
3 files changed, 44 insertions(+), 14 deletions(-)
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index e91f4249b83a..a91e97194c3d 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(u64 ns_id);
extern void configfs_put_super_info(struct configfs_super_info *info);
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index cf89c76ea7e6..6e111e3fd0e0 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1114,7 +1114,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);
@@ -1141,7 +1141,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;
}
@@ -1896,7 +1896,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;
@@ -1904,7 +1904,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);
@@ -1964,7 +1964,7 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
configfs_unlink_root(subsys);
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 f49c8636eccb..8cf4bda14bec 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -177,18 +177,48 @@ 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;
+
+ if (sb) {
+ struct configfs_super_info *root = info;
+ struct dentry *dentry = sb->s_root;
+ struct vfsmount *mnt;
+
+ info = sb->s_fs_info;
+ if (!info->mnt) {
+ mnt = mnt_clone_direct(root->mnt, dentry);
+ if (IS_ERR(mnt))
+ return ERR_CAST(mnt);
+ info->mnt = mnt;
+ }
+ dget(dentry);
+ mntget(info->mnt);
+ info->mnt_count++;
+ return info->mnt->mnt_root;
+ }
+
+ 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;
+ if (sb) {
+ info = sb->s_fs_info;
+ dput(sb->s_root);
+ }
+
+ pr_debug("release ns %llu\n", info->ns_id);
+ simple_release_fs(&info->mnt, &info->mnt_count);
+}
static int __init configfs_init(void)
{
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 6/8] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
` (4 preceding siblings ...)
2026-06-13 11:14 ` [PATCH 5/8] fs/configfs: add superblock as attribute to configfs_pin_fs() Hannes Reinecke
@ 2026-06-13 11:14 ` Hannes Reinecke
2026-06-13 11:14 ` [PATCH 7/8] fs/configfs: switch to get_tree_keyed() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 8/8] nvmet: make configfs setup namespace aware Hannes Reinecke
7 siblings, 0 replies; 9+ messages in thread
From: Hannes Reinecke @ 2026-06-13 11:14 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Andreas Hindborg, 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 | 90 ++++++++++++++++++++++++++++++++-
fs/configfs/mount.c | 6 +++
include/linux/configfs.h | 5 ++
4 files changed, 103 insertions(+), 2 deletions(-)
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index a91e97194c3d..7992a7da3de6 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(u64 ns_id);
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 6e111e3fd0e0..bf59165acec7 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1902,15 +1902,81 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
goto out_put;
}
+ if (subsys->fill_subsystem) {
+ err = subsys->fill_subsystem(subsys, info->ns_id);
+ if (err)
+ goto out_release;
+ }
+
err = configfs_link_root(info, subsys, root);
+ if (err) {
+ subsys->clear_subsystem(subsys, info->ns_id);
+ goto out_put;
+ }
+
+ 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(0);
+
+ if (WARN_ON(IS_ERR(parent)))
+ return;
+ if (WARN_ON(parent == info))
+ return;
+ mutex_lock(&parent->subsys_mutex);
+ list_for_each_entry(s, &parent->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);
+ err = subsys->fill_subsystem(subsys, info->ns_id);
+ if (err) {
+ kfree(subsys);
+ continue;
+ }
+ root = configfs_pin_fs(sb);
+ if (IS_ERR(root)) {
+ subsys->clear_subsystem(subsys, info->ns_id);
+ kfree(subsys);
+ continue;
+ }
+ err = configfs_link_root(info, subsys, root);
+ if (err) {
+ configfs_release_fs(sb);
+ subsys->clear_subsystem(subsys, info->ns_id);
+ kfree(subsys);
+ continue;
+ }
+ mutex_lock(&info->subsys_mutex);
+ list_add(&subsys->su_link, &info->subsys_list);
+ mutex_unlock(&info->subsys_mutex);
+ }
+ mutex_unlock(&parent->subsys_mutex);
+ configfs_put_super_info(parent);
+}
+
static void configfs_unlink_root(struct configfs_subsystem *subsys)
{
struct config_group *group = &subsys->su_group;
@@ -1952,6 +2018,24 @@ static void configfs_unlink_root(struct configfs_subsystem *subsys)
dput(dentry);
}
+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);
+ configfs_unlink_root(subsys);
+ if (subsys->clear_subsystem)
+ subsys->clear_subsystem(subsys, info->ns_id);
+ 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 = configfs_get_super_info(0);
@@ -1959,9 +2043,11 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
if (WARN_ON(IS_ERR(info)))
return;
-
mutex_lock(&info->subsys_mutex);
+ list_del_init(&subsys->su_link);
configfs_unlink_root(subsys);
+ if (subsys->clear_subsystem)
+ subsys->clear_subsystem(subsys, info->ns_id);
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 8cf4bda14bec..8de3ae0cb6dd 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -142,6 +142,10 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_fs_info = info;
set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
sb->s_d_flags |= DCACHE_DONTCACHE;
+
+ pr_info("%s: ns %llu\n", __func__, info->ns_id);
+ configfs_link_subsystems(sb, info);
+
return 0;
}
@@ -164,6 +168,8 @@ static void configfs_kill_sb(struct super_block *sb)
{
struct configfs_super_info *info = sb->s_fs_info;
+ pr_info("%s: ns %llu\n", __func__, info->ns_id);
+ 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 ef65c75beeaa..ce88da5bdcec 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -226,8 +226,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,
+ u64 ns_id);
+ void (*clear_subsystem)(struct configfs_subsystem *subsys,
+ u64 ns_id);
};
static inline struct configfs_subsystem *to_configfs_subsystem(struct config_group *group)
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7/8] fs/configfs: switch to get_tree_keyed()
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
` (5 preceding siblings ...)
2026-06-13 11:14 ` [PATCH 6/8] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks Hannes Reinecke
@ 2026-06-13 11:14 ` Hannes Reinecke
2026-06-13 11:14 ` [PATCH 8/8] nvmet: make configfs setup namespace aware Hannes Reinecke
7 siblings, 0 replies; 9+ messages in thread
From: Hannes Reinecke @ 2026-06-13 11:14 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Andreas Hindborg, 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 | 49 ++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 44 insertions(+), 5 deletions(-)
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 8de3ae0cb6dd..197f5a13a62d 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/mnt_namespace.h>
#include <linux/configfs.h>
#include "configfs_internal.h"
@@ -26,6 +27,13 @@ struct kmem_cache *configfs_dir_cachep;
static DEFINE_XARRAY(configfs_super_xa);
static struct configfs_super_info *configfs_root = NULL;
+static u64 configfs_ns_id(struct ns_common *ns)
+{
+ if (!ns || is_ns_init_id(ns))
+ return 0;
+ return ns->ns_id;
+}
+
static void configfs_free_inode(struct inode *inode)
{
if (S_ISLNK(inode->i_mode))
@@ -106,9 +114,13 @@ 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(0);
+ struct configfs_super_info *info = sb->s_fs_info;
+ struct ns_common *ns = fc->fs_private;
struct inode *inode;
struct dentry *root;
+ u64 ns_id = configfs_ns_id(ns);
+
+ pr_info("%s: ns %llu\n", __func__, ns_id);
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
@@ -125,21 +137,18 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
inc_nlink(inode);
} else {
pr_debug("could not get root inode\n");
- configfs_put_super_info(info);
return -ENOMEM;
}
root = d_make_root(inode);
if (!root) {
pr_debug("%s: could not get root dentry!\n",__func__);
- configfs_put_super_info(info);
return -ENOMEM;
}
config_group_init(&info->group);
info->group.cg_item.ci_dentry = root;
root->d_fsdata = &info->root;
sb->s_root = root;
- sb->s_fs_info = info;
set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
sb->s_d_flags |= DCACHE_DONTCACHE;
@@ -151,15 +160,44 @@ 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 ns_common *ns = fc->fs_private;
+ struct configfs_super_info *info;
+ int err;
+
+ info = configfs_get_super_info(configfs_ns_id(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 ns_common *ns = fc->fs_private;
+ u64 ns_id = configfs_ns_id(ns);
+
+ fc->fs_private = NULL;
+ pr_info("%s: ns %llu\n", __func__, ns_id);
+ if (__ns_ref_put(ns))
+ pr_debug("%s: drop ns\n", __func__);
}
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 ns_common *ns = from_mnt_ns(current->nsproxy->mnt_ns);
+
+ if (!__ns_ref_get(ns))
+ return -EAGAIN;
+
+ fc->fs_private = ns;
fc->ops = &configfs_context_ops;
return 0;
}
@@ -180,6 +218,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] 9+ messages in thread* [PATCH 8/8] nvmet: make configfs setup namespace aware
2026-06-13 11:14 [RFC PATCH 0/8] namespace-aware configfs Hannes Reinecke
` (6 preceding siblings ...)
2026-06-13 11:14 ` [PATCH 7/8] fs/configfs: switch to get_tree_keyed() Hannes Reinecke
@ 2026-06-13 11:14 ` Hannes Reinecke
7 siblings, 0 replies; 9+ messages in thread
From: Hannes Reinecke @ 2026-06-13 11:14 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Andreas Hindborg, 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 | 183 ++++++++++++++++++++++++++------
drivers/nvme/target/core.c | 35 +++---
drivers/nvme/target/discovery.c | 101 +++++++++++++-----
drivers/nvme/target/nvmet.h | 13 ++-
fs/configfs/dir.c | 4 +
fs/configfs/mount.c | 16 +++
include/linux/configfs.h | 2 +
8 files changed, 273 insertions(+), 85 deletions(-)
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 3a80a5e20d07..dcdf46780608 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -25,8 +25,9 @@
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_IDR(nvmet_ports_idr);
+static DEFINE_MUTEX(nvmet_ports_mutex);
struct nvmet_type_name_map {
u8 type;
@@ -51,6 +52,58 @@ static const struct nvmet_type_name_map nvmet_addr_family[] = {
{ NVMF_ADDR_FAMILY_LOOP, "loop" },
};
+struct list_head *nvmet_get_port_list(u64 ns_id)
+{
+ struct list_head *port_list;
+
+ mutex_lock(&nvmet_ports_mutex);
+ port_list = idr_find(&nvmet_ports_idr, ns_id);
+ mutex_unlock(&nvmet_ports_mutex);
+ return port_list;
+}
+
+static int nvmet_add_port_list(u64 ns_id, struct nvmet_port *p)
+{
+ struct list_head *port_list;
+
+ mutex_lock(&nvmet_ports_mutex);
+ port_list = idr_find(&nvmet_ports_idr, ns_id);
+ if (!port_list) {
+ int err;
+
+ port_list = kzalloc_obj(*port_list);
+ if (!port_list)
+ return -ENOMEM;
+ INIT_LIST_HEAD(port_list);
+ err = idr_alloc(&nvmet_ports_idr, port_list,
+ ns_id, ns_id + 1, GFP_KERNEL);
+ if (err < 0) {
+ kfree(port_list);
+ return err;
+ }
+ WARN_ON(err != ns_id);
+ }
+ list_add(&p->global_entry, port_list);
+ mutex_unlock(&nvmet_ports_mutex);
+ return 0;
+}
+
+static void nvmet_del_port_list(u64 ns_id, struct nvmet_port *p)
+{
+ struct list_head *port_list;
+
+ mutex_lock(&nvmet_ports_mutex);
+ port_list = idr_find(&nvmet_ports_idr, ns_id);
+ if (!WARN_ON(!port_list)) {
+ list_del_init(&p->global_entry);
+ if (list_empty(port_list)) {
+ idr_remove(&nvmet_ports_idr, ns_id);
+ kfree(port_list);
+ }
+ }
+ mutex_unlock(&nvmet_ports_mutex);
+}
+
static bool nvmet_is_port_enabled(struct nvmet_port *p, const char *caller)
{
if (p->enabled)
@@ -1718,6 +1771,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);
}
@@ -1735,19 +1789,21 @@ 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;
+ u64 ns_id = configfs_nsid_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);
}
- if (sysfs_streq(name, nvmet_disc_subsys->subsysnqn)) {
+ disc_subsys = nvmet_get_disc_subsys(ns_id);
+ if (sysfs_streq(name, disc_subsys->subsysnqn)) {
pr_err("can't create subsystem using unique discovery NQN\n");
return ERR_PTR(-EINVAL);
}
- subsys = nvmet_subsys_alloc(name, NVME_NQN_NVME);
+ subsys = nvmet_subsys_alloc(name, NVME_NQN_NVME, ns_id);
if (IS_ERR(subsys))
return ERR_CAST(subsys);
@@ -2002,11 +2058,13 @@ static const struct config_item_type nvmet_ana_groups_type = {
static void nvmet_port_release(struct config_item *item)
{
struct nvmet_port *port = to_nvmet_port(item);
+ u64 ns_id = configfs_nsid_from_group(&port->group);
/* Let inflight controllers teardown complete */
flush_workqueue(nvmet_wq);
- list_del(&port->global_entry);
+ nvmet_del_port_list(ns_id, port);
+ configfs_remove_default_groups(&port->group);
key_put(port->keyring);
kfree(port->ana_state);
kfree(port);
@@ -2041,6 +2099,7 @@ static const struct config_item_type nvmet_port_type = {
static struct config_group *nvmet_ports_make(struct config_group *group,
const char *name)
{
+ u64 ns_id = configfs_nsid_from_group(group);
struct nvmet_port *port;
u16 portid;
u32 i;
@@ -2073,7 +2132,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(ns_id, port);
INIT_LIST_HEAD(&port->entry);
INIT_LIST_HEAD(&port->subsystems);
@@ -2120,9 +2179,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)
@@ -2304,17 +2360,22 @@ 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)
{
- return snprintf(page, PAGE_SIZE, "%s\n", nvmet_disc_subsys->subsysnqn);
+ u64 ns_id = configfs_nsid_from_group(to_config_group(item));
+ struct nvmet_subsys *disc_subsys = nvmet_get_disc_subsys(ns_id);
+
+ 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 config_item *subsystems_item;
+ struct config_group *subsystems_group;
+ u64 ns_id = configfs_nsid_from_group(to_config_group(item));
+ struct nvmet_subsys *disc_subsys;
struct list_head *entry;
char *old_nqn, *new_nqn;
size_t len;
@@ -2328,7 +2389,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);
@@ -2339,8 +2407,9 @@ 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(ns_id);
+ old_nqn = disc_subsys->subsysnqn;
+ disc_subsys->subsysnqn = new_nqn;
up_write(&nvmet_config_sem);
kfree(old_nqn);
@@ -2359,6 +2428,68 @@ static const struct config_item_type nvmet_root_type = {
.ct_owner = THIS_MODULE,
};
+static int nvmet_configfs_fill_subsystem(struct configfs_subsystem *subsys,
+ u64 ns_id)
+{
+ struct config_group *subsystems_group, *ports_group, *hosts_group;
+ int err;
+
+ err = nvmet_add_disc_subsys(ns_id);
+ if (err < 0)
+ return err;
+
+ subsystems_group = kzalloc_obj(*subsystems_group);
+ if (!subsystems_group) {
+ nvmet_del_disc_subsys(ns_id);
+ 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(ns_id);
+ 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(ns_id);
+ return -ENOMEM;
+ }
+ config_group_init_type_name(hosts_group,
+ "hosts", &nvmet_hosts_type);
+ configfs_add_default_group(hosts_group,
+ &subsys->su_group);
+ strcpy(subsys->su_group.cg_item.ci_namebuf, "nvmet");
+ subsys->su_group.cg_item.ci_type = &nvmet_root_type;
+
+ return 0;
+}
+
+static void nvmet_configfs_clear_subsystem(struct configfs_subsystem *subsys,
+ u64 ns_id)
+{
+ 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(ns_id);
+}
+
static struct configfs_subsystem nvmet_configfs_subsystem = {
.su_group = {
.cg_item = {
@@ -2366,30 +2497,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);
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index e28e0010cfea..ead61c06adff 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;
+ u64 ns_id;
if (!port)
return NULL;
+ ns_id = configfs_nsid_from_group(&port->group);
+ disc_subsys = nvmet_get_disc_subsys(ns_id);
+
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) {
@@ -1830,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, u64 ns_id)
{
struct nvmet_subsys *subsys;
char serial[NVMET_SN_MAX_SIZE / 2];
@@ -1888,10 +1893,11 @@ 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;
-
+ if (ns_id) {
+ ret = nvmet_debugfs_subsys_setup(subsys);
+ if (ret)
+ goto free_subsysnqn;
+ }
return subsys;
free_subsysnqn:
@@ -1976,21 +1982,15 @@ 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;
#ifdef CONFIG_NVME_TARGET_BPF
nvmet_bpf_struct_ops_init();
#endif
return 0;
-out_exit_discovery:
- nvmet_exit_discovery();
out_exit_debugfs:
nvmet_exit_debugfs();
out_free_nvmet_aen_work_queue:
@@ -2009,7 +2009,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 e9b35549e254..95a6fc0e690a 100644
--- a/drivers/nvme/target/discovery.c
+++ b/drivers/nvme/target/discovery.c
@@ -8,9 +8,48 @@
#include <generated/utsrelease.h>
#include "nvmet.h"
-struct nvmet_subsys *nvmet_disc_subsys;
+static DEFINE_IDR(nvmet_disc_idr);
+static DEFINE_MUTEX(nvmet_disc_mutex);
-static u64 nvmet_genctr;
+struct nvmet_subsys *nvmet_get_disc_subsys(u64 ns_id)
+{
+ struct nvmet_subsys *subsys;
+
+ mutex_lock(&nvmet_disc_mutex);
+ subsys = idr_find(&nvmet_disc_idr, ns_id);
+ mutex_unlock(&nvmet_disc_mutex);
+ return subsys;
+}
+
+int nvmet_add_disc_subsys(u64 ns_id)
+{
+ struct nvmet_subsys *disc_subsys;
+ int err;
+
+ disc_subsys = nvmet_subsys_alloc(NVME_DISC_SUBSYS_NAME,
+ NVME_NQN_CURR, ns_id);
+ if (IS_ERR(disc_subsys))
+ return PTR_ERR(disc_subsys);
+ mutex_lock(&nvmet_disc_mutex);
+ err = idr_alloc(&nvmet_disc_idr, disc_subsys,
+ ns_id, ns_id + 1, GFP_KERNEL);
+ if (err < 0)
+ nvmet_subsys_put(disc_subsys);
+
+ mutex_unlock(&nvmet_disc_mutex);
+ return err;
+}
+
+void nvmet_del_disc_subsys(u64 ns_id)
+{
+ struct nvmet_subsys *disc_subsys;
+
+ mutex_lock(&nvmet_disc_mutex);
+ disc_subsys = idr_remove(&nvmet_disc_idr, ns_id);
+ if (disc_subsys)
+ nvmet_subsys_put(disc_subsys);
+ mutex_unlock(&nvmet_disc_mutex);
+}
static void __nvmet_disc_changed(struct nvmet_port *port,
struct nvmet_ctrl *ctrl)
@@ -29,18 +68,23 @@ void nvmet_port_disc_changed(struct nvmet_port *port,
struct nvmet_subsys *subsys)
{
struct nvmet_ctrl *ctrl;
+ u64 ns_id = configfs_nsid_from_group(&port->group);
+ struct nvmet_subsys *disc_subsys;
lockdep_assert_held(&nvmet_config_sem);
- nvmet_genctr++;
+ disc_subsys = nvmet_get_disc_subsys(ns_id);
+ 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)
@@ -49,18 +93,22 @@ void nvmet_port_disc_changed(struct nvmet_port *port,
static void __nvmet_subsys_disc_changed(struct nvmet_port *port,
struct nvmet_subsys *subsys,
- struct nvmet_host *host)
+ struct nvmet_host *host, u64 ns_id)
{
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(ns_id);
+ 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,15 +116,22 @@ 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;
+ u64 ns_id = configfs_nsid_from_group(&subsys->group);
lockdep_assert_held(&nvmet_config_sem);
- nvmet_genctr++;
+ disc_subsys = nvmet_get_disc_subsys(ns_id);
+ if (WARN_ON(!disc_subsys))
+ return;
+ disc_subsys->genctr++;
- list_for_each_entry(port, nvmet_ports, global_entry)
+ port_list = nvmet_get_port_list(ns_id);
+ list_for_each_entry(port, port_list, global_entry)
list_for_each_entry(s, &port->subsystems, entry) {
if (s->subsys != subsys)
continue;
- __nvmet_subsys_disc_changed(port, subsys, host);
+ __nvmet_subsys_disc_changed(port, subsys, host, ns_id);
}
}
@@ -172,6 +227,8 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
u16 status = 0;
void *buffer;
char traddr[NVMF_TRADDR_SIZE];
+ u64 ns_id = configfs_nsid_from_group(&req->port->group);
+ struct nvmet_subsys *disc_subsys;
if (!nvmet_check_transfer_len(req, data_len))
return;
@@ -206,10 +263,12 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
}
hdr = buffer;
+ disc_subsys = nvmet_get_disc_subsys(ns_id);
+
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++;
@@ -234,7 +293,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);
@@ -407,15 +466,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 002e94123fb2..d23815a325a5 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -328,6 +328,9 @@ struct nvmet_subsys {
u16 cntlid_min;
u16 cntlid_max;
+ /* For discovery subsystems */
+ u64 genctr;
+
struct list_head ctrls;
struct list_head hosts;
@@ -632,7 +635,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, u64 ns_id);
void nvmet_subsys_put(struct nvmet_subsys *subsys);
void nvmet_subsys_del_ctrls(struct nvmet_subsys *subsys);
@@ -668,7 +671,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,
@@ -702,11 +704,12 @@ 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(u64 ns_id);
-int __init nvmet_init_discovery(void);
-void nvmet_exit_discovery(void);
+extern int nvmet_add_disc_subsys(u64 ns_id);
+extern void nvmet_del_disc_subsys(u64 ns_id);
+extern struct nvmet_subsys *nvmet_get_disc_subsys(u64 ns_id);
-extern struct nvmet_subsys *nvmet_disc_subsys;
extern struct rw_semaphore nvmet_config_sem;
extern u32 nvmet_ana_group_enabled[NVMET_MAX_ANAGRPS + 1];
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index bf59165acec7..a4eb47a34f46 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1903,6 +1903,9 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
}
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->ns_id);
if (err)
goto out_release;
@@ -1951,6 +1954,7 @@ void configfs_link_subsystems(struct super_block *sb,
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->ns_id);
if (err) {
kfree(subsys);
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 197f5a13a62d..4e6d3cb251bb 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -265,6 +265,22 @@ void configfs_release_fs(struct super_block *sb)
simple_release_fs(&info->mnt, &info->mnt_count);
}
+u64 configfs_nsid_from_group(struct config_group *group)
+{
+ struct configfs_super_info *info = configfs_root;
+ u64 ns_id = 0;
+
+ if (group) {
+ struct dentry *dentry = group->cg_item.ci_dentry;
+
+ info = dentry->d_sb->s_fs_info;
+ if (info)
+ ns_id = info->ns_id;
+ }
+ return ns_id;
+}
+EXPORT_SYMBOL_GPL(configfs_nsid_from_group);
+
static int __init configfs_init(void)
{
int err = -ENOMEM;
diff --git a/include/linux/configfs.h b/include/linux/configfs.h
index ce88da5bdcec..9c5facf874d9 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -257,6 +257,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);
+u64 configfs_nsid_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] 9+ messages in thread