net: netconsole: add a userdata config_group member to netconsole_target
authorMatthew Wood <thepacketgeek@gmail.com>
Sun, 4 Feb 2024 23:27:36 +0000 (15:27 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 9 Feb 2024 10:23:45 +0000 (10:23 +0000)
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>
drivers/net/netconsole.c

index e6c3b15fe95d145e096500feec802d35ee9fe8eb..3618b9ebcce4230cec8ad7ef544b69c76c0a0ea7 100644 (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 */