mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 08:24:47 +01:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
950 lines
26 KiB
C
950 lines
26 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,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC,
|
|
.name = DEVLINK_PARAM_GENERIC_ENABLE_PHC_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_ENABLE_PHC_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
|
|
.name = DEVLINK_PARAM_GENERIC_CLOCK_ID_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_CLOCK_ID_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_TOTAL_VFS,
|
|
.name = DEVLINK_PARAM_GENERIC_TOTAL_VFS_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_TOTAL_VFS_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_NUM_DOORBELLS,
|
|
.name = DEVLINK_PARAM_GENERIC_NUM_DOORBELLS_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_NUM_DOORBELLS_TYPE,
|
|
},
|
|
{
|
|
.id = DEVLINK_PARAM_GENERIC_ID_MAX_MAC_PER_VF,
|
|
.name = DEVLINK_PARAM_GENERIC_MAX_MAC_PER_VF_NAME,
|
|
.type = DEVLINK_PARAM_GENERIC_MAX_MAC_PER_VF_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,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
if (!param->get)
|
|
return -EOPNOTSUPP;
|
|
return param->get(devlink, param->id, ctx, extack);
|
|
}
|
|
|
|
static int devlink_param_set(struct devlink *devlink,
|
|
const struct devlink_param *param,
|
|
struct devlink_param_gset_ctx *ctx,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
if (!param->set)
|
|
return -EOPNOTSUPP;
|
|
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,
|
|
bool flag_as_u8)
|
|
{
|
|
switch (type) {
|
|
case DEVLINK_PARAM_TYPE_U8:
|
|
if (nla_put_u8(msg, nla_type, val.vu8))
|
|
return -EMSGSIZE;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_U16:
|
|
if (nla_put_u16(msg, nla_type, val.vu16))
|
|
return -EMSGSIZE;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_U32:
|
|
if (nla_put_u32(msg, nla_type, val.vu32))
|
|
return -EMSGSIZE;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_U64:
|
|
if (devlink_nl_put_u64(msg, nla_type, val.vu64))
|
|
return -EMSGSIZE;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_STRING:
|
|
if (nla_put_string(msg, nla_type, val.vstr))
|
|
return -EMSGSIZE;
|
|
break;
|
|
case DEVLINK_PARAM_TYPE_BOOL:
|
|
/* 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;
|
|
}
|
|
|
|
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 default_val,
|
|
bool has_default)
|
|
{
|
|
struct nlattr *param_value_attr;
|
|
int err = -EMSGSIZE;
|
|
|
|
param_value_attr = nla_nest_start_noflag(msg,
|
|
DEVLINK_ATTR_PARAM_VALUE);
|
|
if (!param_value_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_CMODE, cmode))
|
|
goto value_nest_cancel;
|
|
|
|
err = devlink_nl_param_value_put(msg, type,
|
|
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;
|
|
|
|
value_nest_cancel:
|
|
nla_nest_cancel(msg, param_value_attr);
|
|
return err;
|
|
}
|
|
|
|
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,
|
|
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;
|
|
struct nlattr *param_values_list;
|
|
struct nlattr *param_attr;
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_TYPE, param->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],
|
|
default_value[i],
|
|
default_value_set[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) || !devlink_nl_notify_need(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, NULL);
|
|
if (err) {
|
|
nlmsg_free(msg);
|
|
return;
|
|
}
|
|
|
|
devlink_nl_notify_send(devlink, msg);
|
|
}
|
|
|
|
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,
|
|
cb->extack);
|
|
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;
|
|
|
|
*param_type = nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_TYPE]);
|
|
|
|
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_U64:
|
|
if (nla_len(param_data) != sizeof(u64))
|
|
return -EINVAL;
|
|
value->vu64 = nla_get_u64(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, info->extack);
|
|
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;
|
|
bool reset_default;
|
|
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;
|
|
|
|
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))
|
|
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) {
|
|
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 {
|
|
if (!param->set)
|
|
return -EOPNOTSUPP;
|
|
ctx.val = value;
|
|
ctx.cmode = cmode;
|
|
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;
|
|
}
|
|
|
|
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_obj(*param_item);
|
|
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;
|
|
param_item->driverinit_default = init_val;
|
|
|
|
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);
|