]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
devlink: support default values for param-get and param-set
authorDaniel Zahka <daniel.zahka@gmail.com>
Wed, 19 Nov 2025 02:50:33 +0000 (18:50 -0800)
committerJakub Kicinski <kuba@kernel.org>
Fri, 21 Nov 2025 03:01:22 +0000 (19:01 -0800)
Support querying and resetting to default param values.

Introduce two new devlink netlink attrs:
DEVLINK_ATTR_PARAM_VALUE_DEFAULT and
DEVLINK_ATTR_PARAM_RESET_DEFAULT. The former is used to contain an
optional parameter value inside of the param_value nested
attribute. The latter is used in param-set requests from userspace to
indicate that the driver should reset the param to its default value.

To implement this, two new functions are added to the devlink driver
api: devlink_param::get_default() and
devlink_param::reset_default(). These callbacks allow drivers to
implement default param actions for runtime and permanent cmodes. For
driverinit params, the core latches the last value set by a driver via
devl_param_driverinit_value_set(), and uses that as the default value
for a param.

Because default parameter values are optional, it would be impossible
to discern whether or not a param of type bool has default value of
false or not provided if the default value is encoded using a netlink
flag type. For this reason, when a DEVLINK_PARAM_TYPE_BOOL has an
associated default value, the default value is encoded using a u8
type.

Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com>
Link: https://patch.msgid.link/20251119025038.651131-4-daniel.zahka@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/netlink/specs/devlink.yaml
Documentation/networking/devlink/devlink-params.rst
include/net/devlink.h
include/uapi/linux/devlink.h
net/devlink/netlink_gen.c
net/devlink/param.c

index 426d5aa7d95512879deff54e2bda4600c6e3721b..837112da673807bfc0e8fa5d1092aeb4090dd6c2 100644 (file)
@@ -859,6 +859,14 @@ attribute-sets:
         name: health-reporter-burst-period
         type: u64
         doc: Time (in msec) for recoveries before starting the grace period.
+
+      # TODO: fill in the attributes in between
+
+      -
+        name: param-reset-default
+        type: flag
+        doc: Request restoring parameter to its default value.
+        value: 183
   -
     name: dl-dev-stats
     subset-of: devlink
@@ -1793,6 +1801,7 @@ operations:
             - param-type
             # param-value-data is missing here as the type is variable
             - param-value-cmode
+            - param-reset-default
 
     -
       name: region-get
index c0597d456641d742a7c5d050714bc85668900d8d..ea17756dcda6d33d8141859b6f425fa9ff7d69b7 100644 (file)
@@ -41,6 +41,16 @@ In order for ``driverinit`` parameters to take effect, the driver must
 support reloading via the ``devlink-reload`` command. This command will
 request a reload of the device driver.
 
+Default parameter values
+=========================
+
+Drivers may optionally export default values for parameters of cmode
+``runtime`` and ``permanent``. For ``driverinit`` parameters, the last
+value set by the driver will be used as the default value. Drivers can
+also support resetting params with cmode ``runtime`` and ``permanent``
+to their default values. Resetting ``driverinit`` params is supported
+by devlink core without additional driver support needed.
+
 .. _devlink_params_generic:
 
 Generic configuration parameters
