net: netconsole: add a userdata config_group member to netconsole_target

Create configfs machinery for netconsole userdata appending, which depends
on CONFIG_NETCONSOLE_DYNAMIC (for configfs interface). Add a userdata
config_group to netconsole_target for managing userdata entries as a tree
under the netconsole configfs subsystem. Directory names created under the
userdata directory become userdatum keys; the userdatum value is the
content of the value file.

Include the minimum-viable-changes for userdata configfs config_group.
init_target_config_group() ties in the complete configfs machinery to
avoid unused func/variable errors during build. Initializing the
netconsole_target->group is moved to init_target_config_group, which
will also init and add the userdata config_group.

Each userdatum entry has a limit of 256 bytes (54 for
the key/directory, 200 for the value, and 2 for '=' and '\n'
characters), which is enforced by the configfs functions for updating
the userdata config_group.

When a new netconsole_target is created, initialize the userdata
config_group and add it as a default group for netconsole_target
config_group, allowing the userdata configfs sub-tree to be presented
in the netconsole configfs tree under the userdata directory.

Co-developed-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Matthew Wood <thepacketgeek@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Matthew Wood 2024-02-04 15:27:36 -08:00 committed by David S. Miller
parent aa7b608d69
commit 8a6d5fec6c

View File

@ -43,6 +43,10 @@ MODULE_DESCRIPTION("Console driver for network interfaces");
MODULE_LICENSE("GPL");
#define MAX_PARAM_LENGTH 256
#define MAX_USERDATA_NAME_LENGTH 54
#define MAX_USERDATA_VALUE_LENGTH 200
#define MAX_USERDATA_ENTRY_LENGTH 256
#define MAX_USERDATA_ITEMS 16
#define MAX_PRINT_CHUNK 1000
static char config[MAX_PARAM_LENGTH];
@ -80,6 +84,7 @@ static struct console netconsole_ext;
* struct netconsole_target - Represents a configured netconsole target.
* @list: Links this target into the target_list.
* @group: Links us into the configfs subsystem hierarchy.
* @userdata_group: Links to the userdata configfs hierarchy
* @enabled: On / off knob to enable / disable target.
* Visible from userspace (read-write).
* We maintain a strict 1:1 correspondence between this and
@ -103,6 +108,7 @@ struct netconsole_target {
struct list_head list;
#ifdef CONFIG_NETCONSOLE_DYNAMIC
struct config_group group;
struct config_group userdata_group;
#endif
bool enabled;
bool extended;
@ -215,6 +221,10 @@ static struct netconsole_target *alloc_and_init(void)
* | remote_ip
* | local_mac
* | remote_mac
* | userdata/
* | <key>/
* | value
* | ...
* |
* <target>/...
*/
@ -596,6 +606,123 @@ out_unlock:
return -EINVAL;
}
struct userdatum {
struct config_item item;
char value[MAX_USERDATA_VALUE_LENGTH];
};
static struct userdatum *to_userdatum(struct config_item *item)
{
return container_of(item, struct userdatum, item);
}
struct userdata {
struct config_group group;
};
static struct userdata *to_userdata(struct config_item *item)
{
return container_of(to_config_group(item), struct userdata, group);
}
static struct netconsole_target *userdata_to_target(struct userdata *ud)
{
struct config_group *netconsole_group;
netconsole_group = to_config_group(ud->group.cg_item.ci_parent);
return to_target(&netconsole_group->cg_item);
}
static ssize_t userdatum_value_show(struct config_item *item, char *buf)
{
return sysfs_emit(buf, "%s\n", &(to_userdatum(item)->value[0]));
}
static ssize_t userdatum_value_store(struct config_item *item, const char *buf,
size_t count)
{
struct userdatum *udm = to_userdatum(item);
int ret;
if (count > MAX_USERDATA_VALUE_LENGTH)
return -EMSGSIZE;
mutex_lock(&dynamic_netconsole_mutex);
ret = strscpy(udm->value, buf, sizeof(udm->value));
if (ret < 0)
goto out_unlock;
trim_newline(udm->value, sizeof(udm->value));
mutex_unlock(&dynamic_netconsole_mutex);
return count;
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
return ret;
}
CONFIGFS_ATTR(userdatum_, value);
static struct configfs_attribute *userdatum_attrs[] = {
&userdatum_attr_value,
NULL,
};
static void userdatum_release(struct config_item *item)
{
kfree(to_userdatum(item));
}
static struct configfs_item_operations userdatum_ops = {
.release = userdatum_release,
};
static const struct config_item_type userdatum_type = {
.ct_item_ops = &userdatum_ops,
.ct_attrs = userdatum_attrs,
.ct_owner = THIS_MODULE,
};
static struct config_item *userdatum_make_item(struct config_group *group,
const char *name)
{
struct netconsole_target *nt;
struct userdatum *udm;
struct userdata *ud;
size_t child_count;
if (strlen(name) > MAX_USERDATA_NAME_LENGTH)
return ERR_PTR(-ENAMETOOLONG);
ud = to_userdata(&group->cg_item);
nt = userdata_to_target(ud);
child_count = list_count_nodes(&nt->userdata_group.cg_children);
if (child_count >= MAX_USERDATA_ITEMS)
return ERR_PTR(-ENOSPC);
udm = kzalloc(sizeof(*udm), GFP_KERNEL);
if (!udm)
return ERR_PTR(-ENOMEM);
config_item_init_type_name(&udm->item, name, &userdatum_type);
return &udm->item;
}
static struct configfs_attribute *userdata_attrs[] = {
NULL,
};
static struct configfs_group_operations userdata_ops = {
.make_item = userdatum_make_item,
};
static struct config_item_type userdata_type = {
.ct_item_ops = &userdatum_ops,
.ct_group_ops = &userdata_ops,
.ct_attrs = userdata_attrs,
.ct_owner = THIS_MODULE,
};
CONFIGFS_ATTR(, enabled);
CONFIGFS_ATTR(, extended);
CONFIGFS_ATTR(, dev_name);
@ -640,6 +767,15 @@ static const struct config_item_type netconsole_target_type = {
.ct_owner = THIS_MODULE,
};
static void init_target_config_group(struct netconsole_target *nt,
const char *name)
{
config_group_init_type_name(&nt->group, name, &netconsole_target_type);
config_group_init_type_name(&nt->userdata_group, "userdata",
&userdata_type);
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;
@ -674,16 +810,18 @@ static struct config_group *make_netconsole_target(struct config_group *group,
if (!strncmp(name, NETCONSOLE_PARAM_TARGET_PREFIX,
strlen(NETCONSOLE_PARAM_TARGET_PREFIX))) {
nt = find_cmdline_target(name);
if (nt)
if (nt) {
init_target_config_group(nt, name);
return &nt->group;
}
}
nt = alloc_and_init();
if (!nt)
return ERR_PTR(-ENOMEM);
/* Initialize the config_item member */
config_group_init_type_name(&nt->group, name, &netconsole_target_type);
/* Initialize the config_group member */
init_target_config_group(nt, name);
/* Adding, but it is disabled */
spin_lock_irqsave(&target_list_lock, flags);
@ -740,8 +878,7 @@ static void populate_configfs_item(struct netconsole_target *nt,
snprintf(target_name, sizeof(target_name), "%s%d",
NETCONSOLE_PARAM_TARGET_PREFIX, cmdline_count);
config_group_init_type_name(&nt->group, target_name,
&netconsole_target_type);
init_target_config_group(nt, target_name);
}
#endif /* CONFIG_NETCONSOLE_DYNAMIC */