From: Hannes Reinecke Define 'struct configfs_super_info' to hold all required structures and allocate the filesystem contents dynamically. Signed-off-by: Hannes Reinecke --- fs/configfs/configfs_internal.h | 13 ++++ fs/configfs/dir.c | 42 +++++++----- fs/configfs/mount.c | 148 ++++++++++++++++++++++++++++++---------- include/linux/configfs.h | 1 + 4 files changed, 152 insertions(+), 52 deletions(-) diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h index 6261e1f3ec201a58a5d46b08dfbced8c2a44d82b..94b9e306709150e7a123c46bb359077c6dc917ad 100644 --- a/fs/configfs/configfs_internal.h +++ b/fs/configfs/configfs_internal.h @@ -44,6 +44,17 @@ struct configfs_dirent { struct configfs_fragment *s_frag; }; +struct configfs_super_info { + struct configfs_dirent root; + struct config_group group; + struct list_head subsys_list; + struct mutex subsys_mutex; + struct vfsmount *mnt; + int mnt_count; + struct net *net_ns; + refcount_t ref; +}; + #define CONFIGFS_ROOT 0x0001 #define CONFIGFS_DIR 0x0002 #define CONFIGFS_ITEM_ATTR 0x0004 @@ -81,6 +92,8 @@ extern int configfs_setattr(struct mnt_idmap *idmap, extern struct dentry *configfs_pin_fs(void); extern void configfs_release_fs(void); +extern struct configfs_super_info *configfs_get_super_info(struct net *net_ns); +extern void configfs_put_super_info(struct configfs_super_info *info); extern const struct file_operations configfs_dir_operations; extern const struct file_operations configfs_file_operations; diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 3c88f13f1ca2e9f0ee61ab5003356c070f2c05be..2cd64c13538699683487b0669e8950f5c7bedd54 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -34,14 +34,6 @@ */ DEFINE_SPINLOCK(configfs_dirent_lock); -/* - * All of link_obj/unlink_obj/link_group/unlink_group require that - * subsys->su_mutex is held. - * But parent configfs_subsystem is NULL when config_item is root. - * Use this mutex when config_item is root. - */ -static DEFINE_MUTEX(configfs_subsystem_mutex); - static void configfs_d_iput(struct dentry * dentry, struct inode * inode) { @@ -1871,20 +1863,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); } @@ -1892,9 +1891,9 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys) group->cg_item.ci_name = group->cg_item.ci_namebuf; sd = root->d_fsdata; - mutex_lock(&configfs_subsystem_mutex); + mutex_lock(&info->subsys_mutex); link_group(to_config_group(sd->s_element), group); - mutex_unlock(&configfs_subsystem_mutex); + mutex_unlock(&info->subsys_mutex); inode_lock_nested(d_inode(root), I_MUTEX_PARENT); @@ -1921,26 +1920,34 @@ 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))) { + dput(dentry); + return; + } + if (dentry->d_parent != root) { pr_err("Tried to unregister non-subsystem!\n"); + dput(dentry); + configfs_put_super_info(info); return; } @@ -1970,10 +1977,11 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys) dput(dentry); - mutex_lock(&configfs_subsystem_mutex); + mutex_lock(&info->subsys_mutex); unlink_group(group); - mutex_unlock(&configfs_subsystem_mutex); + mutex_unlock(&info->subsys_mutex); configfs_release_fs(); + configfs_put_super_info(info); } EXPORT_SYMBOL(configfs_register_subsystem); diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c index 88da7b428e52f2f4ab139ad3907101f685d617b1..067bb1099a52ca2216d1b2881429a3fd1811c706 100644 --- a/fs/configfs/mount.c +++ b/fs/configfs/mount.c @@ -22,10 +22,18 @@ /* Random magic number */ #define CONFIGFS_MAGIC 0x62656570 -static struct vfsmount *configfs_mount = NULL; struct kmem_cache *configfs_dir_cachep; -static int configfs_mnt_count = 0; +static DEFINE_XARRAY(configfs_super_xa); +static struct configfs_super_info *configfs_root; +static u64 configfs_ns_id(struct net *net_ns) +{ + struct ns_common *ns = net_ns ? to_ns_common(net_ns) : NULL; + + if (!ns || net_ns == &init_net) + return 0; + return ns->ns_id; +} static void configfs_free_inode(struct inode *inode) { @@ -40,13 +48,6 @@ static const struct super_operations configfs_ops = { .free_inode = configfs_free_inode, }; -static struct config_group configfs_root_group = { - .cg_item = { - .ci_namebuf = "root", - .ci_name = configfs_root_group.cg_item.ci_namebuf, - }, -}; - bool configfs_is_root(struct config_item *item) { struct configfs_dirent *sd; @@ -57,19 +58,76 @@ bool configfs_is_root(struct config_item *item) return !!(sd->s_type & CONFIGFS_ROOT); } -static struct configfs_dirent configfs_root = { - .s_sibling = LIST_HEAD_INIT(configfs_root.s_sibling), - .s_children = LIST_HEAD_INIT(configfs_root.s_children), - .s_element = &configfs_root_group.cg_item, - .s_type = CONFIGFS_ROOT, - .s_iattr = NULL, -}; +static void configfs_fill_super_info(struct configfs_super_info *info) +{ + INIT_LIST_HEAD(&info->root.s_sibling); + INIT_LIST_HEAD(&info->root.s_children); + info->root.s_type = CONFIGFS_ROOT; + info->root.s_element = &info->group.cg_item; + strscpy(info->group.cg_item.ci_namebuf, "root", 4); + info->group.cg_item.ci_name = info->group.cg_item.ci_namebuf; + config_group_init(&info->group); + INIT_LIST_HEAD(&info->subsys_list); + mutex_init(&info->subsys_mutex); + refcount_set(&info->ref, 1); + info->mnt_count = 0; +} + +struct configfs_super_info *configfs_get_super_info(struct net *net_ns) +{ + struct configfs_super_info *info; + u64 ns_id = configfs_ns_id(net_ns); + int err; + + xa_lock(&configfs_super_xa); + info = xa_load(&configfs_super_xa, ns_id); + if (info) { + if (!refcount_inc_not_zero((&info->ref))) + info = ERR_PTR(-EBUSY); + xa_unlock(&configfs_super_xa); + return info; + } + info = kzalloc_obj(*info); + if (!info) { + xa_unlock(&configfs_super_xa); + return ERR_PTR(-ENOMEM); + } + + info->net_ns = get_net(net_ns); + configfs_fill_super_info(info); + err = __xa_insert(&configfs_super_xa, ns_id, + info, GFP_KERNEL); + xa_unlock(&configfs_super_xa); + if (err < 0) { + put_net(info->net_ns); + kfree(info); + return ERR_PTR(err); + } + return info; +} + +void configfs_put_super_info(struct configfs_super_info *info) +{ + u64 ns_id = configfs_ns_id(info->net_ns); + + xa_lock(&configfs_super_xa); + if (!refcount_dec_and_test(&info->ref)) { + xa_unlock(&configfs_super_xa); + return; + } + __xa_erase(&configfs_super_xa, ns_id); + xa_unlock(&configfs_super_xa); + put_net(info->net_ns); + kfree(info); +} static int configfs_fill_super(struct super_block *sb, struct fs_context *fc) { + struct configfs_super_info *info = configfs_get_super_info(&init_net); struct inode *inode; struct dentry *root; + sb->s_fs_info = info; sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; sb->s_magic = CONFIGFS_MAGIC; @@ -77,25 +135,25 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_time_gran = 1; inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, - &configfs_root, sb); - if (inode) { - inode->i_op = &configfs_root_inode_operations; - inode->i_fop = &configfs_dir_operations; - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); - } else { + &info->root, sb); + if (IS_ERR(inode)) { pr_debug("could not get root inode\n"); - return -ENOMEM; + configfs_put_super_info(info); + return PTR_ERR(inode); } + inode->i_op = &configfs_root_inode_operations; + inode->i_fop = &configfs_dir_operations; + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); root = d_make_root(inode); if (!root) { pr_debug("%s: could not get root dentry!\n",__func__); + configfs_put_super_info(info); return -ENOMEM; } - config_group_init(&configfs_root_group); - configfs_root_group.cg_item.ci_dentry = root; - root->d_fsdata = &configfs_root; + info->group.cg_item.ci_dentry = root; + root->d_fsdata = &info->root; sb->s_root = root; set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */ sb->s_d_flags |= DCACHE_DONTCACHE; @@ -117,24 +175,33 @@ static int configfs_init_fs_context(struct fs_context *fc) return 0; } +static void configfs_kill_sb(struct super_block *sb) +{ + struct configfs_super_info *info = sb->s_fs_info; + + kill_anon_super(sb); + configfs_put_super_info(info); + sb->s_fs_info = NULL; +} + static struct file_system_type configfs_fs_type = { .owner = THIS_MODULE, .name = "configfs", .init_fs_context = configfs_init_fs_context, - .kill_sb = kill_anon_super, + .kill_sb = configfs_kill_sb, }; MODULE_ALIAS_FS("configfs"); struct dentry *configfs_pin_fs(void) { - int err = simple_pin_fs(&configfs_fs_type, &configfs_mount, - &configfs_mnt_count); - return err ? ERR_PTR(err) : configfs_mount->mnt_root; + int err = simple_pin_fs(&configfs_fs_type, &configfs_root->mnt, + &configfs_root->mnt_count); + return err ? ERR_PTR(err) : configfs_root->mnt->mnt_root; } void configfs_release_fs(void) { - simple_release_fs(&configfs_mount, &configfs_mnt_count); + simple_release_fs(&configfs_root->mnt, &configfs_root->mnt_count); } @@ -148,18 +215,27 @@ static int __init configfs_init(void) if (!configfs_dir_cachep) goto out; + configfs_root = configfs_get_super_info(&init_net); + if (IS_ERR(configfs_root)) { + err = PTR_ERR(configfs_root); + goto out2; + } + err = sysfs_create_mount_point(kernel_kobj, "config"); if (err) - goto out2; + goto out3; err = register_filesystem(&configfs_fs_type); if (err) - goto out3; + goto out4; return 0; -out3: +out4: pr_err("Unable to register filesystem!\n"); sysfs_remove_mount_point(kernel_kobj, "config"); +out3: + configfs_put_super_info(configfs_root); + configfs_root = NULL; out2: kmem_cache_destroy(configfs_dir_cachep); configfs_dir_cachep = NULL; @@ -171,6 +247,8 @@ static void __exit configfs_exit(void) { unregister_filesystem(&configfs_fs_type); sysfs_remove_mount_point(kernel_kobj, "config"); + configfs_put_super_info(configfs_root); + configfs_root = NULL; kmem_cache_destroy(configfs_dir_cachep); configfs_dir_cachep = NULL; } diff --git a/include/linux/configfs.h b/include/linux/configfs.h index ef65c75beeaad87d33a18e86ac47bf2994705f2e..19165e36da810c75500bb584702ae3a594c3510d 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -24,6 +24,7 @@ #include /* struct list_head */ #include /* struct kref */ #include /* struct mutex */ +#include /* struct net */ #define CONFIGFS_ITEM_NAME_LEN 20 -- 2.51.0