index 5f479227144d765436e8ba31986200698bb8354e..cb839e0435a17f7209535952dd14f0dd7f1e0013 100644 (file)
@@ -479,6 +479,10 @@ struct devlink_flash_notify {
  * @set: set parameter value, used for runtime and permanent
  *       configuration modes
  * @validate: validate input value is applicable (within value range, etc.)
+ * @get_default: get parameter default value, used for runtime and permanent
+ *               configuration modes
+ * @reset_default: reset parameter to default value, used for runtime and permanent
+ *                 configuration modes
  *
  * This struct should be used by the driver to fill the data for
  * a parameter it registers.
@@ -498,6 +502,12 @@ struct devlink_param {
        int (*validate)(struct devlink *devlink, u32 id,
                        union devlink_param_value val,
                        struct netlink_ext_ack *extack);
+       int (*get_default)(struct devlink *devlink, u32 id,
+                          struct devlink_param_gset_ctx *ctx,
+                          struct netlink_ext_ack *extack);
+       int (*reset_default)(struct devlink *devlink, u32 id,
+                            enum devlink_param_cmode cmode,
+                            struct netlink_ext_ack *extack);
 };
 
 struct devlink_param_item {
@@ -509,6 +519,7 @@ struct devlink_param_item {
                                                         * until reload.
                                                         */
        bool driverinit_value_new_valid;
+       union devlink_param_value driverinit_default;
 };
 
 enum devlink_param_generic_id {
@@ -630,6 +641,37 @@ enum devlink_param_generic_id {
        .validate = _validate,                                          \
 }
 
+#define DEVLINK_PARAM_GENERIC_WITH_DEFAULTS(_id, _cmodes, _get, _set,        \
+                                           _validate, _get_default,          \
+                                           _reset_default)                   \
+{                                                                            \
+       .id = DEVLINK_PARAM_GENERIC_ID_##_id,                                 \
+       .name = DEVLINK_PARAM_GENERIC_##_id##_NAME,                           \
+       .type = DEVLINK_PARAM_GENERIC_##_id##_TYPE,                           \
+       .generic = true,                                                      \
+       .supported_cmodes = _cmodes,                                          \
+       .get = _get,                                                          \
+       .set = _set,                                                          \
+       .validate = _validate,                                                \
+       .get_default = _get_default,                                          \
+       .reset_default = _reset_default,                                      \
+}
+
+#define DEVLINK_PARAM_DRIVER_WITH_DEFAULTS(_id, _name, _type, _cmodes,       \
+                                          _get, _set, _validate,             \
+                                          _get_default, _reset_default)      \
+{                                                                            \
+       .id = _id,                                                            \
+       .name = _name,                                                        \
+       .type = _type,                                                        \
+       .supported_cmodes = _cmodes,                                          \
+       .get = _get,                                                          \
+       .set = _set,                                                          \
+       .validate = _validate,                                                \
+       .get_default = _get_default,                                          \
+       .reset_default = _reset_default,                                      \
+}
+
 /* Identifier of board design */
 #define DEVLINK_INFO_VERSION_GENERIC_BOARD_ID  "board.id"
 /* Revision of board design */
index 157f11d3fb72f1ac1e2a7a57bb645147d651461c..e7d6b6d1347063f09e7eda0f660fee7e792f4408 100644 (file)
@@ -639,6 +639,9 @@ enum devlink_attr {
 
        DEVLINK_ATTR_HEALTH_REPORTER_BURST_PERIOD,      /* u64 */
 
+       DEVLINK_ATTR_PARAM_VALUE_DEFAULT,       /* dynamic */
+       DEVLINK_ATTR_PARAM_RESET_DEFAULT,       /* flag */
+
        /* Add new attributes above here, update the spec in
         * Documentation/netlink/specs/devlink.yaml and re-generate
         * net/devlink/netlink_gen.c.
index 5ad435aee29de15156e3aff8233a3e663009721e..580985025f49fcc6d541df1ffe6a9210bea5cba5 100644 (file)
@@ -301,12 +301,13 @@ static const struct nla_policy devlink_param_get_dump_nl_policy[DEVLINK_ATTR_DEV
 };
 
 /* DEVLINK_CMD_PARAM_SET - do */
-static const struct nla_policy devlink_param_set_nl_policy[DEVLINK_ATTR_PARAM_VALUE_CMODE + 1] = {
+static const struct nla_policy devlink_param_set_nl_policy[DEVLINK_ATTR_PARAM_RESET_DEFAULT + 1] = {
        [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, },
        [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
        [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING, },
        [DEVLINK_ATTR_PARAM_TYPE] = NLA_POLICY_VALIDATE_FN(NLA_U8, &devlink_attr_param_type_validate),
        [DEVLINK_ATTR_PARAM_VALUE_CMODE] = NLA_POLICY_MAX(NLA_U8, 2),
+       [DEVLINK_ATTR_PARAM_RESET_DEFAULT] = { .type = NLA_FLAG, },
 };
 
 /* DEVLINK_CMD_REGION_GET - do */
@@ -919,7 +920,7 @@ const struct genl_split_ops devlink_nl_ops[74] = {
                .doit           = devlink_nl_param_set_doit,
                .post_doit      = devlink_nl_post_doit,
                .policy         = devlink_param_set_nl_policy,
-               .maxattr        = DEVLINK_ATTR_PARAM_VALUE_CMODE,
+               .maxattr        = DEVLINK_ATTR_PARAM_RESET_DEFAULT,
                .flags          = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
        },
        {
index 3aa14ef345f0796c3d4c6ee30fd3ab3519bdd50d..e0ea93eded43b577825e0df5b0e7202ae102a4c2 100644 (file)
@@ -192,9 +192,32 @@ static int devlink_param_set(struct devlink *devlink,
        return param->set(devlink, param->id, ctx, extack);
 }
 
+static int devlink_param_get_default(struct devlink *devlink,
+                                    const struct devlink_param *param,
+                                    struct devlink_param_gset_ctx *ctx,
+                                    struct netlink_ext_ack *extack)
+{
+       if (!param->get_default)
+               return -EOPNOTSUPP;
+
+       return param->get_default(devlink, param->id, ctx, extack);
+}
+
+static int devlink_param_reset_default(struct devlink *devlink,
+                                      const struct devlink_param *param,
+                                      enum devlink_param_cmode cmode,
+                                      struct netlink_ext_ack *extack)
+{
+       if (!param->reset_default)
+               return -EOPNOTSUPP;
+
+       return param->reset_default(devlink, param->id, cmode, extack);
+}
+
 static int
 devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type,
-                          int nla_type, union devlink_param_value val)
+                          int nla_type, union devlink_param_value val,
+                          bool flag_as_u8)
 {
        switch (type) {
        case DEVLINK_PARAM_TYPE_U8:
@@ -218,8 +241,16 @@ devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type,
                        return -EMSGSIZE;
                break;
        case DEVLINK_PARAM_TYPE_BOOL:
-               if (val.vbool && nla_put_flag(msg, nla_type))
-                       return -EMSGSIZE;
+               /* default values of type bool are encoded with u8, so that
+                * false can be distinguished from not present
+                */
+               if (flag_as_u8) {
+                       if (nla_put_u8(msg, nla_type, val.vbool))
+                               return -EMSGSIZE;
+               } else {
+                       if (val.vbool && nla_put_flag(msg, nla_type))
+                               return -EMSGSIZE;
+               }
                break;
        }
        return 0;
@@ -229,7 +260,9 @@ static int
 devlink_nl_param_value_fill_one(struct sk_buff *msg,
                                enum devlink_param_type type,
                                enum devlink_param_cmode cmode,
-                               union devlink_param_value val)
+                               union devlink_param_value val,
+                               union devlink_param_value default_val,
+                               bool has_default)
 {
        struct nlattr *param_value_attr;
        int err = -EMSGSIZE;
@@ -243,10 +276,19 @@ devlink_nl_param_value_fill_one(struct sk_buff *msg,
                goto value_nest_cancel;
 
        err = devlink_nl_param_value_put(msg, type,
-                                        DEVLINK_ATTR_PARAM_VALUE_DATA, val);
+                                        DEVLINK_ATTR_PARAM_VALUE_DATA,
+                                        val, false);
        if (err)
                goto value_nest_cancel;
 
+       if (has_default) {
+               err = devlink_nl_param_value_put(msg, type,
+                                                DEVLINK_ATTR_PARAM_VALUE_DEFAULT,
+                                                default_val, true);
+               if (err)
+                       goto value_nest_cancel;
+       }
+
        nla_nest_end(msg, param_value_attr);
        return 0;
 
@@ -262,7 +304,9 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
                                 u32 portid, u32 seq, int flags,
                                 struct netlink_ext_ack *extack)
 {
+       union devlink_param_value default_value[DEVLINK_PARAM_CMODE_MAX + 1];
        union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1];
+       bool default_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
        bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
        const struct devlink_param *param = param_item->param;
        struct devlink_param_gset_ctx ctx;
@@ -283,12 +327,26 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
                                param_value[i] = param_item->driverinit_value;
                        else
                                return -EOPNOTSUPP;
+
+                       if (param_item->driverinit_value_valid) {
+                               default_value[i] = param_item->driverinit_default;
+                               default_value_set[i] = true;
+                       }
                } else {
                        ctx.cmode = i;
                        err = devlink_param_get(devlink, param, &ctx, extack);
                        if (err)
                                return err;
                        param_value[i] = ctx.val;
+
+                       err = devlink_param_get_default(devlink, param, &ctx,
+                                                       extack);
+                       if (!err) {
+                               default_value[i] = ctx.val;
+                               default_value_set[i] = true;
+                       } else if (err != -EOPNOTSUPP) {
+                               return err;
+                       }
                }
                param_value_set[i] = true;
        }
@@ -325,7 +383,9 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
                if (!param_value_set[i])
                        continue;
                err = devlink_nl_param_value_fill_one(msg, param->type,
-                                                     i, param_value[i]);
+                                                     i, param_value[i],
+                                                     default_value[i],
+                                                     default_value_set[i]);
                if (err)
                        goto values_list_nest_cancel;
        }
