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.
Unified · Split
| @@ -859,6 +859,14 @@ attribute-sets: · 858 unmodified lines | ||
| 859 | 859 | name: health-reporter-burst-period |
| 860 | 860 | type: u64 |
| 861 | 861 | doc: Time (in msec) for recoveries before starting the grace period. |
| 862 | + | |
| 863 | + # TODO: fill in the attributes in between | |
| 864 | + | |
| 865 | + - | |
| 866 | + name: param-reset-default | |
| 867 | + type: flag | |
| 868 | + doc: Request restoring parameter to its default value. | |
| 869 | + value: 183 | |
| 862 | 870 | - |
| 863 | 871 | name: dl-dev-stats |
| 864 | 872 | subset-of: devlink |
| @@ -1793,6 +1801,7 @@ operations: · 928 unmodified lines | ||
| 1793 | 1801 | - param-type |
| 1794 | 1802 | # param-value-data is missing here as the type is variable |
| 1795 | 1803 | - param-value-cmode |
| 1804 | + - param-reset-default | |
| 1796 | 1805 | |
| 1797 | 1806 | - |
| 1798 | 1807 | name: region-get |
| @@ -41,6 +41,16 @@ In order for ``driverinit`` parameters to take effect, the driver must · 40 unmodified lines | ||
| 41 | 41 | support reloading via the ``devlink-reload`` command. This command will |
| 42 | 42 | request a reload of the device driver. |
| 43 | 43 | |
| 44 | +Default parameter values | |
| 45 | +========================= | |
| 46 | + | |
| 47 | +Drivers may optionally export default values for parameters of cmode | |
| 48 | +``runtime`` and ``permanent``. For ``driverinit`` parameters, the last | |
| 49 | +value set by the driver will be used as the default value. Drivers can | |
| 50 | +also support resetting params with cmode ``runtime`` and ``permanent`` | |
| 51 | +to their default values. Resetting ``driverinit`` params is supported | |
| 52 | +by devlink core without additional driver support needed. | |
| 53 | + | |
| 44 | 54 | .. _devlink_params_generic: |
| 45 | 55 | |
| 46 | 56 | Generic configuration parameters |
| @@ -479,6 +479,10 @@ struct devlink_flash_notify { · 478 unmodified lines | ||
| 479 | 479 | * @set: set parameter value, used for runtime and permanent |
| 480 | 480 | * configuration modes |
| 481 | 481 | * @validate: validate input value is applicable (within value range, etc.) |
| 482 | + * @get_default: get parameter default value, used for runtime and permanent | |
| 483 | + * configuration modes | |
| 484 | + * @reset_default: reset parameter to default value, used for runtime and permanent | |
| 485 | + * configuration modes | |
| 482 | 486 | * |
| 483 | 487 | * This struct should be used by the driver to fill the data for |
| 484 | 488 | * a parameter it registers. |
| @@ -498,6 +502,12 @@ struct devlink_param { · 13 unmodified lines | ||
| 498 | 502 | int (*validate)(struct devlink *devlink, u32 id, |
| 499 | 503 | union devlink_param_value val, |
| 500 | 504 | struct netlink_ext_ack *extack); |
| 505 | + int (*get_default)(struct devlink *devlink, u32 id, | |
| 506 | + struct devlink_param_gset_ctx *ctx, | |
| 507 | + struct netlink_ext_ack *extack); | |
| 508 | + int (*reset_default)(struct devlink *devlink, u32 id, | |
| 509 | + enum devlink_param_cmode cmode, | |
| 510 | + struct netlink_ext_ack *extack); | |
| 501 | 511 | }; |
| 502 | 512 | |
| 503 | 513 | struct devlink_param_item { |
| @@ -509,6 +519,7 @@ struct devlink_param_item { · 5 unmodified lines | ||
| 509 | 519 | * until reload. |
| 510 | 520 | */ |
| 511 | 521 | bool driverinit_value_new_valid; |
| 522 | + union devlink_param_value driverinit_default; | |
| 512 | 523 | }; |
| 513 | 524 | |
| 514 | 525 | enum devlink_param_generic_id { |
| @@ -630,6 +641,37 @@ enum devlink_param_generic_id { · 115 unmodified lines | ||
| 630 | 641 | .validate = _validate, \ |
| 631 | 642 | } |
| 632 | 643 | |
| 644 | +#define DEVLINK_PARAM_GENERIC_WITH_DEFAULTS(_id, _cmodes, _get, _set, \ | |
| 645 | + _validate, _get_default, \ | |
| 646 | + _reset_default) \ | |
| 647 | +{ \ | |
| 648 | + .id = DEVLINK_PARAM_GENERIC_ID_##_id, \ | |
| 649 | + .name = DEVLINK_PARAM_GENERIC_##_id##_NAME, \ | |
| 650 | + .type = DEVLINK_PARAM_GENERIC_##_id##_TYPE, \ | |
| 651 | + .generic = true, \ | |
| 652 | + .supported_cmodes = _cmodes, \ | |
| 653 | + .get = _get, \ | |
| 654 | + .set = _set, \ | |
| 655 | + .validate = _validate, \ | |
| 656 | + .get_default = _get_default, \ | |
| 657 | + .reset_default = _reset_default, \ | |
| 658 | +} | |
| 659 | + | |
| 660 | +#define DEVLINK_PARAM_DRIVER_WITH_DEFAULTS(_id, _name, _type, _cmodes, \ | |
| 661 | + _get, _set, _validate, \ | |
| 662 | + _get_default, _reset_default) \ | |
| 663 | +{ \ | |
| 664 | + .id = _id, \ | |
| 665 | + .name = _name, \ | |
| 666 | + .type = _type, \ | |
| 667 | + .supported_cmodes = _cmodes, \ | |
| 668 | + .get = _get, \ | |
| 669 | + .set = _set, \ | |
| 670 | + .validate = _validate, \ | |
| 671 | + .get_default = _get_default, \ | |
| 672 | + .reset_default = _reset_default, \ | |
| 673 | +} | |
| 674 | + | |
| 633 | 675 | /* Identifier of board design */ |
| 634 | 676 | #define DEVLINK_INFO_VERSION_GENERIC_BOARD_ID "board.id" |
| 635 | 677 | /* Revision of board design */ |
| @@ -639,6 +639,9 @@ enum devlink_attr { · 638 unmodified lines | ||
| 639 | 639 | |
| 640 | 640 | DEVLINK_ATTR_HEALTH_REPORTER_BURST_PERIOD, /* u64 */ |
| 641 | 641 | |
| 642 | + DEVLINK_ATTR_PARAM_VALUE_DEFAULT, /* dynamic */ | |
| 643 | + DEVLINK_ATTR_PARAM_RESET_DEFAULT, /* flag */ | |
| 644 | + | |
| 642 | 645 | /* Add new attributes above here, update the spec in |
| 643 | 646 | * Documentation/netlink/specs/devlink.yaml and re-generate |
| 644 | 647 | * net/devlink/netlink_gen.c. |
| @@ -301,12 +301,13 @@ static const struct nla_policy devlink_param_get_dump_nl_policy[DEVLINK_ATTR_DEV · 300 unmodified lines | ||
| 301 | 301 | }; |
| 302 | 302 | |
| 303 | 303 | /* DEVLINK_CMD_PARAM_SET - do */ |
| 304 | −static const struct nla_policy devlink_param_set_nl_policy[DEVLINK_ATTR_PARAM_VALUE_CMODE + 1] = { | |
| 304 | +static const struct nla_policy devlink_param_set_nl_policy[DEVLINK_ATTR_PARAM_RESET_DEFAULT + 1] = { | |
| 305 | 305 | [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, }, |
| 306 | 306 | [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, }, |
| 307 | 307 | [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING, }, |
| 308 | 308 | [DEVLINK_ATTR_PARAM_TYPE] = NLA_POLICY_VALIDATE_FN(NLA_U8, &devlink_attr_param_type_validate), |
| 309 | 309 | [DEVLINK_ATTR_PARAM_VALUE_CMODE] = NLA_POLICY_MAX(NLA_U8, 2), |
| 310 | + [DEVLINK_ATTR_PARAM_RESET_DEFAULT] = { .type = NLA_FLAG, }, | |
| 310 | 311 | }; |
| 311 | 312 | |
| 312 | 313 | /* DEVLINK_CMD_REGION_GET - do */ |
| @@ -919,7 +920,7 @@ const struct genl_split_ops devlink_nl_ops[74] = { · 606 unmodified lines | ||
| 919 | 920 | .doit = devlink_nl_param_set_doit, |
| 920 | 921 | .post_doit = devlink_nl_post_doit, |
| 921 | 922 | .policy = devlink_param_set_nl_policy, |
| 922 | − .maxattr = DEVLINK_ATTR_PARAM_VALUE_CMODE, | |
| 923 | + .maxattr = DEVLINK_ATTR_PARAM_RESET_DEFAULT, | |
| 923 | 924 | .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, |
| 924 | 925 | }, |
| 925 | 926 | { |
| @@ -192,9 +192,32 @@ static int devlink_param_set(struct devlink *devlink, · 191 unmodified lines | ||
| 192 | 192 | return param->set(devlink, param->id, ctx, extack); |
| 193 | 193 | } |
| 194 | 194 | |
| 195 | +static int devlink_param_get_default(struct devlink *devlink, | |
| 196 | + const struct devlink_param *param, | |
| 197 | + struct devlink_param_gset_ctx *ctx, | |
| 198 | + struct netlink_ext_ack *extack) | |
| 199 | +{ | |
| 200 | + if (!param->get_default) | |
| 201 | + return -EOPNOTSUPP; | |
| 202 | + | |
| 203 | + return param->get_default(devlink, param->id, ctx, extack); | |
| 204 | +} | |
| 205 | + | |
| 206 | +static int devlink_param_reset_default(struct devlink *devlink, | |
| 207 | + const struct devlink_param *param, | |
| 208 | + enum devlink_param_cmode cmode, | |
| 209 | + struct netlink_ext_ack *extack) | |
| 210 | +{ | |
| 211 | + if (!param->reset_default) | |
| 212 | + return -EOPNOTSUPP; | |
| 213 | + | |
| 214 | + return param->reset_default(devlink, param->id, cmode, extack); | |
| 215 | +} | |
| 216 | + | |
| 195 | 217 | static int |
| 196 | 218 | devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type, |
| 197 | − int nla_type, union devlink_param_value val) | |
| 219 | + int nla_type, union devlink_param_value val, | |
| 220 | + bool flag_as_u8) | |
| 198 | 221 | { |
| 199 | 222 | switch (type) { |
| 200 | 223 | case DEVLINK_PARAM_TYPE_U8: |
| @@ -218,8 +241,16 @@ devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type, · 17 unmodified lines | ||
| 218 | 241 | return -EMSGSIZE; |
| 219 | 242 | break; |
| 220 | 243 | case DEVLINK_PARAM_TYPE_BOOL: |
| 221 | − if (val.vbool && nla_put_flag(msg, nla_type)) | |
| 222 | − return -EMSGSIZE; | |
| 244 | + /* default values of type bool are encoded with u8, so that | |
| 245 | + * false can be distinguished from not present | |
| 246 | + */ | |
| 247 | + if (flag_as_u8) { | |
| 248 | + if (nla_put_u8(msg, nla_type, val.vbool)) | |
| 249 | + return -EMSGSIZE; | |
| 250 | + } else { | |
| 251 | + if (val.vbool && nla_put_flag(msg, nla_type)) | |
| 252 | + return -EMSGSIZE; | |
| 253 | + } | |
| 223 | 254 | break; |
| 224 | 255 | } |
| 225 | 256 | return 0; |
| @@ -229,7 +260,9 @@ static int · 3 unmodified lines | ||
| 229 | 260 | devlink_nl_param_value_fill_one(struct sk_buff *msg, |
| 230 | 261 | enum devlink_param_type type, |
| 231 | 262 | enum devlink_param_cmode cmode, |
| 232 | − union devlink_param_value val) | |
| 263 | + union devlink_param_value val, | |
| 264 | + union devlink_param_value default_val, | |
| 265 | + bool has_default) | |
| 233 | 266 | { |
| 234 | 267 | struct nlattr *param_value_attr; |
| 235 | 268 | int err = -EMSGSIZE; |
| @@ -243,10 +276,19 @@ devlink_nl_param_value_fill_one(struct sk_buff *msg, · 7 unmodified lines | ||
| 243 | 276 | goto value_nest_cancel; |
| 244 | 277 | |
| 245 | 278 | err = devlink_nl_param_value_put(msg, type, |
| 246 | − DEVLINK_ATTR_PARAM_VALUE_DATA, val); | |
| 279 | + DEVLINK_ATTR_PARAM_VALUE_DATA, | |
| 280 | + val, false); | |
| 247 | 281 | if (err) |
| 248 | 282 | goto value_nest_cancel; |
| 249 | 283 | |
| 284 | + if (has_default) { | |
| 285 | + err = devlink_nl_param_value_put(msg, type, | |
| 286 | + DEVLINK_ATTR_PARAM_VALUE_DEFAULT, | |
| 287 | + default_val, true); | |
| 288 | + if (err) | |
| 289 | + goto value_nest_cancel; | |
| 290 | + } | |
| 291 | + | |
| 250 | 292 | nla_nest_end(msg, param_value_attr); |
| 251 | 293 | return 0; |
| 252 | 294 | |
| @@ -262,7 +304,9 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, · 9 unmodified lines | ||
| 262 | 304 | u32 portid, u32 seq, int flags, |
| 263 | 305 | struct netlink_ext_ack *extack) |
| 264 | 306 | { |
| 307 | + union devlink_param_value default_value[DEVLINK_PARAM_CMODE_MAX + 1]; | |
| 265 | 308 | union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1]; |
| 309 | + bool default_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {}; | |
| 266 | 310 | bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {}; |
| 267 | 311 | const struct devlink_param *param = param_item->param; |
| 268 | 312 | struct devlink_param_gset_ctx ctx; |
| @@ -283,12 +327,26 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, · 14 unmodified lines | ||
| 283 | 327 | param_value[i] = param_item->driverinit_value; |
| 284 | 328 | else |
| 285 | 329 | return -EOPNOTSUPP; |
| 330 | + | |
| 331 | + if (param_item->driverinit_value_valid) { | |
| 332 | + default_value[i] = param_item->driverinit_default; | |
| 333 | + default_value_set[i] = true; | |
| 334 | + } | |
| 286 | 335 | } else { |
| 287 | 336 | ctx.cmode = i; |
| 288 | 337 | err = devlink_param_get(devlink, param, &ctx, extack); |
| 289 | 338 | if (err) |
| 290 | 339 | return err; |
| 291 | 340 | param_value[i] = ctx.val; |
| 341 | + | |
| 342 | + err = devlink_param_get_default(devlink, param, &ctx, | |
| 343 | + extack); | |
| 344 | + if (!err) { | |
| 345 | + default_value[i] = ctx.val; | |
| 346 | + default_value_set[i] = true; | |
| 347 | + } else if (err != -EOPNOTSUPP) { | |
| 348 | + return err; | |
| 349 | + } | |
| 292 | 350 | } |
| 293 | 351 | param_value_set[i] = true; |
| 294 | 352 | } |
| @@ -325,7 +383,9 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, · 30 unmodified lines | ||
| 325 | 383 | if (!param_value_set[i]) |
| 326 | 384 | continue; |
| 327 | 385 | err = devlink_nl_param_value_fill_one(msg, param->type, |
| 328 | − i, param_value[i]); | |
| 386 | + i, param_value[i], | |
| 387 | + default_value[i], | |
| 388 | + default_value_set[i]); | |
| 329 | 389 | if (err) |
| 330 | 390 | goto values_list_nest_cancel; |
| 331 | 391 | } |
| @@ -542,6 +602,7 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink, · 210 unmodified lines | ||
| 542 | 602 | struct devlink_param_item *param_item; |
| 543 | 603 | const struct devlink_param *param; |
| 544 | 604 | union devlink_param_value value; |
| 605 | + bool reset_default; | |
| 545 | 606 | int err = 0; |
| 546 | 607 | |
| 547 | 608 | param_item = devlink_param_get_from_info(params, info); |
| @@ -553,13 +614,18 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink, · 5 unmodified lines | ||
| 553 | 614 | return err; |
| 554 | 615 | if (param_type != param->type) |
| 555 | 616 | return -EINVAL; |
| 556 | − err = devlink_param_value_get_from_info(param, info, &value); | |
| 557 | − if (err) | |
| 558 | − return err; | |
| 559 | − if (param->validate) { | |
| 560 | − err = param->validate(devlink, param->id, value, info->extack); | |
| 617 | + | |
| 618 | + reset_default = info->attrs[DEVLINK_ATTR_PARAM_RESET_DEFAULT]; | |
| 619 | + if (!reset_default) { | |
| 620 | + err = devlink_param_value_get_from_info(param, info, &value); | |
| 561 | 621 | if (err) |
| 562 | 622 | return err; |
| 623 | + if (param->validate) { | |
| 624 | + err = param->validate(devlink, param->id, value, | |
| 625 | + info->extack); | |
| 626 | + if (err) | |
| 627 | + return err; | |
| 628 | + } | |
| 563 | 629 | } |
| 564 | 630 | |
| 565 | 631 | 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, · 3 unmodified lines | ||
| 569 | 635 | return -EOPNOTSUPP; |
| 570 | 636 | |
| 571 | 637 | if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) { |
| 638 | + if (reset_default) { | |
| 639 | + if (!param_item->driverinit_value_valid) { | |
| 640 | + NL_SET_ERR_MSG(info->extack, | |
| 641 | + "Default value not available"); | |
| 642 | + return -EOPNOTSUPP; | |
| 643 | + } | |
| 644 | + value = param_item->driverinit_default; | |
| 645 | + } | |
| 646 | + | |
| 572 | 647 | param_item->driverinit_value_new = value; |
| 573 | 648 | param_item->driverinit_value_new_valid = true; |
| 574 | 649 | } else { |
| @@ -576,7 +651,12 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink, · 1 unmodified line | ||
| 576 | 651 | return -EOPNOTSUPP; |
| 577 | 652 | ctx.val = value; |
| 578 | 653 | ctx.cmode = cmode; |
| 579 | − err = devlink_param_set(devlink, param, &ctx, info->extack); | |
| 654 | + if (reset_default) | |
| 655 | + err = devlink_param_reset_default(devlink, param, cmode, | |
| 656 | + info->extack); | |
| 657 | + else | |
| 658 | + err = devlink_param_set(devlink, param, &ctx, | |
| 659 | + info->extack); | |
| 580 | 660 | if (err) |
| 581 | 661 | return err; |
| 582 | 662 | } |
| @@ -824,6 +904,7 @@ void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id, · 241 unmodified lines | ||
| 824 | 904 | |
| 825 | 905 | param_item->driverinit_value = init_val; |
| 826 | 906 | param_item->driverinit_value_valid = true; |
| 907 | + param_item->driverinit_default = init_val; | |
| 827 | 908 | |
| 828 | 909 | devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW); |
| 829 | 910 | } |