Add configfs_register_item() and configfs_unregister_item() functions to allow kernel modules to register configfs items whose lifecycle is controlled by kernel space rather than userspace. This is useful for subsystems that need to expose configuration items that are created based on kernel events (like boot parameters) rather than explicit userspace mkdir operations. The items registered this way are marked as default items (CONFIGFS_USET_DEFAULT) and cannot be removed via rmdir. The API follows the same pattern as configfs_register_group() but for individual items: - configfs_register_item() links the item into the parent group's hierarchy and creates the filesystem representation - configfs_unregister_item() reverses the registration, removing the item from configfs Signed-off-by: Breno Leitao --- fs/configfs/dir.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/configfs.h | 4 +++ 2 files changed, 138 insertions(+) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 81f4f06bc87e..f7224bc51826 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1866,6 +1866,140 @@ void configfs_unregister_default_group(struct config_group *group) } EXPORT_SYMBOL(configfs_unregister_default_group); +/** + * configfs_register_item() - registers a kernel-created item with a parent group + * @parent_group: parent group for the new item + * @item: item to be registered + * + * This function allows kernel code to register configfs items whose lifecycle + * is controlled by kernel space rather than userspace (via mkdir/rmdir). + * The item must be already initialized with config_item_init_type_name(). + * + * Return: 0 on success, negative errno on failure + */ +int configfs_register_item(struct config_group *parent_group, + struct config_item *item) +{ + struct configfs_subsystem *subsys = parent_group->cg_subsys; + struct configfs_fragment *frag; + struct dentry *parent, *child; + struct configfs_dirent *sd; + int ret; + + if (!subsys || !item->ci_name) + return -EINVAL; + + frag = new_fragment(); + if (!frag) + return -ENOMEM; + + parent = parent_group->cg_item.ci_dentry; + /* Allocate dentry for the item */ + child = d_alloc_name(parent, item->ci_name); + if (!child) { + put_fragment(frag); + return -ENOMEM; + } + + mutex_lock(&subsys->su_mutex); + link_obj(&parent_group->cg_item, item); + mutex_unlock(&subsys->su_mutex); + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + d_add(child, NULL); + + /* Attach the item to the filesystem */ + ret = configfs_attach_item(&parent_group->cg_item, item, child, frag); + if (ret) + goto err_out; + + /* + * CONFIGFS_USET_DEFAULT mark item as kernel-created (cannot be removed + * by userspace) + */ + sd = child->d_fsdata; + sd->s_type |= CONFIGFS_USET_DEFAULT; + + /* Mark directory as ready */ + spin_lock(&configfs_dirent_lock); + configfs_dir_set_ready(child->d_fsdata); + spin_unlock(&configfs_dirent_lock); + + inode_unlock(d_inode(parent)); + put_fragment(frag); + return 0; + +err_out: + d_drop(child); + dput(child); + inode_unlock(d_inode(parent)); + mutex_lock(&subsys->su_mutex); + unlink_obj(item); + mutex_unlock(&subsys->su_mutex); + put_fragment(frag); + return ret; +} +EXPORT_SYMBOL(configfs_register_item); + +/** + * configfs_unregister_item() - unregisters a kernel-created item + * @item: item to be unregistered + * + * This function reverses the effect of configfs_register_item(), removing + * the item from the configfs filesystem and releasing associated resources. + * The item must have been previously registered with configfs_register_item(). + */ +void configfs_unregister_item(struct config_item *item) +{ + struct config_group *group = item->ci_group; + struct dentry *dentry = item->ci_dentry; + struct configfs_subsystem *subsys; + struct configfs_fragment *frag; + struct configfs_dirent *sd; + struct dentry *parent; + + if (!group || !dentry) + return; + + subsys = group->cg_subsys; + if (!subsys) + return; + + parent = item->ci_parent->ci_dentry; + sd = dentry->d_fsdata; + frag = get_fragment(sd->s_frag); + + if (WARN_ON(!(sd->s_type & CONFIGFS_USET_DEFAULT))) { + put_fragment(frag); + return; + } + + /* Mark fragment as dead */ + down_write(&frag->frag_sem); + frag->frag_dead = true; + up_write(&frag->frag_sem); + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + spin_lock(&configfs_dirent_lock); + configfs_detach_prep(dentry, NULL); + spin_unlock(&configfs_dirent_lock); + + configfs_detach_item(item); + d_inode(dentry)->i_flags |= S_DEAD; + dont_mount(dentry); + d_drop(dentry); + fsnotify_rmdir(d_inode(parent), dentry); + dput(dentry); + inode_unlock(d_inode(parent)); + + mutex_lock(&subsys->su_mutex); + unlink_obj(item); + mutex_unlock(&subsys->su_mutex); + + put_fragment(frag); +} +EXPORT_SYMBOL(configfs_unregister_item); + int configfs_register_subsystem(struct configfs_subsystem *subsys) { int err; diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 698520b1bfdb..70f2d113b4b3 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -244,6 +244,10 @@ int configfs_register_group(struct config_group *parent_group, struct config_group *group); void configfs_unregister_group(struct config_group *group); +int configfs_register_item(struct config_group *parent_group, + struct config_item *item); +void configfs_unregister_item(struct config_item *item); + void configfs_remove_default_groups(struct config_group *group); struct config_group * -- 2.47.3 Convert netconsole to use the new configfs kernel-space item registration API for command-line configured targets. Previously, netconsole created boot-time targets by searching for them when userspace tried to create an item with the a special name. This approach was fragile and hard for users to deal with. This change refactors netconsole to: - Call configfs_register_item() directly from populate_configfs_item() to properly register boot/module parameter targets. - Remove the find_cmdline_target() logic and the special handling in make_netconsole_target() that intercepted userspace operations (aka the ugly workaround) This makes the management of netconsole easier, simplifies the code (removing ~40 lines) and properly separates kernel-created items from userspace-created items, making the code more maintainable and robust. Signed-off-by: Breno Leitao --- drivers/net/netconsole.c | 63 +++++++++++++++++++++------------------------------------------ 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index bb6e03a92956..d3c720a2f9ef 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -225,8 +225,8 @@ static void netconsole_target_put(struct netconsole_target *nt) { } -static void populate_configfs_item(struct netconsole_target *nt, - int cmdline_count) +static int populate_configfs_item(struct netconsole_target *nt, + int cmdline_count) { } #endif /* CONFIG_NETCONSOLE_DYNAMIC */ @@ -1256,23 +1256,6 @@ static void init_target_config_group(struct netconsole_target *nt, configfs_add_default_group(&nt->userdata_group, &nt->group); } -static struct netconsole_target *find_cmdline_target(const char *name) -{ - struct netconsole_target *nt, *ret = NULL; - unsigned long flags; - - spin_lock_irqsave(&target_list_lock, flags); - list_for_each_entry(nt, &target_list, list) { - if (!strcmp(nt->group.cg_item.ci_name, name)) { - ret = nt; - break; - } - } - spin_unlock_irqrestore(&target_list_lock, flags); - - return ret; -} - /* * Group operations and type for netconsole_subsys. */ @@ -1283,19 +1266,6 @@ static struct config_group *make_netconsole_target(struct config_group *group, struct netconsole_target *nt; unsigned long flags; - /* Checking if a target by this name was created at boot time. If so, - * attach a configfs entry to that target. This enables dynamic - * control. - */ - if (!strncmp(name, NETCONSOLE_PARAM_TARGET_PREFIX, - strlen(NETCONSOLE_PARAM_TARGET_PREFIX))) { - nt = find_cmdline_target(name); - if (nt) { - init_target_config_group(nt, name); - return &nt->group; - } - } - nt = alloc_and_init(); if (!nt) return ERR_PTR(-ENOMEM); @@ -1351,14 +1321,20 @@ static struct configfs_subsystem netconsole_subsys = { }, }; -static void populate_configfs_item(struct netconsole_target *nt, - int cmdline_count) +static int populate_configfs_item(struct netconsole_target *nt, + int cmdline_count) { char target_name[16]; + int ret; snprintf(target_name, sizeof(target_name), "%s%d", NETCONSOLE_PARAM_TARGET_PREFIX, cmdline_count); + init_target_config_group(nt, target_name); + + ret = configfs_register_item(&netconsole_subsys.su_group, + &nt->group.cg_item); + return ret; } static int sysdata_append_cpu_nr(struct netconsole_target *nt, int offset) @@ -1899,7 +1875,9 @@ static struct netconsole_target *alloc_param_target(char *target_config, } else { nt->enabled = true; } - populate_configfs_item(nt, cmdline_count); + err = populate_configfs_item(nt, cmdline_count); + if (err) + goto fail; return nt; @@ -1911,6 +1889,8 @@ static struct netconsole_target *alloc_param_target(char *target_config, /* Cleanup netpoll for given target (from boot/module param) and free it */ static void free_param_target(struct netconsole_target *nt) { + if (nt->group.cg_item.ci_dentry) + configfs_unregister_item(&nt->group.cg_item); netpoll_cleanup(&nt->np); kfree(nt); } @@ -1937,6 +1917,10 @@ static int __init init_netconsole(void) char *target_config; char *input = config; + err = dynamic_netconsole_init(); + if (err) + goto exit; + if (strnlen(input, MAX_PARAM_LENGTH)) { while ((target_config = strsep(&input, ";"))) { nt = alloc_param_target(target_config, count); @@ -1966,10 +1950,6 @@ static int __init init_netconsole(void) if (err) goto fail; - err = dynamic_netconsole_init(); - if (err) - goto undonotifier; - if (console_type_needed & CONS_EXTENDED) register_console(&netconsole_ext); if (console_type_needed & CONS_BASIC) @@ -1978,10 +1958,8 @@ static int __init init_netconsole(void) return err; -undonotifier: - unregister_netdevice_notifier(&netconsole_netdev_notifier); - fail: + dynamic_netconsole_exit(); pr_err("cleaning up\n"); /* @@ -1993,6 +1971,7 @@ static int __init init_netconsole(void) list_del(&nt->list); free_param_target(nt); } +exit: return err; } -- 2.47.3