@@ -542,6 +602,7 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
        struct devlink_param_item *param_item;
        const struct devlink_param *param;
        union devlink_param_value value;
+       bool reset_default;
        int err = 0;
 
        param_item = devlink_param_get_from_info(params, info);
@@ -553,13 +614,18 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
                return err;
        if (param_type != param->type)
                return -EINVAL;
-       err = devlink_param_value_get_from_info(param, info, &value);
-       if (err)
-               return err;
-       if (param->validate) {
-               err = param->validate(devlink, param->id, value, info->extack);
+
+       reset_default = info->attrs[DEVLINK_ATTR_PARAM_RESET_DEFAULT];
+       if (!reset_default) {
+               err = devlink_param_value_get_from_info(param, info, &value);
                if (err)
                        return err;
+               if (param->validate) {
+                       err = param->validate(devlink, param->id, value,
+                                             info->extack);
+                       if (err)
+                               return err;
+               }
        }
 
        if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_VALUE_CMODE))
@@ -569,6 +635,15 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
                return -EOPNOTSUPP;
 
        if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) {
+               if (reset_default) {
+                       if (!param_item->driverinit_value_valid) {
+                               NL_SET_ERR_MSG(info->extack,
+                                              "Default value not available");
+                               return -EOPNOTSUPP;
+                       }
+                       value = param_item->driverinit_default;
+               }
+
                param_item->driverinit_value_new = value;
                param_item->driverinit_value_new_valid = true;
        } else {
@@ -576,7 +651,12 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
                        return -EOPNOTSUPP;
                ctx.val = value;
                ctx.cmode = cmode;
-               err = devlink_param_set(devlink, param, &ctx, info->extack);
+               if (reset_default)
+                       err = devlink_param_reset_default(devlink, param, cmode,
+                                                         info->extack);
+               else
+                       err = devlink_param_set(devlink, param, &ctx,
+                                               info->extack);
                if (err)
                        return err;
        }
@@ -824,6 +904,7 @@ void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
 
        param_item->driverinit_value = init_val;
        param_item->driverinit_value_valid = true;
+       param_item->driverinit_default = init_val;
 
        devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
 }