aboutsummaryrefslogtreecommitdiff
path: root/net/core/devlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/core/devlink.c')
-rw-r--r--net/core/devlink.c1603
1 files changed, 1111 insertions, 492 deletions
diff --git a/net/core/devlink.c b/net/core/devlink.c
index 60248a53c0ad..4f31ddc883e7 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -81,6 +81,7 @@ struct devlink_dpipe_header devlink_dpipe_header_ipv6 = {
EXPORT_SYMBOL(devlink_dpipe_header_ipv6);
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
+EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);
static LIST_HEAD(devlink_list);
@@ -115,6 +116,8 @@ static struct devlink *devlink_get_from_attrs(struct net *net,
busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);
devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]);
+ lockdep_assert_held(&devlink_mutex);
+
list_for_each_entry(devlink, &devlink_list, list) {
if (strcmp(devlink->dev->bus->name, busname) == 0 &&
strcmp(dev_name(devlink->dev), devname) == 0 &&
@@ -932,6 +935,9 @@ static int devlink_nl_sb_pool_fill(struct sk_buff *msg, struct devlink *devlink,
if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
pool_info.threshold_type))
goto nla_put_failure;
+ if (nla_put_u32(msg, DEVLINK_ATTR_SB_POOL_CELL_SIZE,
+ pool_info.cell_size))
+ goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
@@ -2656,6 +2662,27 @@ static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
return devlink->ops->reload(devlink, info->extack);
}
+static int devlink_nl_cmd_flash_update(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct devlink *devlink = info->user_ptr[0];
+ const char *file_name, *component;
+ struct nlattr *nla_component;
+
+ if (!devlink->ops->flash_update)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME])
+ return -EINVAL;
+ file_name = nla_data(info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME]);
+
+ nla_component = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT];
+ component = nla_component ? nla_data(nla_component) : NULL;
+
+ return devlink->ops->flash_update(devlink, file_name, component,
+ info->extack);
+}
+
static const struct devlink_param devlink_param_generic[] = {
{
.id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
@@ -2843,11 +2870,13 @@ nla_put_failure:
}
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;
@@ -2866,12 +2895,15 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
return -EOPNOTSUPP;
param_value[i] = param_item->driverinit_value;
} else {
+ if (!param_item->published)
+ continue;
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);
@@ -2880,6 +2912,13 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
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(msg, DEVLINK_ATTR_PARAM);
if (!param_attr)
goto genlmsg_cancel;
@@ -2899,7 +2938,7 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
goto param_nest_cancel;
for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
- if (!devlink_param_cmode_is_supported(param, i))
+ if (!param_value_set[i])
continue;
err = devlink_nl_param_value_fill_one(msg, param->type,
i, param_value[i]);
@@ -2922,18 +2961,22 @@ genlmsg_cancel:
}
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);
+ WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL &&
+ cmd != DEVLINK_CMD_PORT_PARAM_NEW &&
+ cmd != DEVLINK_CMD_PORT_PARAM_DEL);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
- err = devlink_nl_param_fill(msg, devlink, param_item, cmd, 0, 0, 0);
+ err = devlink_nl_param_fill(msg, devlink, port_index, param_item, cmd,
+ 0, 0, 0);
if (err) {
nlmsg_free(msg);
return;
@@ -2962,7 +3005,7 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg,
idx++;
continue;
}
- err = devlink_nl_param_fill(msg, devlink, param_item,
+ err = devlink_nl_param_fill(msg, devlink, 0, param_item,
DEVLINK_CMD_PARAM_GET,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
@@ -3051,7 +3094,7 @@ devlink_param_value_get_from_info(const struct devlink_param *param,
}
static struct devlink_param_item *
-devlink_param_get_from_info(struct devlink *devlink,
+devlink_param_get_from_info(struct list_head *param_list,
struct genl_info *info)
{
char *param_name;
@@ -3060,7 +3103,7 @@ devlink_param_get_from_info(struct devlink *devlink,
return NULL;
param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]);
- return devlink_param_find_by_name(&devlink->param_list, param_name);
+ return devlink_param_find_by_name(param_list, param_name);
}
static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
@@ -3071,7 +3114,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, info);
+ param_item = devlink_param_get_from_info(&devlink->param_list, info);
if (!param_item)
return -EINVAL;
@@ -3079,7 +3122,7 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
if (!msg)
return -ENOMEM;
- err = devlink_nl_param_fill(msg, devlink, param_item,
+ err = devlink_nl_param_fill(msg, devlink, 0, param_item,
DEVLINK_CMD_PARAM_GET,
info->snd_portid, info->snd_seq, 0);
if (err) {
@@ -3090,10 +3133,12 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
return genlmsg_reply(msg, info);
}
-static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
- struct genl_info *info)
+static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
+ unsigned int port_index,
+ struct list_head *param_list,
+ struct genl_info *info,
+ enum devlink_command cmd)
{
- struct devlink *devlink = info->user_ptr[0];
enum devlink_param_type param_type;
struct devlink_param_gset_ctx ctx;
enum devlink_param_cmode cmode;
@@ -3102,7 +3147,7 @@ static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
union devlink_param_value value;
int err = 0;
- param_item = devlink_param_get_from_info(devlink, info);
+ param_item = devlink_param_get_from_info(param_list, info);
if (!param_item)
return -EINVAL;
param = param_item->param;
@@ -3142,17 +3187,28 @@ static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
return err;
}
- devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+ devlink_param_notify(devlink, port_index, param_item, cmd);
return 0;
}
+static int devlink_nl_cmd_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->param_list,
+ info, DEVLINK_CMD_PARAM_NEW);
+}
+
static int devlink_param_register_one(struct devlink *devlink,
- const struct devlink_param *param)
+ unsigned int port_index,
+ struct list_head *param_list,
+ const struct devlink_param *param,
+ enum devlink_command cmd)
{
struct devlink_param_item *param_item;
- if (devlink_param_find_by_name(&devlink->param_list,
- param->name))
+ if (devlink_param_find_by_name(param_list, param->name))
return -EEXIST;
if (param->supported_cmodes == BIT(DEVLINK_PARAM_CMODE_DRIVERINIT))
@@ -3165,24 +3221,111 @@ static int devlink_param_register_one(struct devlink *devlink,
return -ENOMEM;
param_item->param = param;
- list_add_tail(&param_item->list, &devlink->param_list);
- devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+ list_add_tail(&param_item->list, param_list);
+ devlink_param_notify(devlink, port_index, param_item, cmd);
return 0;
}
static void devlink_param_unregister_one(struct devlink *devlink,
- const struct devlink_param *param)
+ unsigned int port_index,
+ struct list_head *param_list,
+ const struct devlink_param *param,
+ enum devlink_command cmd)
{
struct devlink_param_item *param_item;
- param_item = devlink_param_find_by_name(&devlink->param_list,
- param->name);
+ param_item = devlink_param_find_by_name(param_list, param->name);
WARN_ON(!param_item);
- devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_DEL);
+ devlink_param_notify(devlink, port_index, param_item, cmd);
list_del(&param_item->list);
kfree(param_item);
}
+static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg,
+ struct netlink_callback *cb)
+{
+ struct devlink_param_item *param_item;
+ struct devlink_port *devlink_port;
+ struct devlink *devlink;
+ int start = cb->args[0];
+ int idx = 0;
+ int err;
+
+ mutex_lock(&devlink_mutex);
+ list_for_each_entry(devlink, &devlink_list, list) {
+ if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+ continue;
+ mutex_lock(&devlink->lock);
+ list_for_each_entry(devlink_port, &devlink->port_list, list) {
+ list_for_each_entry(param_item,
+ &devlink_port->param_list, list) {
+ if (idx < start) {
+ idx++;
+ continue;
+ }
+ err = devlink_nl_param_fill(msg,
+ devlink_port->devlink,
+ devlink_port->index, param_item,
+ DEVLINK_CMD_PORT_PARAM_GET,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI);
+ if (err) {
+ mutex_unlock(&devlink->lock);
+ goto out;
+ }
+ idx++;
+ }
+ }
+ mutex_unlock(&devlink->lock);
+ }
+out:
+ mutex_unlock(&devlink_mutex);
+
+ cb->args[0] = idx;
+ return msg->len;
+}
+
+static int devlink_nl_cmd_port_param_get_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct devlink_port *devlink_port = info->user_ptr[0];
+ struct devlink_param_item *param_item;
+ struct sk_buff *msg;
+ int err;
+
+ param_item = devlink_param_get_from_info(&devlink_port->param_list,
+ 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_port->devlink,
+ devlink_port->index, param_item,
+ DEVLINK_CMD_PORT_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_port_param_set_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct devlink_port *devlink_port = info->user_ptr[0];
+
+ return __devlink_nl_cmd_param_set_doit(devlink_port->devlink,
+ devlink_port->index,
+ &devlink_port->param_list, info,
+ DEVLINK_CMD_PORT_PARAM_NEW);
+}
+
static int devlink_nl_region_snapshot_id_put(struct sk_buff *msg,
struct devlink *devlink,
struct devlink_snapshot *snapshot)
@@ -3504,44 +3647,56 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
u64 ret_offset, start_offset, end_offset = 0;
- struct nlattr *attrs[DEVLINK_ATTR_MAX + 1];
const struct genl_ops *ops = cb->data;
struct devlink_region *region;
struct nlattr *chunks_attr;
const char *region_name;
struct devlink *devlink;
+ struct nlattr **attrs;
bool dump = true;
void *hdr;
int err;
start_offset = *((u64 *)&cb->args[0]);
+ attrs = kmalloc_array(DEVLINK_ATTR_MAX + 1, sizeof(*attrs), GFP_KERNEL);
+ if (!attrs)
+ return -ENOMEM;
+
err = nlmsg_parse(cb->nlh, GENL_HDRLEN + devlink_nl_family.hdrsize,
attrs, DEVLINK_ATTR_MAX, ops->policy, cb->extack);
if (err)
- goto out;
+ goto out_free;
+ mutex_lock(&devlink_mutex);
devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs);
- if (IS_ERR(devlink))
- goto out;
+ if (IS_ERR(devlink)) {
+ err = PTR_ERR(devlink);
+ goto out_dev;
+ }
- mutex_lock(&devlink_mutex);
mutex_lock(&devlink->lock);
if (!attrs[DEVLINK_ATTR_REGION_NAME] ||
- !attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
+ !attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) {
+ err = -EINVAL;
goto out_unlock;
+ }
region_name = nla_data(attrs[DEVLINK_ATTR_REGION_NAME]);
region = devlink_region_get_by_name(devlink, region_name);
- if (!region)
+ if (!region) {
+ err = -EINVAL;
goto out_unlock;
+ }
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&devlink_nl_family, NLM_F_ACK | NLM_F_MULTI,
DEVLINK_CMD_REGION_READ);
- if (!hdr)
+ if (!hdr) {
+ err = -EMSGSIZE;
goto out_unlock;
+ }
err = devlink_nl_put_handle(skb, devlink);
if (err)
@@ -3552,8 +3707,10 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
goto nla_put_failure;
chunks_attr = nla_nest_start(skb, DEVLINK_ATTR_REGION_CHUNKS);
- if (!chunks_attr)
+ if (!chunks_attr) {
+ err = -EMSGSIZE;
goto nla_put_failure;
+ }
if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] &&
attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]) {
@@ -3576,8 +3733,10 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
goto nla_put_failure;
/* Check if there was any progress done to prevent infinite loop */
- if (ret_offset == start_offset)
+ if (ret_offset == start_offset) {
+ err = -EINVAL;
goto nla_put_failure;
+ }
*((u64 *)&cb->args[0]) = ret_offset;
@@ -3585,6 +3744,7 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
genlmsg_end(skb, hdr);
mutex_unlock(&devlink->lock);
mutex_unlock(&devlink_mutex);
+ kfree(attrs);
return skb->len;
@@ -3592,487 +3752,629 @@ nla_put_failure:
genlmsg_cancel(skb, hdr);
out_unlock:
mutex_unlock(&devlink->lock);
+out_dev:
mutex_unlock(&devlink_mutex);
-out:
+out_free:
+ kfree(attrs);
+ return err;
+}
+
+struct devlink_info_req {
+ struct sk_buff *msg;
+};
+
+int devlink_info_driver_name_put(struct devlink_info_req *req, const char *name)
+{
+ return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME, name);
+}
+EXPORT_SYMBOL_GPL(devlink_info_driver_name_put);
+
+int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
+{
+ return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn);
+}
+EXPORT_SYMBOL_GPL(devlink_info_serial_number_put);
+
+static int devlink_info_version_put(struct devlink_info_req *req, int attr,
+ const char *version_name,
+ const char *version_value)
+{
+ struct nlattr *nest;
+ int err;
+
+ nest = nla_nest_start(req->msg, attr);
+ if (!nest)
+ return -EMSGSIZE;
+
+ err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_NAME,
+ version_name);
+ if (err)
+ goto nla_put_failure;
+
+ err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_VALUE,
+ version_value);
+ if (err)
+ goto nla_put_failure;
+
+ nla_nest_end(req->msg, nest);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(req->msg, nest);
+ return err;
+}
+
+int devlink_info_version_fixed_put(struct devlink_info_req *req,
+ const char *version_name,
+ const char *version_value)
+{
+ return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_FIXED,
+ version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_fixed_put);
+
+int devlink_info_version_stored_put(struct devlink_info_req *req,
+ const char *version_name,
+ const char *version_value)
+{
+ return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
+ version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_stored_put);
+
+int devlink_info_version_running_put(struct devlink_info_req *req,
+ const char *version_name,
+ const char *version_value)
+{
+ return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
+ version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_running_put);
+
+static int
+devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
+ enum devlink_command cmd, u32 portid,
+ u32 seq, int flags, struct netlink_ext_ack *extack)
+{
+ struct devlink_info_req req;
+ void *hdr;
+ int err;
+
+ hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ err = -EMSGSIZE;
+ if (devlink_nl_put_handle(msg, devlink))
+ goto err_cancel_msg;
+
+ req.msg = msg;
+ err = devlink->ops->info_get(devlink, &req, extack);
+ if (err)
+ goto err_cancel_msg;
+
+ genlmsg_end(msg, hdr);
return 0;
+
+err_cancel_msg:
+ genlmsg_cancel(msg, hdr);
+ return err;
}
-#define DEVLINK_HEALTH_BUFFER_SIZE (4096 - GENL_HDRLEN)
-#define DEVLINK_HEALTH_BUFFER_DATA_SIZE (DEVLINK_HEALTH_BUFFER_SIZE / 2)
-#define DEVLINK_HEALTH_SIZE_TO_BUFFERS(size) DIV_ROUND_UP(size, DEVLINK_HEALTH_BUFFER_DATA_SIZE)
-#define DEVLINK_HEALTH_BUFFER_MAX_CHUNK 1024
+static int devlink_nl_cmd_info_get_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct devlink *devlink = info->user_ptr[0];
+ struct sk_buff *msg;
+ int err;
-struct devlink_health_buffer {
- void *data;
- u64 offset;
- u64 bytes_left;
- u64 bytes_left_metadata;
- u64 max_nested_depth;
- u64 curr_nest;
-};
+ if (!devlink->ops || !devlink->ops->info_get)
+ return -EOPNOTSUPP;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
-struct devlink_health_buffer_desc {
+ err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_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_info_get_dumpit(struct sk_buff *msg,
+ struct netlink_callback *cb)
+{
+ struct devlink *devlink;
+ int start = cb->args[0];
+ int idx = 0;
+ int err;
+
+ mutex_lock(&devlink_mutex);
+ list_for_each_entry(devlink, &devlink_list, list) {
+ if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+ continue;
+ if (idx < start) {
+ idx++;
+ continue;
+ }
+
+ mutex_lock(&devlink->lock);
+ err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ cb->extack);
+ mutex_unlock(&devlink->lock);
+ if (err)
+ break;
+ idx++;
+ }
+ mutex_unlock(&devlink_mutex);
+
+ cb->args[0] = idx;
+ return msg->len;
+}
+
+struct devlink_fmsg_item {
+ struct list_head list;
int attrtype;
- u16 len;
u8 nla_type;
- u8 nest_end;
+ u16 len;
int value[0];
};
-static void
-devlink_health_buffers_reset(struct devlink_health_buffer **buffers_list,
- u64 num_of_buffers)
-{
- u64 i;
-
- for (i = 0; i < num_of_buffers; i++) {
- memset(buffers_list[i]->data, 0, DEVLINK_HEALTH_BUFFER_SIZE);
- buffers_list[i]->offset = 0;
- buffers_list[i]->bytes_left = DEVLINK_HEALTH_BUFFER_DATA_SIZE;
- buffers_list[i]->bytes_left_metadata =
- DEVLINK_HEALTH_BUFFER_DATA_SIZE;
- buffers_list[i]->max_nested_depth = 0;
- buffers_list[i]->curr_nest = 0;
- }
-}
-
-static void
-devlink_health_buffers_destroy(struct devlink_health_buffer **buffers_list,
- u64 size);
+struct devlink_fmsg {
+ struct list_head item_list;
+};
-static struct devlink_health_buffer **
-devlink_health_buffers_create(u64 size)
+static struct devlink_fmsg *devlink_fmsg_alloc(void)
{
- struct devlink_health_buffer **buffers_list;
- u64 num_of_buffers = DEVLINK_HEALTH_SIZE_TO_BUFFERS(size);
- u64 i;
+ struct devlink_fmsg *fmsg;
- buffers_list = kcalloc(num_of_buffers,
- sizeof(struct devlink_health_buffer *),
- GFP_KERNEL);
- if (!buffers_list)
+ fmsg = kzalloc(sizeof(*fmsg), GFP_KERNEL);
+ if (!fmsg)
return NULL;
- for (i = 0; i < num_of_buffers; i++) {
- struct devlink_health_buffer *buffer;
- void *data;
+ INIT_LIST_HEAD(&fmsg->item_list);
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- data = kzalloc(DEVLINK_HEALTH_BUFFER_SIZE, GFP_KERNEL);
- if (!buffer || !data) {
- kfree(buffer);
- kfree(data);
- goto buffers_cleanup;
- }
- buffers_list[i] = buffer;
- buffer->data = data;
+ return fmsg;
+}
+
+static void devlink_fmsg_free(struct devlink_fmsg *fmsg)
+{
+ struct devlink_fmsg_item *item, *tmp;
+
+ list_for_each_entry_safe(item, tmp, &fmsg->item_list, list) {
+ list_del(&item->list);
+ kfree(item);
}
- devlink_health_buffers_reset(buffers_list, num_of_buffers);
+ kfree(fmsg);
+}
- return buffers_list;
+static int devlink_fmsg_nest_common(struct devlink_fmsg *fmsg,
+ int attrtype)
+{
+ struct devlink_fmsg_item *item;
-buffers_cleanup:
- devlink_health_buffers_destroy(buffers_list, --i);
- kfree(buffers_list);
- return NULL;
+ item = kzalloc(sizeof(*item), GFP_KERNEL);
+ if (!item)
+ return -ENOMEM;
+
+ item->attrtype = attrtype;
+ list_add_tail(&item->list, &fmsg->item_list);
+
+ return 0;
}
-static void
-devlink_health_buffers_destroy(struct devlink_health_buffer **buffers_list,
- u64 num_of_buffers)
+int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
{
- u64 i;
+ return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
- for (i = 0; i < num_of_buffers; i++) {
- kfree(buffers_list[i]->data);
- kfree(buffers_list[i]);
- }
+static int devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
+{
+ return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
}
-void
-devlink_health_buffer_offset_inc(struct devlink_health_buffer *buffer,
- int len)
+int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
{
- buffer->offset += len;
+ return devlink_fmsg_nest_end(fmsg);
}
+EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
-/* In order to store a nest, need two descriptors, for start and end */
-#define DEVLINK_HEALTH_BUFFER_NEST_SIZE (sizeof(struct devlink_health_buffer_desc) * 2)
+#define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
-int devlink_health_buffer_verify_len(struct devlink_health_buffer *buffer,
- int len, int metadata_len)
+static int devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
{
- if (len > DEVLINK_HEALTH_BUFFER_DATA_SIZE)
- return -EINVAL;
+ struct devlink_fmsg_item *item;
- if (buffer->bytes_left < len ||
- buffer->bytes_left_metadata < metadata_len)
+ if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE)
+ return -EMSGSIZE;
+
+ item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
+ if (!item)
return -ENOMEM;
+ item->nla_type = NLA_NUL_STRING;
+ item->len = strlen(name) + 1;
+ item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
+ memcpy(&item->value, name, item->len);
+ list_add_tail(&item->list, &fmsg->item_list);
+
return 0;
}
-static struct devlink_health_buffer_desc *
-devlink_health_buffer_get_desc_from_offset(struct devlink_health_buffer *buffer)
+int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
{
- return buffer->data + buffer->offset;
+ int err;
+
+ err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
+ if (err)
+ return err;
+
+ err = devlink_fmsg_put_name(fmsg, name);
+ if (err)
+ return err;
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
-int
-devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer,
- int attrtype)
+int devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
+{
+ return devlink_fmsg_nest_end(fmsg);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
+
+int devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
+ const char *name)
{
- struct devlink_health_buffer_desc *desc;
int err;
- err = devlink_health_buffer_verify_len(buffer, 0,
- DEVLINK_HEALTH_BUFFER_NEST_SIZE);
+ err = devlink_fmsg_pair_nest_start(fmsg, name);
if (err)
return err;
- if (attrtype != DEVLINK_ATTR_HEALTH_BUFFER_OBJECT &&
- attrtype != DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_PAIR &&
- attrtype != DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE &&
- attrtype != DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_ARRAY)
- return -EINVAL;
+ err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
+ if (err)
+ return err;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
+
+int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
+{
+ int err;
+
+ err = devlink_fmsg_nest_end(fmsg);
+ if (err)
+ return err;
- desc = devlink_health_buffer_get_desc_from_offset(buffer);
+ err = devlink_fmsg_nest_end(fmsg);
+ if (err)
+ return err;
- desc->attrtype = attrtype;
- buffer->bytes_left_metadata -= DEVLINK_HEALTH_BUFFER_NEST_SIZE;
- devlink_health_buffer_offset_inc(buffer, sizeof(*desc));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
- buffer->curr_nest++;
- buffer->max_nested_depth = max(buffer->max_nested_depth,
- buffer->curr_nest);
+static int devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
+ const void *value, u16 value_len,
+ u8 value_nla_type)
+{
+ struct devlink_fmsg_item *item;
+
+ if (value_len > DEVLINK_FMSG_MAX_SIZE)
+ return -EMSGSIZE;
+
+ item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
+ if (!item)
+ return -ENOMEM;
+
+ item->nla_type = value_nla_type;
+ item->len = value_len;
+ item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
+ memcpy(&item->value, value, item->len);
+ list_add_tail(&item->list, &fmsg->item_list);
return 0;
}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_nest_start);
-enum devlink_health_buffer_nest_end_cancel {
- DEVLINK_HEALTH_BUFFER_NEST_END = 1,
- DEVLINK_HEALTH_BUFFER_NEST_CANCEL,
-};
+int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
+{
+ return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_bool_put);
-static void
-devlink_health_buffer_nest_end_cancel(struct devlink_health_buffer *buffer,
- enum devlink_health_buffer_nest_end_cancel nest)
+int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
{
- struct devlink_health_buffer_desc *desc;
+ return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u8_put);
- WARN_ON(!buffer->curr_nest);
- buffer->curr_nest--;
+int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
+{
+ return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
- desc = devlink_health_buffer_get_desc_from_offset(buffer);
- desc->nest_end = nest;
- devlink_health_buffer_offset_inc(buffer, sizeof(*desc));
+int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
+{
+ return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u64_put);
-void devlink_health_buffer_nest_end(struct devlink_health_buffer *buffer)
+int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
{
- devlink_health_buffer_nest_end_cancel(buffer,
- DEVLINK_HEALTH_BUFFER_NEST_END);
+ return devlink_fmsg_put_value(fmsg, value, strlen(value) + 1,
+ NLA_NUL_STRING);
}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_nest_end);
+EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
-void devlink_health_buffer_nest_cancel(struct devlink_health_buffer *buffer)
+int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
+ u16 value_len)
{
- devlink_health_buffer_nest_end_cancel(buffer,
- DEVLINK_HEALTH_BUFFER_NEST_CANCEL);
+ return devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_nest_cancel);
+EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
-int
-devlink_health_buffer_put_object_name(struct devlink_health_buffer *buffer,
- char *name)
+int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
+ bool value)
{
- struct devlink_health_buffer_desc *desc;
int err;
- err = devlink_health_buffer_verify_len(buffer, strlen(name) + 1,
- sizeof(*desc));
+ err = devlink_fmsg_pair_nest_start(fmsg, name);
if (err)
return err;
- desc = devlink_health_buffer_get_desc_from_offset(buffer);
- desc->attrtype = DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_NAME;
- desc->nla_type = NLA_NUL_STRING;
- desc->len = strlen(name) + 1;
- memcpy(&desc->value, name, desc->len);
- devlink_health_buffer_offset_inc(buffer, sizeof(*desc) + desc->len);
+ err = devlink_fmsg_bool_put(fmsg, value);
+ if (err)
+ return err;
- buffer->bytes_left_metadata -= sizeof(*desc);
- buffer->bytes_left -= (strlen(name) + 1);
+ err = devlink_fmsg_pair_nest_end(fmsg);
+ if (err)
+ return err;
return 0;
}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_put_object_name);
+EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
-static int
-devlink_health_buffer_put_value(struct devlink_health_buffer *buffer,
- u8 nla_type, void *value, int len)
+int devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
+ u8 value)
{
- struct devlink_health_buffer_desc *desc;
int err;
- err = devlink_health_buffer_verify_len(buffer, len, sizeof(*desc));
+ err = devlink_fmsg_pair_nest_start(fmsg, name);
if (err)
return err;
- desc = devlink_health_buffer_get_desc_from_offset(buffer);
- desc->attrtype = DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA;
- desc->nla_type = nla_type;
- desc->len = len;
- memcpy(&desc->value, value, len);
- devlink_health_buffer_offset_inc(buffer, sizeof(*desc) + desc->len);
+ err = devlink_fmsg_u8_put(fmsg, value);
+ if (err)
+ return err;
- buffer->bytes_left_metadata -= sizeof(*desc);
- buffer->bytes_left -= len;
+ err = devlink_fmsg_pair_nest_end(fmsg);
+ if (err)
+ return err;
return 0;
}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
-int
-devlink_health_buffer_put_value_u8(struct devlink_health_buffer *buffer,
- u8 value)
+int devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
+ u32 value)
{
int err;
- err = devlink_health_buffer_put_value(buffer, NLA_U8, &value,
- sizeof(value));
+ err = devlink_fmsg_pair_nest_start(fmsg, name);
if (err)
return err;
- return 0;
-}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_u8);
-
-int
-devlink_health_buffer_put_value_u32(struct devlink_health_buffer *buffer,
- u32 value)
-{
- int err;
+ err = devlink_fmsg_u32_put(fmsg, value);
+ if (err)
+ return err;
- err = devlink_health_buffer_put_value(buffer, NLA_U32, &value,
- sizeof(value));
+ err = devlink_fmsg_pair_nest_end(fmsg);
if (err)
return err;
return 0;
}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_u32);
+EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
-int
-devlink_health_buffer_put_value_u64(struct devlink_health_buffer *buffer,
- u64 value)
+int devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
+ u64 value)
{
int err;
- err = devlink_health_buffer_put_value(buffer, NLA_U64, &value,
- sizeof(value));
+ err = devlink_fmsg_pair_nest_start(fmsg, name);
+ if (err)
+ return err;
+
+ err = devlink_fmsg_u64_put(fmsg, value);
+ if (err)
+ return err;
+
+ err = devlink_fmsg_pair_nest_end(fmsg);
if (err)
return err;
return 0;
}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_u64);
+EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
-int
-devlink_health_buffer_put_value_string(struct devlink_health_buffer *buffer,
- char *name)
+int devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
+ const char *value)
{
int err;
- if (strlen(name) + 1 > DEVLINK_HEALTH_BUFFER_MAX_CHUNK)
- return -EINVAL;
+ err = devlink_fmsg_pair_nest_start(fmsg, name);
+ if (err)
+ return err;
- err = devlink_health_buffer_put_value(buffer, NLA_NUL_STRING, name,
- strlen(name) + 1);
+ err = devlink_fmsg_string_put(fmsg, value);
+ if (err)
+ return err;
+
+ err = devlink_fmsg_pair_nest_end(fmsg);
if (err)
return err;
return 0;
}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_string);
+EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
-int
-devlink_health_buffer_put_value_data(struct devlink_health_buffer *buffer,
- void *data, int len)
+int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
+ const void *value, u16 value_len)
{
int err;
- if (len > DEVLINK_HEALTH_BUFFER_MAX_CHUNK)
- return -EINVAL;
+ err = devlink_fmsg_pair_nest_start(fmsg, name);
+ if (err)
+ return err;
- err = devlink_health_buffer_put_value(buffer, NLA_BINARY, data, len);
+ err = devlink_fmsg_binary_put(fmsg, value, value_len);
+ if (err)
+ return err;
+
+ err = devlink_fmsg_pair_nest_end(fmsg);
if (err)
return err;
return 0;
}
-EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_data);
+EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
static int
-devlink_health_buffer_fill_data(struct sk_buff *skb,
- struct devlink_health_buffer_desc *desc)
+devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
{
- int err = -EINVAL;
-
- switch (desc->nla_type) {
+ switch (msg->nla_type) {
+ case NLA_FLAG:
case NLA_U8:
- err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
- *(u8 *)desc->value);
- break;
case NLA_U32:
- err = nla_put_u32(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
- *(u32 *)desc->value);
- break;
case NLA_U64:
- err = nla_put_u64_64bit(skb,
- DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
- *(u64 *)desc->value, DEVLINK_ATTR_PAD);
- break;
case NLA_NUL_STRING:
- err = nla_put_string(skb,
- DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
- (char *)&desc->value);
- break;
case NLA_BINARY:
- err = nla_put(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
- desc->len, (void *)&desc->value);
- break;
+ return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
+ msg->nla_type);
+ default:
+ return -EINVAL;
}
-
- return err;
}
static int
-devlink_health_buffer_fill_type(struct sk_buff *skb,
- struct devlink_health_buffer_desc *desc)
+devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
{
- int err = -EINVAL;
+ int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
+ u8 tmp;
+
+ switch (msg->nla_type) {
+ case NLA_FLAG:
+ /* Always provide flag data, regardless of its value */
+ tmp = *(bool *) msg->value;
- switch (desc->nla_type) {
+ return nla_put_u8(skb, attrtype, tmp);
case NLA_U8:
- err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
- NLA_U8);
- break;
+ return nla_put_u8(skb, attrtype, *(u8 *) msg->value);
case NLA_U32:
- err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
- NLA_U32);
- break;
+ return nla_put_u32(skb, attrtype, *(u32 *) msg->value);
case NLA_U64:
- err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
- NLA_U64);
- break;
+ return nla_put_u64_64bit(skb, attrtype, *(u64 *) msg->value,
+ DEVLINK_ATTR_PAD);
case NLA_NUL_STRING:
- err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
- NLA_NUL_STRING);
- break;
+ return nla_put_string(skb, attrtype, (char *) &msg->value);
case NLA_BINARY:
- err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
- NLA_BINARY);
- break;
+ return nla_put(skb, attrtype, msg->len, (void *) &msg->value);
+ default:
+ return -EINVAL;
}
-
- return err;
-}
-
-static inline struct devlink_health_buffer_desc *
-devlink_health_buffer_get_next_desc(struct devlink_health_buffer_desc *desc)
-{
- return (void *)&desc->value + desc->len;
}
static int
-devlink_health_buffer_prepare_skb(struct sk_buff *skb,
- struct devlink_health_buffer *buffer)
+devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
+ int *start)
{
- struct devlink_health_buffer_desc *last_desc, *desc;
- struct nlattr **buffer_nlattr;
- int err;
+ struct devlink_fmsg_item *item;
+ struct nlattr *fmsg_nlattr;
int i = 0;
+ int err;
- buffer_nlattr = kcalloc(buffer->max_nested_depth,
- sizeof(*buffer_nlattr), GFP_KERNEL);
- if (!buffer_nlattr)
- return -EINVAL;
+ fmsg_nlattr = nla_nest_start(skb, DEVLINK_ATTR_FMSG);
+ if (!fmsg_nlattr)
+ return -EMSGSIZE;
- last_desc = devlink_health_buffer_get_desc_from_offset(buffer);
- desc = buffer->data;
- while (desc != last_desc) {
- switch (desc->attrtype) {
- case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT:
- case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_PAIR:
- case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE:
- case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_ARRAY:
- buffer_nlattr[i] = nla_nest_start(skb, desc->attrtype);
- if (!buffer_nlattr[i])
- goto nla_put_failure;
+ list_for_each_entry(item, &fmsg->item_list, list) {
+ if (i < *start) {
i++;
+ continue;
+ }
+
+ switch (item->attrtype) {
+ case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
+ case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
+ case DEVLINK_ATTR_FMSG_ARR_NEST_START:
+ case DEVLINK_ATTR_FMSG_NEST_END:
+ err = nla_put_flag(skb, item->attrtype);
break;
- case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA:
- err = devlink_health_buffer_fill_data(skb, desc);
+ case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
+ err = devlink_fmsg_item_fill_type(item, skb);
if (err)
- goto nla_put_failure;
- err = devlink_health_buffer_fill_type(skb, desc);
- if (err)
- goto nla_put_failure;
+ break;
+ err = devlink_fmsg_item_fill_data(item, skb);
break;
- case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_NAME:
- err = nla_put_string(skb, desc->attrtype,
- (char *)&desc->value);
- if (err)
- goto nla_put_failure;
+ case DEVLINK_ATTR_FMSG_OBJ_NAME:
+ err = nla_put_string(skb, item->attrtype,
+ (char *) &item->value);
break;
default:
- WARN_ON(!desc->nest_end);
- WARN_ON(i <= 0);
- if (desc->nest_end == DEVLINK_HEALTH_BUFFER_NEST_END)
- nla_nest_end(skb, buffer_nlattr[--i]);
- else
- nla_nest_cancel(skb, buffer_nlattr[--i]);
+ err = -EINVAL;
break;
}
- desc = devlink_health_buffer_get_next_desc(desc);
+ if (!err)
+ *start = ++i;
+ else
+ break;
}
- return 0;
-
-nla_put_failure:
- kfree(buffer_nlattr);
+ nla_nest_end(skb, fmsg_nlattr);
return err;
}
-static int
-devlink_health_buffer_snd(struct genl_info *info,
- enum devlink_command cmd, int flags,
- struct devlink_health_buffer **buffers_array,
- u64 num_of_buffers)
+static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
+ struct genl_info *info,
+ enum devlink_command cmd, int flags)
{
- struct sk_buff *skb;
struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ bool last = false;
+ int index = 0;
void *hdr;
int err;
- u64 i;
- for (i = 0; i < num_of_buffers; i++) {
- /* Skip buffer if driver did not fill it up with any data */
- if (!buffers_array[i]->offset)
- continue;
+ while (!last) {
+ int tmp_index = index;
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
- &devlink_nl_family, NLM_F_MULTI, cmd);
- if (!hdr)
+ &devlink_nl_family, flags | NLM_F_MULTI, cmd);
+ if (!hdr) {
+ err = -EMSGSIZE;
goto nla_put_failure;
+ }
- err = devlink_health_buffer_prepare_skb(skb, buffers_array[i]);
- if (err)
+ err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
+ if (!err)
+ last = true;
+ else if (err != -EMSGSIZE || tmp_index == index)
goto nla_put_failure;
genlmsg_end(skb, hdr);
@@ -4086,31 +4388,28 @@ devlink_health_buffer_snd(struct genl_info *info,
return -ENOMEM;
nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
NLMSG_DONE, 0, flags | NLM_F_MULTI);
- err = genlmsg_reply(skb, info);
- if (err)
- return err;
+ if (!nlh) {
+ err = -EMSGSIZE;
+ goto nla_put_failure;
+ }
- return 0;
+ return genlmsg_reply(skb, info);
nla_put_failure:
- err = -EIO;
nlmsg_free(skb);
return err;
}
struct devlink_health_reporter {
struct list_head list;
- struct devlink_health_buffer **dump_buffers_array;
- struct mutex dump_lock; /* lock parallel read/write from dump buffers */
- struct devlink_health_buffer **diagnose_buffers_array;
- struct mutex diagnose_lock; /* lock parallel read/write from diagnose buffers */
void *priv;
const struct devlink_health_reporter_ops *ops;
struct devlink *devlink;
+ struct devlink_fmsg *dump_fmsg;
+ struct mutex dump_lock; /* lock parallel read/write from dump buffers */
u64 graceful_period;
bool auto_recover;
u8 health_state;
- u8 dump_avail;
u64 dump_ts;
u64 error_count;
u64 recovery_count;
@@ -4164,9 +4463,7 @@ devlink_health_reporter_create(struct devlink *devlink,
goto unlock;
}
- if (WARN_ON(ops->dump && !ops->dump_size) ||
- WARN_ON(ops->diagnose && !ops->diagnose_size) ||
- WARN_ON(auto_recover && !ops->recover) ||
+ if (WARN_ON(auto_recover && !ops->recover) ||
WARN_ON(graceful_period && !ops->recover)) {
reporter = ERR_PTR(-EINVAL);
goto unlock;
@@ -4178,37 +4475,13 @@ devlink_health_reporter_create(struct devlink *devlink,
goto unlock;
}
- if (ops->dump) {
- reporter->dump_buffers_array =
- devlink_health_buffers_create(ops->dump_size);
- if (!reporter->dump_buffers_array) {
- kfree(reporter);
- reporter = ERR_PTR(-ENOMEM);
- goto unlock;
- }
- }
-
- if (ops->diagnose) {
- reporter->diagnose_buffers_array =
- devlink_health_buffers_create(ops->diagnose_size);
- if (!reporter->diagnose_buffers_array) {
- devlink_health_buffers_destroy(reporter->dump_buffers_array,
- DEVLINK_HEALTH_SIZE_TO_BUFFERS(ops->dump_size));
- kfree(reporter);
- reporter = ERR_PTR(-ENOMEM);
- goto unlock;
- }
- }
-
- list_add_tail(&reporter->list, &devlink->reporter_list);
- mutex_init(&reporter->dump_lock);
- mutex_init(&reporter->diagnose_lock);
-
reporter->priv = priv;
reporter->ops = ops;
reporter->devlink = devlink;
reporter->graceful_period = graceful_period;
reporter->auto_recover = auto_recover;
+ mutex_init(&reporter->dump_lock);
+ list_add_tail(&reporter->list, &devlink->reporter_list);
unlock:
mutex_unlock(&devlink->lock);
return reporter;
@@ -4225,12 +4498,10 @@ devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
{
mutex_lock(&reporter->devlink->lock);
list_del(&reporter->list);
- devlink_health_buffers_destroy(reporter->dump_buffers_array,
- DEVLINK_HEALTH_SIZE_TO_BUFFERS(reporter->ops->dump_size));
- devlink_health_buffers_destroy(reporter->diagnose_buffers_array,
- DEVLINK_HEALTH_SIZE_TO_BUFFERS(reporter->ops->diagnose_size));
- kfree(reporter);
mutex_unlock(&reporter->devlink->lock);
+ if (reporter->dump_fmsg)
+ devlink_fmsg_free(reporter->dump_fmsg);
+ kfree(reporter);
}
EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
@@ -4254,6 +4525,15 @@ devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
return 0;
}
+static void
+devlink_health_dump_clear(struct devlink_health_reporter *reporter)
+{
+ if (!reporter->dump_fmsg)
+ return;
+ devlink_fmsg_free(reporter->dump_fmsg);
+ reporter->dump_fmsg = NULL;
+}
+
static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
void *priv_ctx)
{
@@ -4262,20 +4542,34 @@ static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
if (!reporter->ops->dump)
return 0;
- if (reporter->dump_avail)
+ if (reporter->dump_fmsg)
return 0;
- devlink_health_buffers_reset(reporter->dump_buffers_array,
- DEVLINK_HEALTH_SIZE_TO_BUFFERS(reporter->ops->dump_size));
- err = reporter->ops->dump(reporter, reporter->dump_buffers_array,
- DEVLINK_HEALTH_BUFFER_SIZE,
- DEVLINK_HEALTH_SIZE_TO_BUFFERS(reporter->ops->dump_size),
- priv_ctx);
- if (!err) {
- reporter->dump_avail = true;
- reporter->dump_ts = jiffies;
+ reporter->dump_fmsg = devlink_fmsg_alloc();
+ if (!reporter->dump_fmsg) {
+ err = -ENOMEM;
+ return err;
}
+ err = devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
+ if (err)
+ goto dump_err;
+
+ err = reporter->ops->dump(reporter, reporter->dump_fmsg,
+ priv_ctx);
+ if (err)
+ goto dump_err;
+
+ err = devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
+ if (err)
+ goto dump_err;
+
+ reporter->dump_ts = jiffies;
+
+ return 0;
+
+dump_err:
+ devlink_health_dump_clear(reporter);
return err;
}
@@ -4283,7 +4577,6 @@ int devlink_health_report(struct devlink_health_reporter *reporter,
const char *msg, void *priv_ctx)
{
struct devlink *devlink = reporter->devlink;
- int err = 0;
/* write a log message of the current error */
WARN_ON(!msg);
@@ -4311,9 +4604,9 @@ int devlink_health_report(struct devlink_health_reporter *reporter,
mutex_unlock(&reporter->dump_lock);
if (reporter->auto_recover)
- err = devlink_health_reporter_recover(reporter, priv_ctx);
+ return devlink_health_reporter_recover(reporter, priv_ctx);
- return err;
+ return 0;
}
EXPORT_SYMBOL_GPL(devlink_health_report);
@@ -4357,23 +4650,22 @@ devlink_nl_health_reporter_fill(struct sk_buff *msg,
if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_STATE,
reporter->health_state))
goto reporter_nest_cancel;
- if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR,
+ if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,
reporter->error_count, DEVLINK_ATTR_PAD))
goto reporter_nest_cancel;
- if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER,
+ if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,
reporter->recovery_count, DEVLINK_ATTR_PAD))
goto reporter_nest_cancel;
- if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
+ if (reporter->ops->recover &&
+ nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
reporter->graceful_period,
DEVLINK_ATTR_PAD))
goto reporter_nest_cancel;
- if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
+ if (reporter->ops->recover &&
+ nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
reporter->auto_recover))
goto reporter_nest_cancel;
- if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_AVAIL,
- reporter->dump_avail))
- goto reporter_nest_cancel;
- if (reporter->dump_avail &&
+ if (reporter->dump_fmsg &&
nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
jiffies_to_msecs(reporter->dump_ts),
DEVLINK_ATTR_PAD))
@@ -4474,7 +4766,7 @@ devlink_nl_cmd_health_reporter_set_doit(struct sk_buff *skb,
if (!reporter->ops->recover &&
(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
- return -EINVAL;
+ return -EOPNOTSUPP;
if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
reporter->graceful_period =
@@ -4505,7 +4797,7 @@ static int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_health_reporter *reporter;
- u64 num_of_buffers;
+ struct devlink_fmsg *fmsg;
int err;
reporter = devlink_health_reporter_get_from_info(devlink, info);
@@ -4515,50 +4807,35 @@ static int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
if (!reporter->ops->diagnose)
return -EOPNOTSUPP;
- num_of_buffers =
- DEVLINK_HEALTH_SIZE_TO_BUFFERS(reporter->ops->diagnose_size);
+ fmsg = devlink_fmsg_alloc();
+ if (!fmsg)
+ return -ENOMEM;
- mutex_lock(&reporter->diagnose_lock);
- devlink_health_buffers_reset(reporter->diagnose_buffers_array,
- num_of_buffers);
+ err = devlink_fmsg_obj_nest_start(fmsg);
+ if (err)
+ goto out;
- err = reporter->ops->diagnose(reporter,
- reporter->diagnose_buffers_array,
- DEVLINK_HEALTH_BUFFER_SIZE,
- num_of_buffers);
+ err = reporter->ops->diagnose(reporter, fmsg);
if (err)
goto out;
- err = devlink_health_buffer_snd(info,
- DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
- 0, reporter->diagnose_buffers_array,
- num_of_buffers);
+ err = devlink_fmsg_obj_nest_end(fmsg);
if (err)
goto out;
- mutex_unlock(&reporter->diagnose_lock);
- return 0;
+ err = devlink_fmsg_snd(fmsg, info,
+ DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
out:
- mutex_unlock(&reporter->diagnose_lock);
+ devlink_fmsg_free(fmsg);
return err;
}
-static void
-devlink_health_dump_clear(struct devlink_health_reporter *reporter)
-{
- reporter->dump_avail = false;
- reporter->dump_ts = 0;
- devlink_health_buffers_reset(reporter->dump_buffers_array,
- DEVLINK_HEALTH_SIZE_TO_BUFFERS(reporter->ops->dump_size));
-}
-
static int devlink_nl_cmd_health_reporter_dump_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_health_reporter *reporter;
- u64 num_of_buffers;
int err;
reporter = devlink_health_reporter_get_from_info(devlink, info);
@@ -4568,18 +4845,13 @@ static int devlink_nl_cmd_health_reporter_dump_get_doit(struct sk_buff *skb,
if (!reporter->ops->dump)
return -EOPNOTSUPP;
- num_of_buffers =
- DEVLINK_HEALTH_SIZE_TO_BUFFERS(reporter->ops->dump_size);
-
mutex_lock(&reporter->dump_lock);
err = devlink_health_do_dump(reporter, NULL);
if (err)
goto out;
- err = devlink_health_buffer_snd(info,
- DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
- 0, reporter->dump_buffers_array,
- num_of_buffers);
+ err = devlink_fmsg_snd(reporter->dump_fmsg, info,
+ DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET, 0);
out:
mutex_unlock(&reporter->dump_lock);
@@ -4597,6 +4869,9 @@ devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb,
if (!reporter)
return -EINVAL;
+ if (!reporter->ops->dump)
+ return -EOPNOTSUPP;
+
mutex_lock(&reporter->dump_lock);
devlink_health_dump_clear(reporter);
mutex_unlock(&reporter->dump_lock);
@@ -4631,6 +4906,8 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_HEALTH_REPORTER_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = { .type = NLA_U64 },
[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 },
+ [DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] = { .type = NLA_NUL_STRING },
+ [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = { .type = NLA_NUL_STRING },
};
static const struct genl_ops devlink_nl_ops[] = {
@@ -4830,6 +5107,21 @@ static const struct genl_ops devlink_nl_ops[] = {
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
},
{
+ .cmd = DEVLINK_CMD_PORT_PARAM_GET,
+ .doit = devlink_nl_cmd_port_param_get_doit,
+ .dumpit = devlink_nl_cmd_port_param_get_dumpit,
+ .policy = devlink_nl_policy,
+ .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = DEVLINK_CMD_PORT_PARAM_SET,
+ .doit = devlink_nl_cmd_port_param_set_doit,
+ .policy = devlink_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+ },
+ {
.cmd = DEVLINK_CMD_REGION_GET,
.doit = devlink_nl_cmd_region_get_doit,
.dumpit = devlink_nl_cmd_region_get_dumpit,
@@ -4852,6 +5144,14 @@ static const struct genl_ops devlink_nl_ops[] = {
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
},
{
+ .cmd = DEVLINK_CMD_INFO_GET,
+ .doit = devlink_nl_cmd_info_get_doit,
+ .dumpit = devlink_nl_cmd_info_get_dumpit,
+ .policy = devlink_nl_policy,
+ .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+ /* can be retrieved by unprivileged users */
+ },
+ {
.cmd = DEVLINK_CMD_HEALTH_REPORTER_GET,
.doit = devlink_nl_cmd_health_reporter_get_doit,
.dumpit = devlink_nl_cmd_health_reporter_get_dumpit,
@@ -4896,6 +5196,13 @@ static const struct genl_ops devlink_nl_ops[] = {
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NO_LOCK,
},
+ {
+ .cmd = DEVLINK_CMD_FLASH_UPDATE,
+ .doit = devlink_nl_cmd_flash_update,
+ .policy = devlink_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+ },
};
static struct genl_family devlink_nl_family __ro_after_init = {
@@ -4979,6 +5286,14 @@ EXPORT_SYMBOL_GPL(devlink_unregister);
*/
void devlink_free(struct devlink *devlink)
{
+ 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));
+ WARN_ON(!list_empty(&devlink->port_list));
+
kfree(devlink);
}
EXPORT_SYMBOL_GPL(devlink_free);
@@ -5009,6 +5324,7 @@ int devlink_port_register(struct devlink *devlink,
devlink_port->index = port_index;
devlink_port->registered = true;
list_add_tail(&devlink_port->list, &devlink->port_list);
+ INIT_LIST_HEAD(&devlink_port->param_list);
mutex_unlock(&devlink->lock);
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
return 0;
@@ -5526,18 +5842,23 @@ out:
}
EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
-/**
- * devlink_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 devlink_params_register(struct devlink *devlink,
- const struct devlink_param *params,
- size_t params_count)
+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_params_register(struct devlink *devlink,
+ unsigned int port_index,
+ struct list_head *param_list,
+ const struct devlink_param *params,
+ size_t params_count,
+ enum devlink_command reg_cmd,
+ enum devlink_command unreg_cmd)
{
const struct devlink_param *param = params;
int i;
@@ -5545,20 +5866,12 @@ int devlink_params_register(struct devlink *devlink,
mutex_lock(&devlink->lock);
for (i = 0; i < params_count; i++, param++) {
- if (!param || !param->name || !param->supported_cmodes) {
- err = -EINVAL;
+ err = devlink_param_verify(param);
+ if (err)
goto rollback;
- }
- if (param->generic) {
- err = devlink_param_generic_verify(param);
- if (err)
- goto rollback;
- } else {
- err = devlink_param_driver_verify(param);
- if (err)
- goto rollback;
- }
- err = devlink_param_register_one(devlink, param);
+
+ err = devlink_param_register_one(devlink, port_index,
+ param_list, param, reg_cmd);
if (err)
goto rollback;
}
@@ -5570,11 +5883,48 @@ rollback:
if (!i)
goto unlock;
for (param--; i > 0; i--, param--)
- devlink_param_unregister_one(devlink, param);
+ devlink_param_unregister_one(devlink, port_index, param_list,
+ param, unreg_cmd);
unlock:
mutex_unlock(&devlink->lock);
return err;
}
+
+static void __devlink_params_unregister(struct devlink *devlink,
+ unsigned int port_index,
+ struct list_head *param_list,
+ const struct devlink_param *params,
+ size_t params_count,
+ enum devlink_command cmd)
+{
+ const struct devlink_param *param = params;
+ int i;
+
+ mutex_lock(&devlink->lock);
+ for (i = 0; i < params_count; i++, param++)
+ devlink_param_unregister_one(devlink, 0, param_list, param,
+ cmd);
+ mutex_unlock(&devlink->lock);
+}
+
+/**
+ * devlink_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 devlink_params_register(struct devlink *devlink,
+ const struct devlink_param *params,
+ size_t params_count)
+{
+ return __devlink_params_register(devlink, 0, &devlink->param_list,
+ params, params_count,
+ DEVLINK_CMD_PARAM_NEW,
+ DEVLINK_CMD_PARAM_DEL);
+}
EXPORT_SYMBOL_GPL(devlink_params_register);
/**
@@ -5587,36 +5937,103 @@ void devlink_params_unregister(struct devlink *devlink,
const struct devlink_param *params,
size_t params_count)
{
- const struct devlink_param *param = params;
- int i;
-
- mutex_lock(&devlink->lock);
- for (i = 0; i < params_count; i++, param++)
- devlink_param_unregister_one(devlink, param);
- mutex_unlock(&devlink->lock);
+ return __devlink_params_unregister(devlink, 0, &devlink->param_list,
+ params, params_count,
+ DEVLINK_CMD_PARAM_DEL);
}
EXPORT_SYMBOL_GPL(devlink_params_unregister);
/**
- * devlink_param_driverinit_value_get - get configuration parameter
- * value for driver initializing
+ * devlink_params_publish - publish configuration parameters
*
* @devlink: devlink
- * @param_id: parameter ID
- * @init_val: value of parameter in driverinit configuration mode
*
- * This function should be used by the driver to get driverinit
- * configuration for initialization after reload command.
+ * Publish previously registered configuration parameters.
*/
-int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
- union devlink_param_value *init_val)
+void devlink_params_publish(struct devlink *devlink)
{
struct devlink_param_item *param_item;
- if (!devlink->ops || !devlink->ops->reload)
- return -EOPNOTSUPP;
+ list_for_each_entry(param_item, &devlink->param_list, list) {
+ if (param_item->published)
+ continue;
+ param_item->published = true;
+ devlink_param_notify(devlink, 0, param_item,
+ DEVLINK_CMD_PARAM_NEW);
+ }
+}
+EXPORT_SYMBOL_GPL(devlink_params_publish);
- param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
+/**
+ * devlink_params_unpublish - unpublish configuration parameters
+ *
+ * @devlink: devlink
+ *
+ * Unpublish previously registered configuration parameters.
+ */
+void devlink_params_unpublish(struct devlink *devlink)
+{
+ struct devlink_param_item *param_item;
+
+ list_for_each_entry(param_item, &devlink->param_list, list) {
+ if (!param_item->published)
+ continue;
+ param_item->published = false;
+ devlink_param_notify(devlink, 0, param_item,
+ DEVLINK_CMD_PARAM_DEL);
+ }
+}
+EXPORT_SYMBOL_GPL(devlink_params_unpublish);
+
+/**
+ * devlink_port_params_register - register port configuration parameters
+ *
+ * @devlink_port: devlink port
+ * @params: configuration parameters array
+ * @params_count: number of parameters provided
+ *
+ * Register the configuration parameters supported by the port.
+ */
+int devlink_port_params_register(struct devlink_port *devlink_port,
+ const struct devlink_param *params,
+ size_t params_count)
+{
+ return __devlink_params_register(devlink_port->devlink,
+ devlink_port->index,
+ &devlink_port->param_list, params,
+ params_count,
+ DEVLINK_CMD_PORT_PARAM_NEW,
+ DEVLINK_CMD_PORT_PARAM_DEL);
+}
+EXPORT_SYMBOL_GPL(devlink_port_params_register);
+
+/**
+ * devlink_port_params_unregister - unregister port configuration
+ * parameters
+ *
+ * @devlink_port: devlink port
+ * @params: configuration parameters array
+ * @params_count: number of parameters provided
+ */
+void devlink_port_params_unregister(struct devlink_port *devlink_port,
+ const struct devlink_param *params,
+ size_t params_count)
+{
+ return __devlink_params_unregister(devlink_port->devlink,
+ devlink_port->index,
+ &devlink_port->param_list,
+ params, params_count,
+ DEVLINK_CMD_PORT_PARAM_DEL);
+}
+EXPORT_SYMBOL_GPL(devlink_port_params_unregister);
+
+static int
+__devlink_param_driverinit_value_get(struct list_head *param_list, u32 param_id,
+ union devlink_param_value *init_val)
+{
+ struct devlink_param_item *param_item;
+
+ param_item = devlink_param_find_by_id(param_list, param_id);
if (!param_item)
return -EINVAL;
@@ -5632,6 +6049,54 @@ int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
return 0;
}
+
+static int
+__devlink_param_driverinit_value_set(struct devlink *devlink,
+ unsigned int port_index,
+ struct list_head *param_list, u32 param_id,
+ union devlink_param_value init_val,
+ enum devlink_command cmd)
+{
+ struct devlink_param_item *param_item;
+
+ param_item = devlink_param_find_by_id(param_list, param_id);
+ if (!param_item)
+ return -EINVAL;
+
+ if (!devlink_param_cmode_is_supported(param_item->param,
+ DEVLINK_PARAM_CMODE_DRIVERINIT))
+ return -EOPNOTSUPP;
+
+ 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_valid = true;
+
+ devlink_param_notify(devlink, port_index, param_item, cmd);
+ return 0;
+}
+
+/**
+ * devlink_param_driverinit_value_get - get configuration parameter
+ * value for driver initializing
+ *
+ * @devlink: devlink
+ * @param_id: parameter ID
+ * @init_val: value of parameter in driverinit configuration mode
+ *
+ * This function should be used by the driver to get driverinit
+ * configuration for initialization after reload command.
+ */
+int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
+ union devlink_param_value *init_val)
+{
+ if (!devlink->ops || !devlink->ops->reload)
+ return -EOPNOTSUPP;
+
+ return __devlink_param_driverinit_value_get(&devlink->param_list,
+ param_id, init_val);
+}
EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_get);
/**
@@ -5649,26 +6114,61 @@ EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_get);
int devlink_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
union devlink_param_value init_val)
{
- struct devlink_param_item *param_item;
+ return __devlink_param_driverinit_value_set(devlink, 0,
+ &devlink->param_list,
+ param_id, init_val,
+ DEVLINK_CMD_PARAM_NEW);
+}
+EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
- param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
- if (!param_item)
- return -EINVAL;
+/**
+ * devlink_port_param_driverinit_value_get - get configuration parameter
+ * value for driver initializing
+ *
+ * @devlink_port: devlink_port
+ * @param_id: parameter ID
+ * @init_val: value of parameter in driverinit configuration mode
+ *
+ * This function should be used by the driver to get driverinit
+ * configuration for initialization after reload command.
+ */
+int devlink_port_param_driverinit_value_get(struct devlink_port *devlink_port,
+ u32 param_id,
+ union devlink_param_value *init_val)
+{
+ struct devlink *devlink = devlink_port->devlink;
- if (!devlink_param_cmode_is_supported(param_item->param,
- DEVLINK_PARAM_CMODE_DRIVERINIT))
+ if (!devlink->ops || !devlink->ops->reload)
return -EOPNOTSUPP;
- 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_valid = true;
+ return __devlink_param_driverinit_value_get(&devlink_port->param_list,
+ param_id, init_val);
+}
+EXPORT_SYMBOL_GPL(devlink_port_param_driverinit_value_get);
- devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
- return 0;
+/**
+ * devlink_port_param_driverinit_value_set - set value of configuration
+ * parameter for driverinit
+ * configuration mode
+ *
+ * @devlink_port: devlink_port
+ * @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.
+ */
+int devlink_port_param_driverinit_value_set(struct devlink_port *devlink_port,
+ u32 param_id,
+ union devlink_param_value init_val)
+{
+ return __devlink_param_driverinit_value_set(devlink_port->devlink,
+ devlink_port->index,
+ &devlink_port->param_list,
+ param_id, init_val,
+ DEVLINK_CMD_PORT_PARAM_NEW);
}
-EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
+EXPORT_SYMBOL_GPL(devlink_port_param_driverinit_value_set);
/**
* devlink_param_value_changed - notify devlink on a parameter's value
@@ -5681,7 +6181,6 @@ EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
* 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
- * devlink_param_driverinit_value_set() instead.
*/
void devlink_param_value_changed(struct devlink *devlink, u32 param_id)
{
@@ -5690,11 +6189,38 @@ void devlink_param_value_changed(struct devlink *devlink, u32 param_id)
param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
WARN_ON(!param_item);
- devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+ devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
}
EXPORT_SYMBOL_GPL(devlink_param_value_changed);
/**
+ * devlink_port_param_value_changed - notify devlink on a parameter's value
+ * change. Should be called by the driver
+ * right after the change.
+ *
+ * @devlink_port: devlink_port
+ * @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
+ * devlink_port_param_driverinit_value_set() instead.
+ */
+void devlink_port_param_value_changed(struct devlink_port *devlink_port,
+ u32 param_id)
+{
+ struct devlink_param_item *param_item;
+
+ param_item = devlink_param_find_by_id(&devlink_port->param_list,
+ param_id);
+ WARN_ON(!param_item);
+
+ devlink_param_notify(devlink_port->devlink, devlink_port->index,
+ param_item, DEVLINK_CMD_PORT_PARAM_NEW);
+}
+EXPORT_SYMBOL_GPL(devlink_port_param_value_changed);
+
+/**
* devlink_param_value_str_fill - Safely fill-up the string preventing
* from overflow of the preallocated buffer
*
@@ -5863,6 +6389,99 @@ unlock:
}
EXPORT_SYMBOL_GPL(devlink_region_snapshot_create);
+static void __devlink_compat_running_version(struct devlink *devlink,
+ char *buf, size_t len)
+{
+ const struct nlattr *nlattr;
+ struct devlink_info_req req;
+ struct sk_buff *msg;
+ int rem, err;
+
+ if (!devlink->ops->info_get)
+ return;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ req.msg = msg;
+ err = devlink->ops->info_get(devlink, &req, NULL);
+ if (err)
+ goto free_msg;
+
+ nla_for_each_attr(nlattr, (void *)msg->data, msg->len, rem) {
+ const struct nlattr *kv;
+ int rem_kv;
+
+ if (nla_type(nlattr) != DEVLINK_ATTR_INFO_VERSION_RUNNING)
+ continue;
+
+ nla_for_each_nested(kv, nlattr, rem_kv) {
+ if (nla_type(kv) != DEVLINK_ATTR_INFO_VERSION_VALUE)
+ continue;
+
+ strlcat(buf, nla_data(kv), len);
+ strlcat(buf, " ", len);
+ }
+ }
+free_msg:
+ nlmsg_free(msg);
+}
+
+void devlink_compat_running_version(struct net_device *dev,
+ char *buf, size_t len)
+{
+ struct devlink_port *devlink_port;
+ struct devlink *devlink;
+
+ mutex_lock(&devlink_mutex);
+ list_for_each_entry(devlink, &devlink_list, list) {
+ mutex_lock(&devlink->lock);
+ list_for_each_entry(devlink_port, &devlink->port_list, list) {
+ if (devlink_port->type == DEVLINK_PORT_TYPE_ETH &&
+ devlink_port->type_dev == dev) {
+ __devlink_compat_running_version(devlink,
+ buf, len);
+ mutex_unlock(&devlink->lock);
+ goto out;
+ }
+ }
+ mutex_unlock(&devlink->lock);
+ }
+out:
+ mutex_unlock(&devlink_mutex);
+}
+
+int devlink_compat_flash_update(struct net_device *dev, const char *file_name)
+{
+ struct devlink_port *devlink_port;
+ struct devlink *devlink;
+
+ mutex_lock(&devlink_mutex);
+ list_for_each_entry(devlink, &devlink_list, list) {
+ mutex_lock(&devlink->lock);
+ list_for_each_entry(devlink_port, &devlink->port_list, list) {
+ int ret = -EOPNOTSUPP;
+
+ if (devlink_port->type != DEVLINK_PORT_TYPE_ETH ||
+ devlink_port->type_dev != dev)
+ continue;
+
+ mutex_unlock(&devlink_mutex);
+ if (devlink->ops->flash_update)
+ ret = devlink->ops->flash_update(devlink,
+ file_name,
+ NULL, NULL);
+ mutex_unlock(&devlink->lock);
+ return ret;
+ }
+ mutex_unlock(&devlink->lock);
+ }
+ mutex_unlock(&devlink_mutex);
+
+ return -EOPNOTSUPP;
+}
+
static int __init devlink_module_init(void)
{
return genl_register_family(&devlink_nl_family);