mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
devlink: support default values for param-get and param-set
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>
This commit is contained in:
committed by
Jakub Kicinski
parent
17a42aa465
commit
2a367002ed
@@ -859,6 +859,14 @@ attribute-sets:
|
|||||||
name: health-reporter-burst-period
|
name: health-reporter-burst-period
|
||||||
type: u64
|
type: u64
|
||||||
doc: Time (in msec) for recoveries before starting the grace period.
|
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
|
name: dl-dev-stats
|
||||||
subset-of: devlink
|
subset-of: devlink
|
||||||
@@ -1793,6 +1801,7 @@ operations:
|
|||||||
- param-type
|
- param-type
|
||||||
# param-value-data is missing here as the type is variable
|
# param-value-data is missing here as the type is variable
|
||||||
- param-value-cmode
|
- param-value-cmode
|
||||||
|
- param-reset-default
|
||||||
|
|
||||||
-
|
-
|
||||||
name: region-get
|
name: region-get
|
||||||
|
|||||||
@@ -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
|
support reloading via the ``devlink-reload`` command. This command will
|
||||||
request a reload of the device driver.
|
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:
|
.. _devlink_params_generic:
|
||||||
|
|
||||||
Generic configuration parameters
|
Generic configuration parameters
|
||||||
|
|||||||
@@ -479,6 +479,10 @@ struct devlink_flash_notify {
|
|||||||
* @set: set parameter value, used for runtime and permanent
|
* @set: set parameter value, used for runtime and permanent
|
||||||
* configuration modes
|
* configuration modes
|
||||||
* @validate: validate input value is applicable (within value range, etc.)
|
* @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
|
* This struct should be used by the driver to fill the data for
|
||||||
* a parameter it registers.
|
* a parameter it registers.
|
||||||
@@ -498,6 +502,12 @@ struct devlink_param {
|
|||||||
int (*validate)(struct devlink *devlink, u32 id,
|
int (*validate)(struct devlink *devlink, u32 id,
|
||||||
union devlink_param_value val,
|
union devlink_param_value val,
|
||||||
struct netlink_ext_ack *extack);
|
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 {
|
struct devlink_param_item {
|
||||||
@@ -509,6 +519,7 @@ struct devlink_param_item {
|
|||||||
* until reload.
|
* until reload.
|
||||||
*/
|
*/
|
||||||
bool driverinit_value_new_valid;
|
bool driverinit_value_new_valid;
|
||||||
|
union devlink_param_value driverinit_default;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum devlink_param_generic_id {
|
enum devlink_param_generic_id {
|
||||||
@@ -630,6 +641,37 @@ enum devlink_param_generic_id {
|
|||||||
.validate = _validate, \
|
.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 */
|
/* Identifier of board design */
|
||||||
#define DEVLINK_INFO_VERSION_GENERIC_BOARD_ID "board.id"
|
#define DEVLINK_INFO_VERSION_GENERIC_BOARD_ID "board.id"
|
||||||
/* Revision of board design */
|
/* Revision of board design */
|
||||||
|
|||||||
@@ -639,6 +639,9 @@ enum devlink_attr {
|
|||||||
|
|
||||||
DEVLINK_ATTR_HEALTH_REPORTER_BURST_PERIOD, /* u64 */
|
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
|
/* Add new attributes above here, update the spec in
|
||||||
* Documentation/netlink/specs/devlink.yaml and re-generate
|
* Documentation/netlink/specs/devlink.yaml and re-generate
|
||||||
* net/devlink/netlink_gen.c.
|
* net/devlink/netlink_gen.c.
|
||||||
|
|||||||
@@ -301,12 +301,13 @@ static const struct nla_policy devlink_param_get_dump_nl_policy[DEVLINK_ATTR_DEV
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* DEVLINK_CMD_PARAM_SET - do */
|
/* 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_BUS_NAME] = { .type = NLA_NUL_STRING, },
|
||||||
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
|
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
|
||||||
[DEVLINK_ATTR_PARAM_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_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_VALUE_CMODE] = NLA_POLICY_MAX(NLA_U8, 2),
|
||||||
|
[DEVLINK_ATTR_PARAM_RESET_DEFAULT] = { .type = NLA_FLAG, },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DEVLINK_CMD_REGION_GET - do */
|
/* DEVLINK_CMD_REGION_GET - do */
|
||||||
@@ -919,7 +920,7 @@ const struct genl_split_ops devlink_nl_ops[74] = {
|
|||||||
.doit = devlink_nl_param_set_doit,
|
.doit = devlink_nl_param_set_doit,
|
||||||
.post_doit = devlink_nl_post_doit,
|
.post_doit = devlink_nl_post_doit,
|
||||||
.policy = devlink_param_set_nl_policy,
|
.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,
|
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -192,9 +192,32 @@ static int devlink_param_set(struct devlink *devlink,
|
|||||||
return param->set(devlink, param->id, ctx, extack);
|
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
|
static int
|
||||||
devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type,
|
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) {
|
switch (type) {
|
||||||
case DEVLINK_PARAM_TYPE_U8:
|
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;
|
return -EMSGSIZE;
|
||||||
break;
|
break;
|
||||||
case DEVLINK_PARAM_TYPE_BOOL:
|
case DEVLINK_PARAM_TYPE_BOOL:
|
||||||
if (val.vbool && nla_put_flag(msg, nla_type))
|
/* default values of type bool are encoded with u8, so that
|
||||||
return -EMSGSIZE;
|
* 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;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -229,7 +260,9 @@ static int
|
|||||||
devlink_nl_param_value_fill_one(struct sk_buff *msg,
|
devlink_nl_param_value_fill_one(struct sk_buff *msg,
|
||||||
enum devlink_param_type type,
|
enum devlink_param_type type,
|
||||||
enum devlink_param_cmode cmode,
|
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;
|
struct nlattr *param_value_attr;
|
||||||
int err = -EMSGSIZE;
|
int err = -EMSGSIZE;
|
||||||
@@ -243,10 +276,19 @@ devlink_nl_param_value_fill_one(struct sk_buff *msg,
|
|||||||
goto value_nest_cancel;
|
goto value_nest_cancel;
|
||||||
|
|
||||||
err = devlink_nl_param_value_put(msg, type,
|
err = devlink_nl_param_value_put(msg, type,
|
||||||
DEVLINK_ATTR_PARAM_VALUE_DATA, val);
|
DEVLINK_ATTR_PARAM_VALUE_DATA,
|
||||||
|
val, false);
|
||||||
if (err)
|
if (err)
|
||||||
goto value_nest_cancel;
|
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);
|
nla_nest_end(msg, param_value_attr);
|
||||||
return 0;
|
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,
|
u32 portid, u32 seq, int flags,
|
||||||
struct netlink_ext_ack *extack)
|
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];
|
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] = {};
|
bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
|
||||||
const struct devlink_param *param = param_item->param;
|
const struct devlink_param *param = param_item->param;
|
||||||
struct devlink_param_gset_ctx ctx;
|
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;
|
param_value[i] = param_item->driverinit_value;
|
||||||
else
|
else
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (param_item->driverinit_value_valid) {
|
||||||
|
default_value[i] = param_item->driverinit_default;
|
||||||
|
default_value_set[i] = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.cmode = i;
|
ctx.cmode = i;
|
||||||
err = devlink_param_get(devlink, param, &ctx, extack);
|
err = devlink_param_get(devlink, param, &ctx, extack);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
param_value[i] = ctx.val;
|
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;
|
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])
|
if (!param_value_set[i])
|
||||||
continue;
|
continue;
|
||||||
err = devlink_nl_param_value_fill_one(msg, param->type,
|
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)
|
if (err)
|
||||||
goto values_list_nest_cancel;
|
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;
|
struct devlink_param_item *param_item;
|
||||||
const struct devlink_param *param;
|
const struct devlink_param *param;
|
||||||
union devlink_param_value value;
|
union devlink_param_value value;
|
||||||
|
bool reset_default;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
param_item = devlink_param_get_from_info(params, info);
|
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;
|
return err;
|
||||||
if (param_type != param->type)
|
if (param_type != param->type)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
err = devlink_param_value_get_from_info(param, info, &value);
|
|
||||||
if (err)
|
reset_default = info->attrs[DEVLINK_ATTR_PARAM_RESET_DEFAULT];
|
||||||
return err;
|
if (!reset_default) {
|
||||||
if (param->validate) {
|
err = devlink_param_value_get_from_info(param, info, &value);
|
||||||
err = param->validate(devlink, param->id, value, info->extack);
|
|
||||||
if (err)
|
if (err)
|
||||||
return 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))
|
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;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) {
|
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 = value;
|
||||||
param_item->driverinit_value_new_valid = true;
|
param_item->driverinit_value_new_valid = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -576,7 +651,12 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
ctx.val = value;
|
ctx.val = value;
|
||||||
ctx.cmode = cmode;
|
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)
|
if (err)
|
||||||
return 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 = init_val;
|
||||||
param_item->driverinit_value_valid = true;
|
param_item->driverinit_value_valid = true;
|
||||||
|
param_item->driverinit_default = init_val;
|
||||||
|
|
||||||
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
|
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user