* [RFC PATCH 0/8] namespace-aware configfs
@ 2026-06-13 11:14 Hannes Reinecke
2026-06-13 11:14 ` [PATCH 1/8] fs/configfs: rework configfs_is_root() Hannes Reinecke
` (7 more replies)
0 siblings, 8 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
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
is stored in an xarray using the 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?
- The current cloning mechanism is not really a hierarchy, but rather
always using the default namespace to find registered subsystems.
Meaning you can only call 'register_subsystem()' for the default
namespace. But then one shouldn't call modprobe in a container, so
that's okay I guess.
- The original content of the configfs remains visible even from
within the container, and the new 'mount' will just overlay that.
Ideally I would have the container start off with an empty
/sys/kernel/config to avoid configuration issues. But again I've
no idea how to do that (or if it's even possible).
Otherwise, as usual, commands and reviews are welcome.
Hannes Reinecke (8):
fs/configfs: rework configfs_is_root()
fs/configfs: dynamically allocate super_info
fs/configfs: separate out configfs_{link,unlink}_root()
fs/namespace: implement mnt_clone_direct()
fs/configfs: add superblock as attribute to configfs_pin_fs()
fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks
fs/configfs: switch to get_tree_keyed()
nvmet: make configfs setup namespace aware
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/configfs_internal.h | 21 +++-
fs/configfs/dir.c | 167 +++++++++++++++++++-----
fs/configfs/mount.c | 216 +++++++++++++++++++++++++++-----
fs/namespace.c | 11 ++
include/linux/configfs.h | 7 ++
include/linux/mount.h | 1 +
11 files changed, 615 insertions(+), 144 deletions(-)
--
2.51.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [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
end of thread, other threads:[~2026-06-13 11:15 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH 3/8] fs/configfs: separate out configfs_{link,unlink}_root() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 4/8] fs/namespace: implement mnt_clone_direct() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 5/8] fs/configfs: add superblock as attribute to configfs_pin_fs() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 6/8] fs/configfs: add 'fill_subsystem' and 'clear_subsystem' callbacks Hannes Reinecke
2026-06-13 11:14 ` [PATCH 7/8] fs/configfs: switch to get_tree_keyed() Hannes Reinecke
2026-06-13 11:14 ` [PATCH 8/8] nvmet: make configfs setup namespace aware Hannes Reinecke
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.