Messages in this thread Patch in this message |  | | From | Mark Bloch <> | | Subject | [RFC net-next 3/4] devlink: Add runtime parameter boot defaults | | Date | Wed, 6 May 2026 15:37:38 +0300 |
| |
Add support for setting devlink device parameters from the devlink= kernel command line parameter.
The supported syntax is:
devlink=[<handle>]:param:<name>:<value>
Parameter values are parsed according to the registered devlink parameter type and are applied in runtime configuration mode. Driverinit and permanent configuration modes are intentionally not part of the boot-default syntax.
Add a helper that finds a parameter by name, verifies runtime mode support, converts the string value, runs the parameter validator and invokes the existing set callback.
Signed-off-by: Mark Bloch <mbloch@nvidia.com> --- .../admin-guide/kernel-parameters.txt | 2 + .../networking/devlink/devlink-defaults.rst | 18 ++- net/devlink/core.c | 110 ++++++++++++------ net/devlink/devl_internal.h | 3 + net/devlink/param.c | 70 +++++++++++ 5 files changed, 165 insertions(+), 38 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 150202882870..761ae45b8607 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1293,9 +1293,11 @@ Kernel parameters Currently supported settings: esw:mode:{ legacy | switchdev | switchdev_inactive } + param:<name>:<value> Examples: devlink=[pci/0000:08:00.0]:esw:mode:switchdev + devlink=[pci/0000:08:00.0]:param:flow_steering_mode:smfs devlink=[pci/0000:08:00.0,pci/0000:08:00.1]:esw:mode:legacy devlink=[pci/0000:08:00.0]:esw:mode:switchdev,[pci/0000:08:00.1]:esw:mode:switchdev_inactive diff --git a/Documentation/networking/devlink/devlink-defaults.rst b/Documentation/networking/devlink/devlink-defaults.rst index 7d6ccaddca86..0d4036e59e88 100644 --- a/Documentation/networking/devlink/devlink-defaults.rst +++ b/Documentation/networking/devlink/devlink-defaults.rst @@ -55,13 +55,16 @@ The following syntax rules apply: * Defaults for the same handle are applied in command-line order. * The same ``esw`` attribute may be specified only once for a given devlink handle. +* The same ``param`` name may be specified only once for a given devlink + handle. * Duplicate entries for the same handle are rejected and all devlink defaults are ignored. +* Parameter names and values must not contain ``:`` or ``,``. Supported defaults ================== -The supported command is ``esw``: +The supported commands are ``esw`` and ``param``: .. list-table:: :widths: 10 25 35 @@ -73,11 +76,16 @@ The supported command is ``esw``: * - ``esw`` - ``mode:<mode>`` - ``legacy``, ``switchdev``, ``switchdev_inactive`` + * - ``param`` + - ``<name>:<value>`` + - ``<value>`` is parsed according to the registered devlink parameter + type. Only runtime devlink parameters are supported. The ``esw:mode`` default corresponds to the userspace command:: devlink dev eswitch set <handle> mode <value> +The ``param`` default applies the named devlink parameter in runtime mode. Examples ======== @@ -90,6 +98,10 @@ Set two PCI devlink instances to legacy mode:: devlink=[pci/0000:08:00.0,pci/0000:08:00.1]:esw:mode:legacy +Set a runtime devlink device parameter:: + + devlink=[pci/0000:08:00.0]:param:flow_steering_mode:smfs + Set different modes for different PCI devlink instances:: devlink=[pci/0000:08:00.0]:esw:mode:switchdev,[pci/0000:08:00.1]:esw:mode:switchdev_inactive @@ -97,3 +109,7 @@ Set different modes for different PCI devlink instances:: The following is invalid because the same handle receives ``esw:mode`` twice:: devlink=[pci/0000:08:00.0]:esw:mode:legacy,[pci/0000:08:00.0]:esw:mode:switchdev + +The following is invalid because the same handle receives ``param:x`` twice:: + + devlink=[pci/0]:param:x:1,[pci/0]:param:x:2 diff --git a/net/devlink/core.c b/net/devlink/core.c index 4b404135181c..22990793ab8c 100644 --- a/net/devlink/core.c +++ b/net/devlink/core.c @@ -50,6 +50,13 @@ struct devlink_default_node { struct list_head cmd_list; }; +struct devlink_default_attr_spec { + const char *name; + enum devlink_attr attr; + int (*value_parse)(const char *value, + struct devlink_default_attr_item *attr_item); +}; + struct devlink_default_cmd_spec { const char *name; enum devlink_command cmd; @@ -73,13 +80,6 @@ devlink_default_attr_free(struct devlink_default_attr_item *attr) kfree(attr->value.param.value); } -struct devlink_default_attr_spec { - const char *name; - enum devlink_attr attr; - int (*value_parse)(const char *value, - struct devlink_default_attr_item *attr_item); -}; - static int __init devlink_default_attr_parse(char *str, const struct devlink_default_attr_spec *attrs, @@ -153,6 +153,33 @@ devlink_default_esw_attr_parse(char *str, attr_item); } +static int __init +devlink_default_param_attr_parse(char *str, + struct devlink_default_attr_item *attr_item) +{ + char *name; + char *value; + + attr_item->attr = DEVLINK_ATTR_PARAM; + + name = strsep(&str, ":"); + value = strsep(&str, ":"); + if (!name || !*name || !value || !*value || str) + return -EINVAL; + + attr_item->value.param.name = kstrdup(name, GFP_KERNEL); + if (!attr_item->value.param.name) + return -ENOMEM; + attr_item->value.param.value = kstrdup(value, GFP_KERNEL); + if (!attr_item->value.param.value) { + kfree(attr_item->value.param.name); + attr_item->value.param.name = NULL; + return -ENOMEM; + } + + return 0; +} + static int devlink_default_eswitch_apply(struct devlink *devlink, const struct devlink_default_attr_item *attr) @@ -171,6 +198,17 @@ devlink_default_eswitch_apply(struct devlink *devlink, } } +static int +devlink_default_param_apply(struct devlink *devlink, + const struct devlink_default_attr_item *attr) +{ + if (attr->attr != DEVLINK_ATTR_PARAM) + return -EOPNOTSUPP; + + return devlink_param_set_from_string(devlink, attr->value.param.name, + attr->value.param.value); +} + static const struct devlink_default_cmd_spec devlink_default_cmds[] __initconst = { { .name = "esw", @@ -178,52 +216,46 @@ static const struct devlink_default_cmd_spec devlink_default_cmds[] __initconst .run = devlink_default_eswitch_apply, .attr_parse = devlink_default_esw_attr_parse, }, + { + .name = "param", + .cmd = DEVLINK_CMD_PARAM_SET, + .run = devlink_default_param_apply, + .attr_parse = devlink_default_param_attr_parse, + }, }; -static const struct devlink_default_cmd_spec *__init -devlink_default_cmd_spec_find(const char *name) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(devlink_default_cmds); i++) { - if (!strcmp(name, devlink_default_cmds[i].name)) - return &devlink_default_cmds[i]; - } - - return NULL; -} - static int __init devlink_default_cmd_parse(char *str, struct devlink_default_cmd_item *cmd_item) { - const struct devlink_default_cmd_spec *spec; struct devlink_default_attr_item attr_item = {}; char *cmd_name; int err; + size_t i; cmd_name = strsep(&str, ":"); if (!cmd_name || !*cmd_name || !str || !*str) return -EINVAL; - spec = devlink_default_cmd_spec_find(cmd_name); - if (!spec) - return -EINVAL; - - err = spec->attr_parse(str, &attr_item); - if (err) { - devlink_default_attr_free(&attr_item); - return err; - } - if (cmd_item) { - cmd_item->cmd = spec->cmd; - cmd_item->run = spec->run; - cmd_item->attr = attr_item; - } else { - devlink_default_attr_free(&attr_item); + for (i = 0; i < ARRAY_SIZE(devlink_default_cmds); i++) { + if (!strcmp(cmd_name, devlink_default_cmds[i].name)) { + err = devlink_default_cmds[i].attr_parse(str, &attr_item); + if (err) { + devlink_default_attr_free(&attr_item); + return err; + } + if (cmd_item) { + cmd_item->cmd = devlink_default_cmds[i].cmd; + cmd_item->run = devlink_default_cmds[i].run; + cmd_item->attr = attr_item; + } else { + devlink_default_attr_free(&attr_item); + } + return 0; + } } - return 0; + return -EINVAL; } static int __init @@ -369,6 +401,10 @@ devlink_default_cmd_equal(const struct devlink_default_cmd_item *a, if (a->cmd != b->cmd || a->attr.attr != b->attr.attr) return false; + if (a->cmd == DEVLINK_CMD_PARAM_SET) + return !strcmp(a->attr.value.param.name, + b->attr.value.param.name); + return true; } diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h index e4e48ee2da5a..bde333c22f18 100644 --- a/net/devlink/devl_internal.h +++ b/net/devlink/devl_internal.h @@ -212,6 +212,9 @@ static inline int devlink_nl_put_u64(struct sk_buff *msg, int attrtype, u64 val) int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net, struct devlink *devlink, int attrtype); int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info); +int devlink_param_set_from_string(struct devlink *devlink, + const char *param_name, + const char *value_str); static inline bool devlink_nl_notify_need(struct devlink *devlink) { diff --git a/net/devlink/param.c b/net/devlink/param.c index cf95268da5b0..d2604fe2eee5 100644 --- a/net/devlink/param.c +++ b/net/devlink/param.c @@ -4,6 +4,8 @@ * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> */ +#include <linux/kstrtox.h> + #include "devl_internal.h" static const struct devlink_param devlink_param_generic[] = { @@ -551,6 +553,74 @@ devlink_param_value_get_from_info(const struct devlink_param *param, return 0; } +static int +devlink_param_value_get_from_str(const struct devlink_param *param, + const char *value_str, + union devlink_param_value *value) +{ + switch (param->type) { + case DEVLINK_PARAM_TYPE_U8: + return kstrtou8(value_str, 0, &value->vu8); + case DEVLINK_PARAM_TYPE_U16: + return kstrtou16(value_str, 0, &value->vu16); + case DEVLINK_PARAM_TYPE_U32: + return kstrtou32(value_str, 0, &value->vu32); + case DEVLINK_PARAM_TYPE_U64: + return kstrtou64(value_str, 0, &value->vu64); + case DEVLINK_PARAM_TYPE_STRING: + if (strscpy(value->vstr, value_str, sizeof(value->vstr)) < 0) + return -EINVAL; + return 0; + case DEVLINK_PARAM_TYPE_BOOL: + return kstrtobool(value_str, &value->vbool); + } + + return -EINVAL; +} + +int devlink_param_set_from_string(struct devlink *devlink, + const char *param_name, + const char *value_str) +{ + struct devlink_param_gset_ctx ctx; + struct devlink_param_item *param_item; + const struct devlink_param *param; + union devlink_param_value value; + int err; + + devl_assert_locked(devlink); + + param_item = devlink_param_find_by_name(&devlink->params, param_name); + if (!param_item) + return -EINVAL; + param = param_item->param; + + if (!devlink_param_cmode_is_supported(param, + DEVLINK_PARAM_CMODE_RUNTIME)) + return -EOPNOTSUPP; + if (!param->set) + return -EOPNOTSUPP; + + err = devlink_param_value_get_from_str(param, value_str, &value); + if (err) + return err; + + if (param->validate) { + err = param->validate(devlink, param->id, value, NULL); + if (err) + return err; + } + + ctx.val = value; + ctx.cmode = DEVLINK_PARAM_CMODE_RUNTIME; + err = devlink_param_set(devlink, param, &ctx, NULL); + if (err) + return err; + + devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW); + return 0; +} + static struct devlink_param_item * devlink_param_get_from_info(struct xarray *params, struct genl_info *info) { -- 2.34.1
|  |