net: devlink: Add support for port regions

Allow regions to be registered to a devlink port. The same netlink API
is used, but the port index is provided to indicate when a region is a
port region as opposed to a device region.

Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Tested-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Andrew Lunn 2020-10-04 18:12:54 +02:00 коммит произвёл David S. Miller
Родитель 3122433eb5
Коммит 544e7c33ec
2 изменённых файлов: 251 добавлений и 26 удалений

Просмотреть файл

@ -110,6 +110,7 @@ struct devlink_port_attrs {
struct devlink_port { struct devlink_port {
struct list_head list; struct list_head list;
struct list_head param_list; struct list_head param_list;
struct list_head region_list;
struct devlink *devlink; struct devlink *devlink;
unsigned int index; unsigned int index;
bool registered; bool registered;
@ -591,6 +592,26 @@ struct devlink_region_ops {
void *priv; void *priv;
}; };
/**
* struct devlink_port_region_ops - Region operations for a port
* @name: region name
* @destructor: callback used to free snapshot memory when deleting
* @snapshot: callback to request an immediate snapshot. On success,
* the data variable must be updated to point to the snapshot data.
* The function will be called while the devlink instance lock is
* held.
* @priv: Pointer to driver private data for the region operation
*/
struct devlink_port_region_ops {
const char *name;
void (*destructor)(const void *data);
int (*snapshot)(struct devlink_port *port,
const struct devlink_port_region_ops *ops,
struct netlink_ext_ack *extack,
u8 **data);
void *priv;
};
struct devlink_fmsg; struct devlink_fmsg;
struct devlink_health_reporter; struct devlink_health_reporter;
@ -1445,7 +1466,13 @@ struct devlink_region *
devlink_region_create(struct devlink *devlink, devlink_region_create(struct devlink *devlink,
const struct devlink_region_ops *ops, const struct devlink_region_ops *ops,
u32 region_max_snapshots, u64 region_size); u32 region_max_snapshots, u64 region_size);
struct devlink_region *
devlink_port_region_create(struct devlink_port *port,
const struct devlink_port_region_ops *ops,
u32 region_max_snapshots, u64 region_size);
void devlink_region_destroy(struct devlink_region *region); void devlink_region_destroy(struct devlink_region *region);
void devlink_port_region_destroy(struct devlink_region *region);
int devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id); int devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id);
void devlink_region_snapshot_id_put(struct devlink *devlink, u32 id); void devlink_region_snapshot_id_put(struct devlink *devlink, u32 id);
int devlink_region_snapshot_create(struct devlink_region *region, int devlink_region_snapshot_create(struct devlink_region *region,

Просмотреть файл

@ -347,8 +347,12 @@ devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb,
struct devlink_region { struct devlink_region {
struct devlink *devlink; struct devlink *devlink;
struct devlink_port *port;
struct list_head list; struct list_head list;
const struct devlink_region_ops *ops; union {
const struct devlink_region_ops *ops;
const struct devlink_port_region_ops *port_ops;
};
struct list_head snapshot_list; struct list_head snapshot_list;
u32 max_snapshots; u32 max_snapshots;
u32 cur_snapshots; u32 cur_snapshots;
@ -374,6 +378,19 @@ devlink_region_get_by_name(struct devlink *devlink, const char *region_name)
return NULL; return NULL;
} }
static struct devlink_region *
devlink_port_region_get_by_name(struct devlink_port *port,
const char *region_name)
{
struct devlink_region *region;
list_for_each_entry(region, &port->region_list, list)
if (!strcmp(region->ops->name, region_name))
return region;
return NULL;
}
static struct devlink_snapshot * static struct devlink_snapshot *
devlink_region_snapshot_get_by_id(struct devlink_region *region, u32 id) devlink_region_snapshot_get_by_id(struct devlink_region *region, u32 id)
{ {
@ -3926,6 +3943,11 @@ static int devlink_nl_region_fill(struct sk_buff *msg, struct devlink *devlink,
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
if (region->port)
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,
region->port->index))
goto nla_put_failure;
err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, region->ops->name); err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, region->ops->name);
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
@ -3973,6 +3995,11 @@ devlink_nl_region_notify_build(struct devlink_region *region,
if (err) if (err)
goto out_cancel_msg; goto out_cancel_msg;
if (region->port)
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,
region->port->index))
goto out_cancel_msg;
err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME,
region->ops->name); region->ops->name);
if (err) if (err)
@ -4219,16 +4246,30 @@ static int devlink_nl_cmd_region_get_doit(struct sk_buff *skb,
struct genl_info *info) struct genl_info *info)
{ {
struct devlink *devlink = info->user_ptr[0]; struct devlink *devlink = info->user_ptr[0];
struct devlink_port *port = NULL;
struct devlink_region *region; struct devlink_region *region;
const char *region_name; const char *region_name;
struct sk_buff *msg; struct sk_buff *msg;
unsigned int index;
int err; int err;
if (!info->attrs[DEVLINK_ATTR_REGION_NAME]) if (!info->attrs[DEVLINK_ATTR_REGION_NAME])
return -EINVAL; return -EINVAL;
if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
port = devlink_port_get_by_index(devlink, index);
if (!port)
return -ENODEV;
}
region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]); region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]);
region = devlink_region_get_by_name(devlink, region_name); if (port)
region = devlink_port_region_get_by_name(port, region_name);
else
region = devlink_region_get_by_name(devlink, region_name);
if (!region) if (!region)
return -EINVAL; return -EINVAL;
@ -4247,10 +4288,75 @@ static int devlink_nl_cmd_region_get_doit(struct sk_buff *skb,
return genlmsg_reply(msg, info); return genlmsg_reply(msg, info);
} }
static int devlink_nl_cmd_region_get_port_dumpit(struct sk_buff *msg,
struct netlink_callback *cb,
struct devlink_port *port,
int *idx,
int start)
{
struct devlink_region *region;
int err = 0;
list_for_each_entry(region, &port->region_list, list) {
if (*idx < start) {
(*idx)++;
continue;
}
err = devlink_nl_region_fill(msg, port->devlink,
DEVLINK_CMD_REGION_GET,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI, region);
if (err)
goto out;
(*idx)++;
}
out:
return err;
}
static int devlink_nl_cmd_region_get_devlink_dumpit(struct sk_buff *msg,
struct netlink_callback *cb,
struct devlink *devlink,
int *idx,
int start)
{
struct devlink_region *region;
struct devlink_port *port;
int err = 0;
mutex_lock(&devlink->lock);
list_for_each_entry(region, &devlink->region_list, list) {
if (*idx < start) {
(*idx)++;
continue;
}
err = devlink_nl_region_fill(msg, devlink,
DEVLINK_CMD_REGION_GET,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI, region);
if (err)
goto out;
(*idx)++;
}
list_for_each_entry(port, &devlink->port_list, list) {
err = devlink_nl_cmd_region_get_port_dumpit(msg, cb, port, idx,
start);
if (err)
goto out;
}
out:
mutex_unlock(&devlink->lock);
return err;
}
static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg, static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg,
struct netlink_callback *cb) struct netlink_callback *cb)
{ {
struct devlink_region *region;
struct devlink *devlink; struct devlink *devlink;
int start = cb->args[0]; int start = cb->args[0];
int idx = 0; int idx = 0;
@ -4260,25 +4366,10 @@ static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg,
list_for_each_entry(devlink, &devlink_list, list) { list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
continue; continue;
err = devlink_nl_cmd_region_get_devlink_dumpit(msg, cb, devlink,
mutex_lock(&devlink->lock); &idx, start);
list_for_each_entry(region, &devlink->region_list, list) { if (err)
if (idx < start) { goto out;
idx++;
continue;
}
err = devlink_nl_region_fill(msg, devlink,
DEVLINK_CMD_REGION_GET,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI, region);
if (err) {
mutex_unlock(&devlink->lock);
goto out;
}
idx++;
}
mutex_unlock(&devlink->lock);
} }
out: out:
mutex_unlock(&devlink_mutex); mutex_unlock(&devlink_mutex);
@ -4291,8 +4382,10 @@ static int devlink_nl_cmd_region_del(struct sk_buff *skb,
{ {
struct devlink *devlink = info->user_ptr[0]; struct devlink *devlink = info->user_ptr[0];
struct devlink_snapshot *snapshot; struct devlink_snapshot *snapshot;
struct devlink_port *port = NULL;
struct devlink_region *region; struct devlink_region *region;
const char *region_name; const char *region_name;
unsigned int index;
u32 snapshot_id; u32 snapshot_id;
if (!info->attrs[DEVLINK_ATTR_REGION_NAME] || if (!info->attrs[DEVLINK_ATTR_REGION_NAME] ||
@ -4302,7 +4395,19 @@ static int devlink_nl_cmd_region_del(struct sk_buff *skb,
region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]); region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]);
snapshot_id = nla_get_u32(info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]); snapshot_id = nla_get_u32(info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]);
region = devlink_region_get_by_name(devlink, region_name); if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
port = devlink_port_get_by_index(devlink, index);
if (!port)
return -ENODEV;
}
if (port)
region = devlink_port_region_get_by_name(port, region_name);
else
region = devlink_region_get_by_name(devlink, region_name);
if (!region) if (!region)
return -EINVAL; return -EINVAL;
@ -4319,9 +4424,11 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
{ {
struct devlink *devlink = info->user_ptr[0]; struct devlink *devlink = info->user_ptr[0];
struct devlink_snapshot *snapshot; struct devlink_snapshot *snapshot;
struct devlink_port *port = NULL;
struct nlattr *snapshot_id_attr; struct nlattr *snapshot_id_attr;
struct devlink_region *region; struct devlink_region *region;
const char *region_name; const char *region_name;
unsigned int index;
u32 snapshot_id; u32 snapshot_id;
u8 *data; u8 *data;
int err; int err;
@ -4332,7 +4439,20 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
} }
region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]); region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]);
region = devlink_region_get_by_name(devlink, region_name);
if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
port = devlink_port_get_by_index(devlink, index);
if (!port)
return -ENODEV;
}
if (port)
region = devlink_port_region_get_by_name(port, region_name);
else
region = devlink_region_get_by_name(devlink, region_name);
if (!region) { if (!region) {
NL_SET_ERR_MSG_MOD(info->extack, "The requested region does not exist"); NL_SET_ERR_MSG_MOD(info->extack, "The requested region does not exist");
return -EINVAL; return -EINVAL;
@ -4368,7 +4488,12 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
} }
} }
err = region->ops->snapshot(devlink, region->ops, info->extack, &data); if (port)
err = region->port_ops->snapshot(port, region->port_ops,
info->extack, &data);
else
err = region->ops->snapshot(devlink, region->ops,
info->extack, &data);
if (err) if (err)
goto err_snapshot_capture; goto err_snapshot_capture;
@ -4490,10 +4615,12 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
const struct genl_dumpit_info *info = genl_dumpit_info(cb); const struct genl_dumpit_info *info = genl_dumpit_info(cb);
u64 ret_offset, start_offset, end_offset = U64_MAX; u64 ret_offset, start_offset, end_offset = U64_MAX;
struct nlattr **attrs = info->attrs; struct nlattr **attrs = info->attrs;
struct devlink_port *port = NULL;
struct devlink_region *region; struct devlink_region *region;
struct nlattr *chunks_attr; struct nlattr *chunks_attr;
const char *region_name; const char *region_name;
struct devlink *devlink; struct devlink *devlink;
unsigned int index;
void *hdr; void *hdr;
int err; int err;
@ -4514,8 +4641,21 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
goto out_unlock; goto out_unlock;
} }
if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
port = devlink_port_get_by_index(devlink, index);
if (!port)
return -ENODEV;
}
region_name = nla_data(attrs[DEVLINK_ATTR_REGION_NAME]); region_name = nla_data(attrs[DEVLINK_ATTR_REGION_NAME]);
region = devlink_region_get_by_name(devlink, region_name);
if (port)
region = devlink_port_region_get_by_name(port, region_name);
else
region = devlink_region_get_by_name(devlink, region_name);
if (!region) { if (!region) {
err = -EINVAL; err = -EINVAL;
goto out_unlock; goto out_unlock;
@ -4552,6 +4692,11 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
if (region->port)
if (nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX,
region->port->index))
goto nla_put_failure;
err = nla_put_string(skb, DEVLINK_ATTR_REGION_NAME, region_name); err = nla_put_string(skb, DEVLINK_ATTR_REGION_NAME, region_name);
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
@ -7666,6 +7811,7 @@ int devlink_port_register(struct devlink *devlink,
mutex_init(&devlink_port->reporters_lock); mutex_init(&devlink_port->reporters_lock);
list_add_tail(&devlink_port->list, &devlink->port_list); list_add_tail(&devlink_port->list, &devlink->port_list);
INIT_LIST_HEAD(&devlink_port->param_list); INIT_LIST_HEAD(&devlink_port->param_list);
INIT_LIST_HEAD(&devlink_port->region_list);
mutex_unlock(&devlink->lock); mutex_unlock(&devlink->lock);
INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn); INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
devlink_port_type_warn_schedule(devlink_port); devlink_port_type_warn_schedule(devlink_port);
@ -7689,6 +7835,7 @@ void devlink_port_unregister(struct devlink_port *devlink_port)
list_del(&devlink_port->list); list_del(&devlink_port->list);
mutex_unlock(&devlink->lock); mutex_unlock(&devlink->lock);
WARN_ON(!list_empty(&devlink_port->reporter_list)); WARN_ON(!list_empty(&devlink_port->reporter_list));
WARN_ON(!list_empty(&devlink_port->region_list));
mutex_destroy(&devlink_port->reporters_lock); mutex_destroy(&devlink_port->reporters_lock);
} }
EXPORT_SYMBOL_GPL(devlink_port_unregister); EXPORT_SYMBOL_GPL(devlink_port_unregister);
@ -8768,6 +8915,57 @@ unlock:
} }
EXPORT_SYMBOL_GPL(devlink_region_create); EXPORT_SYMBOL_GPL(devlink_region_create);
/**
* devlink_port_region_create - create a new address region for a port
*
* @port: devlink port
* @ops: region operations and name
* @region_max_snapshots: Maximum supported number of snapshots for region
* @region_size: size of region
*/
struct devlink_region *
devlink_port_region_create(struct devlink_port *port,
const struct devlink_port_region_ops *ops,
u32 region_max_snapshots, u64 region_size)
{
struct devlink *devlink = port->devlink;
struct devlink_region *region;
int err = 0;
if (WARN_ON(!ops) || WARN_ON(!ops->destructor))
return ERR_PTR(-EINVAL);
mutex_lock(&devlink->lock);
if (devlink_port_region_get_by_name(port, ops->name)) {
err = -EEXIST;
goto unlock;
}
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region) {
err = -ENOMEM;
goto unlock;
}
region->devlink = devlink;
region->port = port;
region->max_snapshots = region_max_snapshots;
region->port_ops = ops;
region->size = region_size;
INIT_LIST_HEAD(&region->snapshot_list);
list_add_tail(&region->list, &port->region_list);
devlink_nl_region_notify(region, NULL, DEVLINK_CMD_REGION_NEW);
mutex_unlock(&devlink->lock);
return region;
unlock:
mutex_unlock(&devlink->lock);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(devlink_port_region_create);
/** /**
* devlink_region_destroy - destroy address region * devlink_region_destroy - destroy address region
* *