mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 13:14:07 +08:00
53590934ba
All remaining doit and dumpit netlink callback functions are going to be used by generated split ops. They expect certain name format. Rename the callback to be aligned with generated names. Signed-off-by: Jiri Pirko <jiri@nvidia.com> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Link: https://lore.kernel.org/r/20231021112711.660606-8-jiri@resnulli.us Signed-off-by: Jakub Kicinski <kuba@kernel.org>
866 lines
23 KiB
C
866 lines
23 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
|
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
|
|
*/
|
|
|
|
#include "devl_internal.h"
|
|
|
|
static const struct devlink_param devlink_param_generic[] = {
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
|
|
.name = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
|
|
.name = DEVLINK_PARAM_GENERIC_MAX_MACS_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_MAX_MACS_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV,
|
|
.name = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT,
|
|
.name = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI,
|
|
.name = DEVLINK_PARAM_GENERIC_IGNORE_ARI_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_IGNORE_ARI_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
|
|
.name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
|
|
.name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
|
|
.name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE,
|
|
.name = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE,
|
|
.name = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_REMOTE_DEV_RESET,
|
|
.name = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH,
|
|
.name = DEVLINK_PARAM_GENERIC_ENABLE_ETH_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_ENABLE_ETH_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,
|
|
.name = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,
|
|
.name = DEVLINK_PARAM_GENERIC_ENABLE_VNET_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_ENABLE_VNET_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_IWARP,
|
|
.name = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
|
|
.name = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
|
|
.name = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_TYPE,
|
|
},
|
|
};
|
|
|
|
static int devlink_param_generic_verify(const struct devlink_param *param)
|
|
{
|
|
/* verify it match generic parameter by id and name */
|
|
if (param->id > DEVLINK_PARAM_GENERIC_ID_MAX)
|
|
return -EINVAL;
|
|
if (strcmp(param->name, devlink_param_generic[param->id].name))
|
|
return -ENOENT;
|
|
|
|
WARN_ON(param->type != devlink_param_generic[param->id].type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_param_driver_verify(const struct devlink_param *param)
|
|
{
|
|
int i;
|
|
|
|
if (param->id <= DEVLINK_PARAM_GENERIC_ID_MAX)
|
|
return -EINVAL;
|
|
/* verify no such name in generic params */
|
|
for (i = 0; i <= DEVLINK_PARAM_GENERIC_ID_MAX; i++)
|
|
if (!strcmp(param->name, devlink_param_generic[i].name))
|
|
return -EEXIST;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct devlink_param_item *
|
|
devlink_param_find_by_name(struct xarray *params, const char *param_name)
|
|
{
|
|
struct devlink_param_item *param_item;
|
|
unsigned long param_id;
|
|
|
|
xa_for_each(params, param_id, param_item) {
|
|
if (!strcmp(param_item->param->name, param_name))
|
|
return param_item;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct devlink_param_item *
|
|
devlink_param_find_by_id(struct xarray *params, u32 param_id)
|
|
{
|
|
return xa_load(params, param_id);
|
|
}
|
|
|
|
static bool
|
|
devlink_param_cmode_is_supported(const struct devlink_param *param,
|
|
enum devlink_param_cmode cmode)
|
|
{
|
|
return test_bit(cmode, ¶m->supported_cmodes);
|
|
}
|
|
|
|
static int devlink_param_get(struct devlink *devlink,
|
|
const struct devlink_param *param,
|
|
struct devlink_param_gset_ctx *ctx)
|
|
{
|
|
if (!param->get)
|
|
return -EOPNOTSUPP;
|
|
return param->get(devlink, param->id, ctx);
|
|
}
|
|
|
|
static int devlink_param_set(struct devlink *devlink,
|
|
const struct devlink_param *param,
|
|
struct devlink_param_gset_ctx *ctx)
|
|
{
|
|
if (!param->set)
|
|
return -EOPNOTSUPP;
|
|
return param->set(devlink, param->id, ctx);
|
|
}
|
|
|
|
static int
|
|
devlink_param_type_to_nla_type(enum devlink_param_type param_type)
|
|
{
|
|
switch (param_type) {
|
|
case DEVLINK_PARAM_TYPE_U8:
|
|
return NLA_U8;
|
|
case DEVLINK_PARAM_TYPE_U16:
|
|
return NLA_U16;
|
|
case DEVLINK_PARAM_TYPE_U32:
|
|
return NLA_U32;
|
|
case DEVLINK_PARAM_TYPE_STRING:
|
|
return NLA_STRING;
|
|
case DEVLINK_PARAM_TYPE_BOOL:
|
|
return NLA_FLAG;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct nlattr *param_value_attr;
|
|
|
|
param_value_attr = nla_nest_start_noflag(msg,
|
|
DEVLINK_ATTR_PARAM_VALUE);
|
|
if (!param_value_attr)
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_CMODE, cmode))
|
|
goto value_nest_cancel;
|
|
|
|
switch (type) {
|
|
case DEVLINK_PARAM_TYPE_U8:
|
|
if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu8))
|
|
goto value_nest_cancel;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_U16:
|
|
if (nla_put_u16(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu16))
|
|
goto value_nest_cancel;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_U32:
|
|
if (nla_put_u32(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu32))
|
|
goto value_nest_cancel;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_STRING:
|
|
if (nla_put_string(msg, DEVLINK_ATTR_PARAM_VALUE_DATA,
|
|
val.vstr))
|
|
goto value_nest_cancel;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_BOOL:
|
|
if (val.vbool &&
|
|
nla_put_flag(msg, DEVLINK_ATTR_PARAM_VALUE_DATA))
|
|
goto value_nest_cancel;
|
|
break;
|
|
}
|
|
|
|
nla_nest_end(msg, param_value_attr);
|
|
return 0;
|
|
|
|
value_nest_cancel:
|
|
nla_nest_cancel(msg, param_value_attr);
|
|
nla_put_failure:
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
|
|
unsigned int port_index,
|
|
struct devlink_param_item *param_item,
|
|
enum devlink_command cmd,
|
|
u32 portid, u32 seq, int flags)
|
|
{
|
|
union devlink_param_value param_value[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;
|
|
struct nlattr *param_values_list;
|
|
struct nlattr *param_attr;
|
|
int nla_type;
|
|
void *hdr;
|
|
int err;
|
|
int i;
|
|
|
|
/* Get value from driver part to driverinit configuration mode */
|
|
for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
|
|
if (!devlink_param_cmode_is_supported(param, i))
|
|
continue;
|
|
if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) {
|
|
if (param_item->driverinit_value_new_valid)
|
|
param_value[i] = param_item->driverinit_value_new;
|
|
else if (param_item->driverinit_value_valid)
|
|
param_value[i] = param_item->driverinit_value;
|
|
else
|
|
return -EOPNOTSUPP;
|
|
} else {
|
|
ctx.cmode = i;
|
|
err = devlink_param_get(devlink, param, &ctx);
|
|
if (err)
|
|
return err;
|
|
param_value[i] = ctx.val;
|
|
}
|
|
param_value_set[i] = true;
|
|
}
|
|
|
|
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
|
if (!hdr)
|
|
return -EMSGSIZE;
|
|
|
|
if (devlink_nl_put_handle(msg, devlink))
|
|
goto genlmsg_cancel;
|
|
|
|
if (cmd == DEVLINK_CMD_PORT_PARAM_GET ||
|
|
cmd == DEVLINK_CMD_PORT_PARAM_NEW ||
|
|
cmd == DEVLINK_CMD_PORT_PARAM_DEL)
|
|
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, port_index))
|
|
goto genlmsg_cancel;
|
|
|
|
param_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PARAM);
|
|
if (!param_attr)
|
|
goto genlmsg_cancel;
|
|
if (nla_put_string(msg, DEVLINK_ATTR_PARAM_NAME, param->name))
|
|
goto param_nest_cancel;
|
|
if (param->generic && nla_put_flag(msg, DEVLINK_ATTR_PARAM_GENERIC))
|
|
goto param_nest_cancel;
|
|
|
|
nla_type = devlink_param_type_to_nla_type(param->type);
|
|
if (nla_type < 0)
|
|
goto param_nest_cancel;
|
|
if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_TYPE, nla_type))
|
|
goto param_nest_cancel;
|
|
|
|
param_values_list = nla_nest_start_noflag(msg,
|
|
DEVLINK_ATTR_PARAM_VALUES_LIST);
|
|
if (!param_values_list)
|
|
goto param_nest_cancel;
|
|
|
|
for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
|
|
if (!param_value_set[i])
|
|
continue;
|
|
err = devlink_nl_param_value_fill_one(msg, param->type,
|
|
i, param_value[i]);
|
|
if (err)
|
|
goto values_list_nest_cancel;
|
|
}
|
|
|
|
nla_nest_end(msg, param_values_list);
|
|
nla_nest_end(msg, param_attr);
|
|
genlmsg_end(msg, hdr);
|
|
return 0;
|
|
|
|
values_list_nest_cancel:
|
|
nla_nest_end(msg, param_values_list);
|
|
param_nest_cancel:
|
|
nla_nest_cancel(msg, param_attr);
|
|
genlmsg_cancel:
|
|
genlmsg_cancel(msg, hdr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static void devlink_param_notify(struct devlink *devlink,
|
|
unsigned int port_index,
|
|
struct devlink_param_item *param_item,
|
|
enum devlink_command cmd)
|
|
{
|
|
struct sk_buff *msg;
|
|
int err;
|
|
|
|
WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL &&
|
|
cmd != DEVLINK_CMD_PORT_PARAM_NEW &&
|
|
cmd != DEVLINK_CMD_PORT_PARAM_DEL);
|
|
|
|
/* devlink_notify_register() / devlink_notify_unregister()
|
|
* will replay the notifications if the params are added/removed
|
|
* outside of the lifetime of the instance.
|
|
*/
|
|
if (!devl_is_registered(devlink))
|
|
return;
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!msg)
|
|
return;
|
|
err = devlink_nl_param_fill(msg, devlink, port_index, param_item, cmd,
|
|
0, 0, 0);
|
|
if (err) {
|
|
nlmsg_free(msg);
|
|
return;
|
|
}
|
|
|
|
genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
|
|
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
|
|
}
|
|
|
|
static void devlink_params_notify(struct devlink *devlink,
|
|
enum devlink_command cmd)
|
|
{
|
|
struct devlink_param_item *param_item;
|
|
unsigned long param_id;
|
|
|
|
xa_for_each(&devlink->params, param_id, param_item)
|
|
devlink_param_notify(devlink, 0, param_item, cmd);
|
|
}
|
|
|
|
void devlink_params_notify_register(struct devlink *devlink)
|
|
{
|
|
devlink_params_notify(devlink, DEVLINK_CMD_PARAM_NEW);
|
|
}
|
|
|
|
void devlink_params_notify_unregister(struct devlink *devlink)
|
|
{
|
|
devlink_params_notify(devlink, DEVLINK_CMD_PARAM_DEL);
|
|
}
|
|
|
|
static int devlink_nl_param_get_dump_one(struct sk_buff *msg,
|
|
struct devlink *devlink,
|
|
struct netlink_callback *cb,
|
|
int flags)
|
|
{
|
|
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
|
struct devlink_param_item *param_item;
|
|
unsigned long param_id;
|
|
int err = 0;
|
|
|
|
xa_for_each_start(&devlink->params, param_id, param_item, state->idx) {
|
|
err = devlink_nl_param_fill(msg, devlink, 0, param_item,
|
|
DEVLINK_CMD_PARAM_GET,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq, flags);
|
|
if (err == -EOPNOTSUPP) {
|
|
err = 0;
|
|
} else if (err) {
|
|
state->idx = param_id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int devlink_nl_param_get_dumpit(struct sk_buff *skb,
|
|
struct netlink_callback *cb)
|
|
{
|
|
return devlink_nl_dumpit(skb, cb, devlink_nl_param_get_dump_one);
|
|
}
|
|
|
|
static int
|
|
devlink_param_type_get_from_info(struct genl_info *info,
|
|
enum devlink_param_type *param_type)
|
|
{
|
|
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_TYPE))
|
|
return -EINVAL;
|
|
|
|
switch (nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_TYPE])) {
|
|
case NLA_U8:
|
|
*param_type = DEVLINK_PARAM_TYPE_U8;
|
|
break;
|
|
case NLA_U16:
|
|
*param_type = DEVLINK_PARAM_TYPE_U16;
|
|
break;
|
|
case NLA_U32:
|
|
*param_type = DEVLINK_PARAM_TYPE_U32;
|
|
break;
|
|
case NLA_STRING:
|
|
*param_type = DEVLINK_PARAM_TYPE_STRING;
|
|
break;
|
|
case NLA_FLAG:
|
|
*param_type = DEVLINK_PARAM_TYPE_BOOL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
devlink_param_value_get_from_info(const struct devlink_param *param,
|
|
struct genl_info *info,
|
|
union devlink_param_value *value)
|
|
{
|
|
struct nlattr *param_data;
|
|
int len;
|
|
|
|
param_data = info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA];
|
|
|
|
if (param->type != DEVLINK_PARAM_TYPE_BOOL && !param_data)
|
|
return -EINVAL;
|
|
|
|
switch (param->type) {
|
|
case DEVLINK_PARAM_TYPE_U8:
|
|
if (nla_len(param_data) != sizeof(u8))
|
|
return -EINVAL;
|
|
value->vu8 = nla_get_u8(param_data);
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_U16:
|
|
if (nla_len(param_data) != sizeof(u16))
|
|
return -EINVAL;
|
|
value->vu16 = nla_get_u16(param_data);
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_U32:
|
|
if (nla_len(param_data) != sizeof(u32))
|
|
return -EINVAL;
|
|
value->vu32 = nla_get_u32(param_data);
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_STRING:
|
|
len = strnlen(nla_data(param_data), nla_len(param_data));
|
|
if (len == nla_len(param_data) ||
|
|
len >= __DEVLINK_PARAM_MAX_STRING_VALUE)
|
|
return -EINVAL;
|
|
strcpy(value->vstr, nla_data(param_data));
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_BOOL:
|
|
if (param_data && nla_len(param_data))
|
|
return -EINVAL;
|
|
value->vbool = nla_get_flag(param_data);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct devlink_param_item *
|
|
devlink_param_get_from_info(struct xarray *params, struct genl_info *info)
|
|
{
|
|
char *param_name;
|
|
|
|
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_NAME))
|
|
return NULL;
|
|
|
|
param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]);
|
|
return devlink_param_find_by_name(params, param_name);
|
|
}
|
|
|
|
int devlink_nl_param_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_param_item *param_item;
|
|
struct sk_buff *msg;
|
|
int err;
|
|
|
|
param_item = devlink_param_get_from_info(&devlink->params, info);
|
|
if (!param_item)
|
|
return -EINVAL;
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!msg)
|
|
return -ENOMEM;
|
|
|
|
err = devlink_nl_param_fill(msg, devlink, 0, param_item,
|
|
DEVLINK_CMD_PARAM_GET,
|
|
info->snd_portid, info->snd_seq, 0);
|
|
if (err) {
|
|
nlmsg_free(msg);
|
|
return err;
|
|
}
|
|
|
|
return genlmsg_reply(msg, info);
|
|
}
|
|
|
|
static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
|
|
unsigned int port_index,
|
|
struct xarray *params,
|
|
struct genl_info *info,
|
|
enum devlink_command cmd)
|
|
{
|
|
enum devlink_param_type param_type;
|
|
struct devlink_param_gset_ctx ctx;
|
|
enum devlink_param_cmode cmode;
|
|
struct devlink_param_item *param_item;
|
|
const struct devlink_param *param;
|
|
union devlink_param_value value;
|
|
int err = 0;
|
|
|
|
param_item = devlink_param_get_from_info(params, info);
|
|
if (!param_item)
|
|
return -EINVAL;
|
|
param = param_item->param;
|
|
err = devlink_param_type_get_from_info(info, ¶m_type);
|
|
if (err)
|
|
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);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_VALUE_CMODE))
|
|
return -EINVAL;
|
|
cmode = nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_VALUE_CMODE]);
|
|
if (!devlink_param_cmode_is_supported(param, cmode))
|
|
return -EOPNOTSUPP;
|
|
|
|
if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) {
|
|
param_item->driverinit_value_new = value;
|
|
param_item->driverinit_value_new_valid = true;
|
|
} else {
|
|
if (!param->set)
|
|
return -EOPNOTSUPP;
|
|
ctx.val = value;
|
|
ctx.cmode = cmode;
|
|
err = devlink_param_set(devlink, param, &ctx);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
devlink_param_notify(devlink, port_index, param_item, cmd);
|
|
return 0;
|
|
}
|
|
|
|
int devlink_nl_param_set_doit(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
|
|
return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->params,
|
|
info, DEVLINK_CMD_PARAM_NEW);
|
|
}
|
|
|
|
int devlink_nl_port_param_get_dumpit(struct sk_buff *msg,
|
|
struct netlink_callback *cb)
|
|
{
|
|
NL_SET_ERR_MSG(cb->extack, "Port params are not supported");
|
|
return msg->len;
|
|
}
|
|
|
|
int devlink_nl_port_param_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
NL_SET_ERR_MSG(info->extack, "Port params are not supported");
|
|
return -EINVAL;
|
|
}
|
|
|
|
int devlink_nl_port_param_set_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
NL_SET_ERR_MSG(info->extack, "Port params are not supported");
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int devlink_param_verify(const struct devlink_param *param)
|
|
{
|
|
if (!param || !param->name || !param->supported_cmodes)
|
|
return -EINVAL;
|
|
if (param->generic)
|
|
return devlink_param_generic_verify(param);
|
|
else
|
|
return devlink_param_driver_verify(param);
|
|
}
|
|
|
|
static int devlink_param_register(struct devlink *devlink,
|
|
const struct devlink_param *param)
|
|
{
|
|
struct devlink_param_item *param_item;
|
|
int err;
|
|
|
|
WARN_ON(devlink_param_verify(param));
|
|
WARN_ON(devlink_param_find_by_name(&devlink->params, param->name));
|
|
|
|
if (param->supported_cmodes == BIT(DEVLINK_PARAM_CMODE_DRIVERINIT))
|
|
WARN_ON(param->get || param->set);
|
|
else
|
|
WARN_ON(!param->get || !param->set);
|
|
|
|
param_item = kzalloc(sizeof(*param_item), GFP_KERNEL);
|
|
if (!param_item)
|
|
return -ENOMEM;
|
|
|
|
param_item->param = param;
|
|
|
|
err = xa_insert(&devlink->params, param->id, param_item, GFP_KERNEL);
|
|
if (err)
|
|
goto err_xa_insert;
|
|
|
|
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
|
|
return 0;
|
|
|
|
err_xa_insert:
|
|
kfree(param_item);
|
|
return err;
|
|
}
|
|
|
|
static void devlink_param_unregister(struct devlink *devlink,
|
|
const struct devlink_param *param)
|
|
{
|
|
struct devlink_param_item *param_item;
|
|
|
|
param_item = devlink_param_find_by_id(&devlink->params, param->id);
|
|
if (WARN_ON(!param_item))
|
|
return;
|
|
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_DEL);
|
|
xa_erase(&devlink->params, param->id);
|
|
kfree(param_item);
|
|
}
|
|
|
|
/**
|
|
* devl_params_register - register configuration parameters
|
|
*
|
|
* @devlink: devlink
|
|
* @params: configuration parameters array
|
|
* @params_count: number of parameters provided
|
|
*
|
|
* Register the configuration parameters supported by the driver.
|
|
*/
|
|
int devl_params_register(struct devlink *devlink,
|
|
const struct devlink_param *params,
|
|
size_t params_count)
|
|
{
|
|
const struct devlink_param *param = params;
|
|
int i, err;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
for (i = 0; i < params_count; i++, param++) {
|
|
err = devlink_param_register(devlink, param);
|
|
if (err)
|
|
goto rollback;
|
|
}
|
|
return 0;
|
|
|
|
rollback:
|
|
if (!i)
|
|
return err;
|
|
|
|
for (param--; i > 0; i--, param--)
|
|
devlink_param_unregister(devlink, param);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_params_register);
|
|
|
|
int devlink_params_register(struct devlink *devlink,
|
|
const struct devlink_param *params,
|
|
size_t params_count)
|
|
{
|
|
int err;
|
|
|
|
devl_lock(devlink);
|
|
err = devl_params_register(devlink, params, params_count);
|
|
devl_unlock(devlink);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_params_register);
|
|
|
|
/**
|
|
* devl_params_unregister - unregister configuration parameters
|
|
* @devlink: devlink
|
|
* @params: configuration parameters to unregister
|
|
* @params_count: number of parameters provided
|
|
*/
|
|
void devl_params_unregister(struct devlink *devlink,
|
|
const struct devlink_param *params,
|
|
size_t params_count)
|
|
{
|
|
const struct devlink_param *param = params;
|
|
int i;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
for (i = 0; i < params_count; i++, param++)
|
|
devlink_param_unregister(devlink, param);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_params_unregister);
|
|
|
|
void devlink_params_unregister(struct devlink *devlink,
|
|
const struct devlink_param *params,
|
|
size_t params_count)
|
|
{
|
|
devl_lock(devlink);
|
|
devl_params_unregister(devlink, params, params_count);
|
|
devl_unlock(devlink);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_params_unregister);
|
|
|
|
/**
|
|
* devl_param_driverinit_value_get - get configuration parameter
|
|
* value for driver initializing
|
|
*
|
|
* @devlink: devlink
|
|
* @param_id: parameter ID
|
|
* @val: pointer to store the value of parameter in driverinit
|
|
* configuration mode
|
|
*
|
|
* This function should be used by the driver to get driverinit
|
|
* configuration for initialization after reload command.
|
|
*
|
|
* Note that lockless call of this function relies on the
|
|
* driver to maintain following basic sane behavior:
|
|
* 1) Driver ensures a call to this function cannot race with
|
|
* registering/unregistering the parameter with the same parameter ID.
|
|
* 2) Driver ensures a call to this function cannot race with
|
|
* devl_param_driverinit_value_set() call with the same parameter ID.
|
|
* 3) Driver ensures a call to this function cannot race with
|
|
* reload operation.
|
|
* If the driver is not able to comply, it has to take the devlink->lock
|
|
* while calling this.
|
|
*/
|
|
int devl_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
|
|
union devlink_param_value *val)
|
|
{
|
|
struct devlink_param_item *param_item;
|
|
|
|
if (WARN_ON(!devlink_reload_supported(devlink->ops)))
|
|
return -EOPNOTSUPP;
|
|
|
|
param_item = devlink_param_find_by_id(&devlink->params, param_id);
|
|
if (!param_item)
|
|
return -EINVAL;
|
|
|
|
if (!param_item->driverinit_value_valid)
|
|
return -EOPNOTSUPP;
|
|
|
|
if (WARN_ON(!devlink_param_cmode_is_supported(param_item->param,
|
|
DEVLINK_PARAM_CMODE_DRIVERINIT)))
|
|
return -EOPNOTSUPP;
|
|
|
|
*val = param_item->driverinit_value;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_param_driverinit_value_get);
|
|
|
|
/**
|
|
* devl_param_driverinit_value_set - set value of configuration
|
|
* parameter for driverinit
|
|
* configuration mode
|
|
*
|
|
* @devlink: devlink
|
|
* @param_id: parameter ID
|
|
* @init_val: value of parameter to set for driverinit configuration mode
|
|
*
|
|
* This function should be used by the driver to set driverinit
|
|
* configuration mode default value.
|
|
*/
|
|
void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
|
|
union devlink_param_value init_val)
|
|
{
|
|
struct devlink_param_item *param_item;
|
|
|
|
devl_assert_locked(devlink);
|
|
|
|
param_item = devlink_param_find_by_id(&devlink->params, param_id);
|
|
if (WARN_ON(!param_item))
|
|
return;
|
|
|
|
if (WARN_ON(!devlink_param_cmode_is_supported(param_item->param,
|
|
DEVLINK_PARAM_CMODE_DRIVERINIT)))
|
|
return;
|
|
|
|
param_item->driverinit_value = init_val;
|
|
param_item->driverinit_value_valid = true;
|
|
|
|
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_param_driverinit_value_set);
|
|
|
|
void devlink_params_driverinit_load_new(struct devlink *devlink)
|
|
{
|
|
struct devlink_param_item *param_item;
|
|
unsigned long param_id;
|
|
|
|
xa_for_each(&devlink->params, param_id, param_item) {
|
|
if (!devlink_param_cmode_is_supported(param_item->param,
|
|
DEVLINK_PARAM_CMODE_DRIVERINIT) ||
|
|
!param_item->driverinit_value_new_valid)
|
|
continue;
|
|
param_item->driverinit_value = param_item->driverinit_value_new;
|
|
param_item->driverinit_value_valid = true;
|
|
param_item->driverinit_value_new_valid = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* devl_param_value_changed - notify devlink on a parameter's value
|
|
* change. Should be called by the driver
|
|
* right after the change.
|
|
*
|
|
* @devlink: devlink
|
|
* @param_id: parameter ID
|
|
*
|
|
* This function should be used by the driver to notify devlink on value
|
|
* change, excluding driverinit configuration mode.
|
|
* For driverinit configuration mode driver should use the function
|
|
*/
|
|
void devl_param_value_changed(struct devlink *devlink, u32 param_id)
|
|
{
|
|
struct devlink_param_item *param_item;
|
|
|
|
param_item = devlink_param_find_by_id(&devlink->params, param_id);
|
|
WARN_ON(!param_item);
|
|
|
|
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_param_value_changed);
|