Commit 238052e0 authored by David S. Miller's avatar David S. Miller

Merge branch 'devlink-params-cleanup'

Jiri Pirko says:

====================
devlink: params cleanups and devl_param_driverinit_value_get() fix

The primary motivation of this patchset is the patch #6, which fixes an
issue introduced by 075935f0 ("devlink: protect devlink param list
by instance lock") and reported by Kim Phillips <kim.phillips@amd.com>
(https://lore.kernel.org/netdev/719de4f0-76ac-e8b9-38a9-167ae239efc7@amd.com/)
and my colleagues doing mlx5 driver regression testing.

The basis idea is that devl_param_driverinit_value_get() could be
possible to the called without holding devlink intance lock in
most of the cases (all existing ones in the current codebase),
which would fix some mlx5 flows where the lock is not held.

To achieve that, make sure that the param value does not change between
reloads with patch #2.

Also, convert the param list to xarray which removes the worry about
list_head consistency when doing lockless lookup.

The rest of the patches are doing some small related cleanup of things
that poke me in the eye during the work.

---
v1->v2:
- a small bug was fixed in patch #2, the rest of the code stays the same
  so I left review/ack tags attached to them
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 170677fe 6b4bfa43
......@@ -489,6 +489,10 @@ struct devlink_param_item {
const struct devlink_param *param;
union devlink_param_value driverinit_value;
bool driverinit_value_valid;
union devlink_param_value driverinit_value_new; /* Not reachable
* until reload.
*/
bool driverinit_value_new_valid;
};
enum devlink_param_generic_id {
......@@ -1780,7 +1784,7 @@ void devlink_params_unregister(struct devlink *devlink,
const struct devlink_param *params,
size_t params_count);
int devl_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
union devlink_param_value *init_val);
union devlink_param_value *val);
void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
union devlink_param_value init_val);
void devl_param_value_changed(struct devlink *devlink, u32 param_id);
......
......@@ -212,6 +212,7 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops,
devlink->dev = dev;
devlink->ops = ops;
xa_init_flags(&devlink->ports, XA_FLAGS_ALLOC);
xa_init_flags(&devlink->params, XA_FLAGS_ALLOC);
xa_init_flags(&devlink->snapshot_ids, XA_FLAGS_ALLOC);
write_pnet(&devlink->_net, net);
INIT_LIST_HEAD(&devlink->rate_list);
......@@ -219,7 +220,6 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops,
INIT_LIST_HEAD(&devlink->sb_list);
INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list);
INIT_LIST_HEAD(&devlink->resource_list);
INIT_LIST_HEAD(&devlink->param_list);
INIT_LIST_HEAD(&devlink->region_list);
INIT_LIST_HEAD(&devlink->reporter_list);
INIT_LIST_HEAD(&devlink->trap_list);
......@@ -255,7 +255,6 @@ void devlink_free(struct devlink *devlink)
WARN_ON(!list_empty(&devlink->trap_list));
WARN_ON(!list_empty(&devlink->reporter_list));
WARN_ON(!list_empty(&devlink->region_list));
WARN_ON(!list_empty(&devlink->param_list));
WARN_ON(!list_empty(&devlink->resource_list));
WARN_ON(!list_empty(&devlink->dpipe_table_list));
WARN_ON(!list_empty(&devlink->sb_list));
......@@ -264,6 +263,7 @@ void devlink_free(struct devlink *devlink)
WARN_ON(!xa_empty(&devlink->ports));
xa_destroy(&devlink->snapshot_ids);
xa_destroy(&devlink->params);
xa_destroy(&devlink->ports);
WARN_ON_ONCE(unregister_netdevice_notifier(&devlink->netdevice_nb));
......
......@@ -369,6 +369,9 @@ int devlink_reload(struct devlink *devlink, struct net *dest_net,
if (dest_net && !net_eq(dest_net, curr_net))
devlink_reload_netns_change(devlink, curr_net, dest_net);
if (action == DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
devlink_params_driverinit_load_new(devlink);
err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack);
devlink_reload_failed_set(devlink, !!err);
if (err)
......
......@@ -29,7 +29,7 @@ struct devlink {
struct list_head sb_list;
struct list_head dpipe_table_list;
struct list_head resource_list;
struct list_head param_list;
struct xarray params;
struct list_head region_list;
struct list_head reporter_list;
struct devlink_dpipe_headers *dpipe_headers;
......@@ -189,6 +189,9 @@ static inline bool devlink_reload_supported(const struct devlink_ops *ops)
return ops->reload_down && ops->reload_up;
}
/* Params */
void devlink_params_driverinit_load_new(struct devlink *devlink);
/* Resources */
struct devlink_resource;
int devlink_resources_validate(struct devlink *devlink,
......
......@@ -1110,24 +1110,18 @@ devlink_nl_cmd_port_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_port *devlink_port;
unsigned long port_index;
int idx = 0;
int err = 0;
xa_for_each(&devlink->ports, port_index, devlink_port) {
if (idx < state->idx) {
idx++;
continue;
}
xa_for_each_start(&devlink->ports, port_index, devlink_port, state->idx) {
err = devlink_nl_port_fill(msg, devlink_port,
DEVLINK_CMD_NEW,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI, cb->extack);
if (err) {
state->idx = idx;
state->idx = port_index;
break;
}
idx++;
}
return err;
......@@ -3959,26 +3953,22 @@ static int devlink_param_driver_verify(const struct devlink_param *param)
}
static struct devlink_param_item *
devlink_param_find_by_name(struct list_head *param_list,
const char *param_name)
devlink_param_find_by_name(struct xarray *params, const char *param_name)
{
struct devlink_param_item *param_item;
unsigned long param_id;
list_for_each_entry(param_item, param_list, list)
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 list_head *param_list, u32 param_id)
devlink_param_find_by_id(struct xarray *params, u32 param_id)
{
struct devlink_param_item *param_item;
list_for_each_entry(param_item, param_list, list)
if (param_item->param->id == param_id)
return param_item;
return NULL;
return xa_load(params, param_id);
}
static bool
......@@ -4097,9 +4087,12 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
if (!devlink_param_cmode_is_supported(param, i))
continue;
if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) {
if (!param_item->driverinit_value_valid)
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;
param_value[i] = param_item->driverinit_value;
} else {
ctx.cmode = i;
err = devlink_param_get(devlink, param, &ctx);
......@@ -4204,14 +4197,10 @@ devlink_nl_cmd_param_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_param_item *param_item;
int idx = 0;
unsigned long param_id;
int err = 0;
list_for_each_entry(param_item, &devlink->param_list, list) {
if (idx < state->idx) {
idx++;
continue;
}
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,
......@@ -4220,10 +4209,9 @@ devlink_nl_cmd_param_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
if (err == -EOPNOTSUPP) {
err = 0;
} else if (err) {
state->idx = idx;
state->idx = param_id;
break;
}
idx++;
}
return err;
......@@ -4309,8 +4297,7 @@ devlink_param_value_get_from_info(const struct devlink_param *param,
}
static struct devlink_param_item *
devlink_param_get_from_info(struct list_head *param_list,
struct genl_info *info)
devlink_param_get_from_info(struct xarray *params, struct genl_info *info)
{
char *param_name;
......@@ -4318,7 +4305,7 @@ devlink_param_get_from_info(struct list_head *param_list,
return NULL;
param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]);
return devlink_param_find_by_name(param_list, param_name);
return devlink_param_find_by_name(params, param_name);
}
static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
......@@ -4329,7 +4316,7 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
struct sk_buff *msg;
int err;
param_item = devlink_param_get_from_info(&devlink->param_list, info);
param_item = devlink_param_get_from_info(&devlink->params, info);
if (!param_item)
return -EINVAL;
......@@ -4350,7 +4337,7 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
unsigned int port_index,
struct list_head *param_list,
struct xarray *params,
struct genl_info *info,
enum devlink_command cmd)
{
......@@ -4362,7 +4349,7 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
union devlink_param_value value;
int err = 0;
param_item = devlink_param_get_from_info(param_list, info);
param_item = devlink_param_get_from_info(params, info);
if (!param_item)
return -EINVAL;
param = param_item->param;
......@@ -4387,11 +4374,8 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
return -EOPNOTSUPP;
if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) {
if (param->type == DEVLINK_PARAM_TYPE_STRING)
strcpy(param_item->driverinit_value.vstr, value.vstr);
else
param_item->driverinit_value = value;
param_item->driverinit_value_valid = true;
param_item->driverinit_value_new = value;
param_item->driverinit_value_new_valid = true;
} else {
if (!param->set)
return -EOPNOTSUPP;
......@@ -4411,7 +4395,7 @@ static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
{
struct devlink *devlink = info->user_ptr[0];
return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->param_list,
return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->params,
info, DEVLINK_CMD_PARAM_NEW);
}
......@@ -8043,6 +8027,7 @@ void devlink_notify_register(struct devlink *devlink)
struct devlink_rate *rate_node;
struct devlink_region *region;
unsigned long port_index;
unsigned long param_id;
devlink_notify(devlink, DEVLINK_CMD_NEW);
list_for_each_entry(linecard, &devlink->linecard_list, list)
......@@ -8068,7 +8053,7 @@ void devlink_notify_register(struct devlink *devlink)
list_for_each_entry(region, &devlink->region_list, list)
devlink_nl_region_notify(region, NULL, DEVLINK_CMD_REGION_NEW);
list_for_each_entry(param_item, &devlink->param_list, list)
xa_for_each(&devlink->params, param_id, param_item)
devlink_param_notify(devlink, 0, param_item,
DEVLINK_CMD_PARAM_NEW);
}
......@@ -8083,8 +8068,9 @@ void devlink_notify_unregister(struct devlink *devlink)
struct devlink_rate *rate_node;
struct devlink_region *region;
unsigned long port_index;
unsigned long param_id;
list_for_each_entry_reverse(param_item, &devlink->param_list, list)
xa_for_each(&devlink->params, param_id, param_item)
devlink_param_notify(devlink, 0, param_item,
DEVLINK_CMD_PARAM_DEL);
......@@ -9511,9 +9497,10 @@ 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->param_list, param->name));
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);
......@@ -9526,9 +9513,16 @@ static int devlink_param_register(struct devlink *devlink,
param_item->param = param;
list_add_tail(&param_item->list, &devlink->param_list);
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,
......@@ -9536,12 +9530,11 @@ static void devlink_param_unregister(struct devlink *devlink,
{
struct devlink_param_item *param_item;
param_item =
devlink_param_find_by_name(&devlink->param_list, param->name);
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);
list_del(&param_item->list);
xa_erase(&devlink->params, param->id);
kfree(param_item);
}
......@@ -9629,22 +9622,32 @@ EXPORT_SYMBOL_GPL(devlink_params_unregister);
*
* @devlink: devlink
* @param_id: parameter ID
* @init_val: value of parameter in driverinit configuration mode
* @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 *init_val)
union devlink_param_value *val)
{
struct devlink_param_item *param_item;
lockdep_assert_held(&devlink->lock);
if (WARN_ON(!devlink_reload_supported(devlink->ops)))
return -EOPNOTSUPP;
param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
param_item = devlink_param_find_by_id(&devlink->params, param_id);
if (!param_item)
return -EINVAL;
......@@ -9655,10 +9658,7 @@ int devl_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
DEVLINK_PARAM_CMODE_DRIVERINIT)))
return -EOPNOTSUPP;
if (param_item->param->type == DEVLINK_PARAM_TYPE_STRING)
strcpy(init_val->vstr, param_item->driverinit_value.vstr);
else
*init_val = param_item->driverinit_value;
*val = param_item->driverinit_value;
return 0;
}
......@@ -9681,7 +9681,9 @@ void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
{
struct devlink_param_item *param_item;
param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
devl_assert_locked(devlink);
param_item = devlink_param_find_by_id(&devlink->params, param_id);
if (WARN_ON(!param_item))
return;
......@@ -9689,16 +9691,29 @@ void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
DEVLINK_PARAM_CMODE_DRIVERINIT)))
return;
if (param_item->param->type == DEVLINK_PARAM_TYPE_STRING)
strcpy(param_item->driverinit_value.vstr, init_val.vstr);
else
param_item->driverinit_value = init_val;
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
......@@ -9715,7 +9730,7 @@ 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->param_list, param_id);
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment