Message ID | 1548172644-30862-2-git-send-email-eranbe@mellanox.com |
---|---|
State | Changes Requested |
Delegated to: | David Miller |
Headers | show |
Series | Devlink health updates | expand |
Tue, Jan 22, 2019 at 04:57:18PM CET, eranbe@mellanox.com wrote: >Devlink msg is a mechanism to pass descriptors between drivers and >devlink, in json-like format. The API allows the driver to add objects, >object pair, value array (nested attributes), value and name. > >Driver can use this API to fill the msg context in a format which can be >translated by the devlink to the netlink message later. > >With this API, driver does not need to declare the total size per message >context, and it dynamically add more messages to the context (bounded by >the system memory limitation only). >There is an implicit request to have the preliminary main objects size >created via this API to be upper bounded by netlink SKB size, as those >objects do not get fragmented between different SKBs when passed to the >netlink layer. > >Devlink msg will replace devlink buffers for health reporters. Devlink >buffers which will be deprecated and removed in the downstream patch. > >Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com> >CC: Wei Yongjun <weiyongjun1@huawei.com> >Reviewed-by: Moshe Shemesh <moshe@mellanox.com> >--- > include/net/devlink.h | 70 ++++++ > include/uapi/linux/devlink.h | 8 + > net/core/devlink.c | 455 +++++++++++++++++++++++++++++++++++ > 3 files changed, 533 insertions(+) > >diff --git a/include/net/devlink.h b/include/net/devlink.h >index a81a1b7a67d7..fe323e9b14e1 100644 >--- a/include/net/devlink.h >+++ b/include/net/devlink.h >@@ -424,6 +424,7 @@ struct devlink_region; > > typedef void devlink_snapshot_data_dest_t(const void *data); > >+struct devlink_msg_ctx; > struct devlink_health_buffer; > struct devlink_health_reporter; > >@@ -615,6 +616,21 @@ int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, > u8 *data, u32 snapshot_id, > devlink_snapshot_data_dest_t *data_destructor); > >+int devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype); devlink_msg_*() functions should work with struct devlink_msg. devlink_msg_ctx*() functions should work with struct devlink_msg_ctx. Please be consistent with the naming. I think you can call this just "struct devlink_msg" of maybe "fmsg" as for "formatted message" - so it is not confused with any other devlink netlink message. >+int devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx); >+int devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, >+ void *value, u16 value_len, u8 value_nla_type); >+int devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, >+ char *name); >+int devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, >+ char *name, void *value, u16 value_len, >+ u8 value_nla_type); >+int devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, >+ char *name, void **value, u16 *value_len, >+ u8 *value_nla_type, int array_size); >+int devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name); >+int devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name); >+ > int devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer, > int attrtype); > void devlink_health_buffer_nest_end(struct devlink_health_buffer *buffer); >@@ -903,6 +919,60 @@ devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, > return 0; > } > >+static inline int >+devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype) >+{ >+ return 0; >+} >+ >+static inline int >+devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx) >+{ >+ return 0; >+} >+ >+static inline int >+devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, >+ void *value, u16 value_len, u8 value_nla_type) >+{ >+ return 0; >+} >+ >+static inline int >+devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, >+ char *name) >+{ >+ return 0; >+} >+ >+static inline int >+devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, >+ char *name, void *value, u16 value_len, >+ u8 value_nla_type) >+{ >+ return 0; >+} >+ >+static inline int >+devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, >+ char *name, void **value, u16 *value_len, >+ u8 *value_nla_type, int array_size) >+{ >+ return 0; >+} >+ >+static inline int >+devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name) >+{ >+ return 0; >+} >+ >+static inline int >+devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name) >+{ >+ return 0; >+} >+ > static inline int > devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer, > int attrtype) >diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h >index 6b26bb2ce4dc..68eeda1d0455 100644 >--- a/include/uapi/linux/devlink.h >+++ b/include/uapi/linux/devlink.h >@@ -300,6 +300,14 @@ enum devlink_attr { > DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE, /* u8 */ > DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA, /* dynamic */ > >+ DEVLINK_ATTR_MSG_OBJECT, /* nested */ >+ DEVLINK_ATTR_MSG_OBJECT_PAIR, /* nested */ >+ DEVLINK_ATTR_MSG_OBJECT_NAME, /* string */ >+ DEVLINK_ATTR_MSG_OBJECT_VALUE, /* nested */ >+ DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY, /* nested */ >+ DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, /* u8 */ >+ DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, /* dynamic */ >+ > DEVLINK_ATTR_HEALTH_REPORTER, /* nested */ > DEVLINK_ATTR_HEALTH_REPORTER_NAME, /* string */ > DEVLINK_ATTR_HEALTH_REPORTER_STATE, /* u8 */ >diff --git a/net/core/devlink.c b/net/core/devlink.c >index 60248a53c0ad..57ca096849b3 100644 >--- a/net/core/devlink.c >+++ b/net/core/devlink.c >@@ -4098,6 +4098,461 @@ devlink_health_buffer_snd(struct genl_info *info, > return err; > } > >+struct devlink_msg { Let's call this "struct devlink_msg_item" >+ struct list_head list; >+ int attrtype; >+ u8 nla_type; >+ u16 len; >+ int value[0]; >+}; >+ >+struct devlink_msg_ctx { >+ struct list_head msg_list; >+ int max_nested_depth; >+ int curr_nest; >+}; >+ >+#define DEVLINK_MSG_MAX_SIZE (4096 - GENL_HDRLEN) >+ >+static struct devlink_msg_ctx *devlink_msg_ctx_alloc(void) >+{ >+ struct devlink_msg_ctx *msg_ctx; >+ >+ msg_ctx = kzalloc(sizeof(*msg_ctx), GFP_KERNEL); >+ if (!msg_ctx) >+ return ERR_PTR(-ENOMEM); >+ >+ INIT_LIST_HEAD(&msg_ctx->msg_list); >+ >+ return msg_ctx; >+} >+ >+static void devlink_msg_ctx_free(struct devlink_msg_ctx *msg_ctx) >+{ >+ struct devlink_msg *msg, *tm; >+ >+ list_for_each_entry_safe(msg, tm, &msg_ctx->msg_list, list) { >+ list_del(&msg->list); >+ kfree(msg); >+ } >+ kfree(msg_ctx); >+} >+ >+int devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype) >+{ >+ struct devlink_msg *nest_msg; >+ >+ if (attrtype != DEVLINK_ATTR_MSG_OBJECT && >+ attrtype != DEVLINK_ATTR_MSG_OBJECT_PAIR && >+ attrtype != DEVLINK_ATTR_MSG_OBJECT_VALUE && >+ attrtype != DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY) >+ return -EINVAL; >+ >+ nest_msg = kzalloc(sizeof(*nest_msg), GFP_KERNEL); >+ if (!nest_msg) >+ return -ENOMEM; >+ >+ nest_msg->attrtype = attrtype; >+ msg_ctx->curr_nest++; >+ msg_ctx->max_nested_depth = max(msg_ctx->max_nested_depth, >+ msg_ctx->curr_nest); >+ list_add_tail(&nest_msg->list, &msg_ctx->msg_list); >+ >+ return 0; >+} >+EXPORT_SYMBOL_GPL(devlink_msg_nest_start); >+ >+int devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx) >+{ >+ struct devlink_msg *nest_msg; >+ >+ WARN_ON(!msg_ctx->curr_nest); >+ nest_msg = kzalloc(sizeof(*nest_msg), GFP_KERNEL); >+ if (!nest_msg) >+ return -ENOMEM; >+ >+ msg_ctx->curr_nest--; >+ list_add_tail(&nest_msg->list, &msg_ctx->msg_list); >+ >+ return 0; >+} >+EXPORT_SYMBOL_GPL(devlink_msg_nest_end); >+ >+int devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, >+ void *value, u16 value_len, u8 value_nla_type) >+{ >+ struct devlink_msg *value_msg; >+ >+ if (value_len > DEVLINK_MSG_MAX_SIZE) >+ return -EMSGSIZE; >+ >+ if (value_nla_type != NLA_U8 && >+ value_nla_type != NLA_U32 && >+ value_nla_type != NLA_U64 && >+ value_nla_type != NLA_NUL_STRING && >+ value_nla_type != NLA_BINARY) >+ return -EINVAL; >+ >+ value_msg = kzalloc(sizeof(*value_msg) + value_len, GFP_KERNEL); Looks fine. >+ if (!value_msg) >+ return -ENOMEM; >+ >+ value_msg->nla_type = value_nla_type; >+ value_msg->len = value_len; >+ value_msg->attrtype = DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA; >+ memcpy(&value_msg->value, value, value_len); >+ list_add_tail(&value_msg->list, &msg_ctx->msg_list); >+ >+ return 0; >+} >+EXPORT_SYMBOL_GPL(devlink_msg_put_value); >+ >+int devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, char *name) >+{ >+ struct devlink_msg *name_msg; >+ >+ if (strlen(name) + 1 > DEVLINK_MSG_MAX_SIZE) >+ return -EMSGSIZE; >+ >+ name_msg = kzalloc(sizeof(*name_msg) + strlen(name) + 1, GFP_KERNEL); >+ if (!name_msg) >+ return -ENOMEM; >+ >+ name_msg->nla_type = NLA_NUL_STRING; >+ name_msg->len = strlen(name) + 1; >+ name_msg->attrtype = DEVLINK_ATTR_MSG_OBJECT_NAME; >+ memcpy(&name_msg->value, name, name_msg->len); >+ list_add_tail(&name_msg->list, &msg_ctx->msg_list); >+ >+ return 0; >+} >+EXPORT_SYMBOL_GPL(devlink_msg_put_name); >+ >+int devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, >+ char *name, void *value, u16 value_len, >+ u8 value_nla_type) >+{ >+ int err; >+ >+ err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); >+ if (err) >+ return err; >+ >+ err = devlink_msg_put_name(msg_ctx, name); >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); >+ if (err) >+ return err; >+ >+ err = devlink_msg_put_value(msg_ctx, value, value_len, value_nla_type); >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_end(msg_ctx); >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_end(msg_ctx); >+ if (err) >+ return err; >+ >+ return 0; >+} >+EXPORT_SYMBOL_GPL(devlink_msg_put_name_value_pair); >+ >+int devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, >+ char *name, void **value, u16 *value_len, >+ u8 *value_nla_type, int array_size) This is limitting the arrays to plain values. One should be able to nest objects inside. If fact, that is what you can to do with sqs: object start name sqs array start object start index 0 xxx yyy object end object start index 1 xxx yyy object end array end object end So you need to have: devlink_msg_put_array_start() devlink_msg_put_array_end() >+{ >+ int err, i; >+ >+ err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); >+ if (err) >+ return err; >+ >+ err = devlink_msg_put_name(msg_ctx, name); >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_start(msg_ctx, >+ DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY); >+ if (err) >+ return err; >+ >+ for (i = 0; i < array_size; i++) { >+ err = devlink_msg_nest_start(msg_ctx, >+ DEVLINK_ATTR_MSG_OBJECT_VALUE); >+ if (err) >+ return err; >+ >+ err = devlink_msg_put_value(msg_ctx, value[i], >+ value_len[i], value_nla_type[i]); >+ if (err) >+ return err; >+ err = devlink_msg_nest_end(msg_ctx); >+ if (err) >+ return err; >+ } >+ >+ err = devlink_msg_nest_end(msg_ctx); >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_end(msg_ctx); >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_end(msg_ctx); >+ if (err) >+ return err; >+ >+ return 0; >+} >+EXPORT_SYMBOL_GPL(devlink_msg_put_name_value_array); >+ >+int devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name) >+{ >+ int err; >+ >+ err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT); >+ if (err) >+ return err; >+ >+ if (!name) >+ return 0; >+ >+ err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); >+ if (err) >+ return err; >+ >+ err = devlink_msg_put_name(msg_ctx, name); >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); >+ if (err) >+ return err; >+ >+ return 0; >+} >+EXPORT_SYMBOL_GPL(devlink_msg_object_start); >+ >+int devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name) >+{ >+ int err; >+ >+ if (!name) >+ goto end_object; >+ >+ err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT_VALUE */ >+ if (err) >+ return err; >+ >+ err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT_PAIR */ >+ if (err) >+ return err; >+ >+end_object: >+ err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT */ >+ if (err) >+ return err; >+ >+ return 0; >+} >+EXPORT_SYMBOL_GPL(devlink_msg_object_end); >+ >+static int >+devlink_msg_fill_data(struct sk_buff *skb, struct devlink_msg *msg) >+{ >+ int err; >+ >+ switch (msg->nla_type) { >+ case NLA_U8: >+ err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >+ *(u8 *)msg->value); >+ break; >+ case NLA_U32: >+ err = nla_put_u32(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >+ *(u32 *)msg->value); >+ break; >+ case NLA_U64: >+ err = nla_put_u64_64bit(skb, >+ DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >+ *(u64 *)msg->value, DEVLINK_ATTR_PAD); >+ break; >+ case NLA_NUL_STRING: >+ err = nla_put_string(skb, >+ DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >+ (char *)&msg->value); >+ break; >+ case NLA_BINARY: >+ err = nla_put(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >+ msg->len, (void *)&msg->value); >+ break; >+ default: >+ err = -EINVAL; >+ break; >+ } >+ >+ return err; >+} >+ >+static int >+devlink_msg_fill_type(struct sk_buff *skb, struct devlink_msg *msg) >+{ >+ int err; >+ >+ switch (msg->nla_type) { >+ case NLA_U8: >+ err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >+ NLA_U8); >+ break; >+ case NLA_U32: >+ err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >+ NLA_U32); >+ break; >+ case NLA_U64: >+ err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >+ NLA_U64); >+ break; >+ case NLA_NUL_STRING: >+ err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >+ NLA_NUL_STRING); >+ break; >+ case NLA_BINARY: >+ err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >+ NLA_BINARY); >+ break; >+ default: >+ err = -EINVAL; >+ break; >+ } >+ >+ return err; >+} >+ >+static int >+devlink_msg_prepare_skb(struct sk_buff *skb, struct devlink_msg_ctx *msg_ctx, >+ int *start) >+{ >+ struct nlattr **nlattr_arr; >+ struct devlink_msg *msg; >+ int j = 0, i = 0; >+ int err; >+ >+ nlattr_arr = kcalloc(msg_ctx->max_nested_depth, >+ sizeof(*nlattr_arr), GFP_KERNEL); >+ if (!nlattr_arr) >+ return -EINVAL; >+ >+ list_for_each_entry(msg, &msg_ctx->msg_list, list) { >+ if (j < *start) { >+ j++; >+ continue; >+ } >+ >+ switch (msg->attrtype) { >+ case DEVLINK_ATTR_MSG_OBJECT: >+ case DEVLINK_ATTR_MSG_OBJECT_PAIR: >+ case DEVLINK_ATTR_MSG_OBJECT_VALUE: >+ case DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY: >+ nlattr_arr[i] = nla_nest_start(skb, msg->attrtype); >+ if (!nlattr_arr[i]) { >+ err = -EMSGSIZE; >+ goto nla_put_failure; >+ } >+ i++; >+ break; >+ case DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA: >+ err = devlink_msg_fill_data(skb, msg); >+ if (err) >+ goto nla_put_failure; >+ err = devlink_msg_fill_type(skb, msg); >+ if (err) >+ goto nla_put_failure; >+ break; >+ case DEVLINK_ATTR_MSG_OBJECT_NAME: >+ err = nla_put_string(skb, msg->attrtype, >+ (char *)&msg->value); >+ if (err) >+ goto nla_put_failure; >+ break; >+ default: /* No attrtype */ >+ nla_nest_end(skb, nlattr_arr[--i]); >+ break; >+ } >+ j++; >+ if (!i) >+ *start = j; >+ } >+ >+ kfree(nlattr_arr); >+ return 0; >+ >+nla_put_failure: >+ for (j = 0; j < i; j++) >+ nla_nest_cancel(skb, nlattr_arr[j]); >+ kfree(nlattr_arr); >+ return err; >+} >+ >+static int devlink_msg_snd(struct genl_info *info, >+ enum devlink_command cmd, int flags, >+ struct devlink_msg_ctx *msg_ctx) >+{ >+ struct sk_buff *skb; >+ struct nlmsghdr *nlh; >+ int err, index = 0; >+ bool last = false; >+ void *hdr; >+ >+ 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) >+ goto nla_put_failure; >+ >+ err = devlink_msg_prepare_skb(skb, msg_ctx, &index); >+ if (!err) >+ last = true; >+ else if (err != -EMSGSIZE || tmp_index == index) >+ goto nla_put_failure; >+ >+ genlmsg_end(skb, hdr); >+ err = genlmsg_reply(skb, info); >+ if (err) >+ return err; Looks fine. >+ } >+ >+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); >+ if (!skb) >+ 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; >+ >+ return 0; >+ >+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; >-- >2.17.1 >
On 1/23/2019 4:31 PM, Jiri Pirko wrote: > Tue, Jan 22, 2019 at 04:57:18PM CET, eranbe@mellanox.com wrote: >> Devlink msg is a mechanism to pass descriptors between drivers and >> devlink, in json-like format. The API allows the driver to add objects, >> object pair, value array (nested attributes), value and name. >> >> Driver can use this API to fill the msg context in a format which can be >> translated by the devlink to the netlink message later. >> >> With this API, driver does not need to declare the total size per message >> context, and it dynamically add more messages to the context (bounded by >> the system memory limitation only). >> There is an implicit request to have the preliminary main objects size >> created via this API to be upper bounded by netlink SKB size, as those >> objects do not get fragmented between different SKBs when passed to the >> netlink layer. >> >> Devlink msg will replace devlink buffers for health reporters. Devlink >> buffers which will be deprecated and removed in the downstream patch. >> >> Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com> >> CC: Wei Yongjun <weiyongjun1@huawei.com> >> Reviewed-by: Moshe Shemesh <moshe@mellanox.com> >> --- >> include/net/devlink.h | 70 ++++++ >> include/uapi/linux/devlink.h | 8 + >> net/core/devlink.c | 455 +++++++++++++++++++++++++++++++++++ >> 3 files changed, 533 insertions(+) >> >> diff --git a/include/net/devlink.h b/include/net/devlink.h >> index a81a1b7a67d7..fe323e9b14e1 100644 >> --- a/include/net/devlink.h >> +++ b/include/net/devlink.h >> @@ -424,6 +424,7 @@ struct devlink_region; >> >> typedef void devlink_snapshot_data_dest_t(const void *data); >> >> +struct devlink_msg_ctx; >> struct devlink_health_buffer; >> struct devlink_health_reporter; >> >> @@ -615,6 +616,21 @@ int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, >> u8 *data, u32 snapshot_id, >> devlink_snapshot_data_dest_t *data_destructor); >> >> +int devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype); > > > devlink_msg_*() functions should work with struct devlink_msg. > devlink_msg_ctx*() functions should work with struct devlink_msg_ctx. > Please be consistent with the naming. > > I think you can call this just "struct devlink_msg" of maybe "fmsg" as > for "formatted message" - so it is not confused with any other devlink > netlink message. Ack. > > > >> +int devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx); >> +int devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, >> + void *value, u16 value_len, u8 value_nla_type); >> +int devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, >> + char *name); >> +int devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, >> + char *name, void *value, u16 value_len, >> + u8 value_nla_type); >> +int devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, >> + char *name, void **value, u16 *value_len, >> + u8 *value_nla_type, int array_size); >> +int devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name); >> +int devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name); >> + >> int devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer, >> int attrtype); >> void devlink_health_buffer_nest_end(struct devlink_health_buffer *buffer); >> @@ -903,6 +919,60 @@ devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, >> return 0; >> } >> >> +static inline int >> +devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype) >> +{ >> + return 0; >> +} >> + >> +static inline int >> +devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx) >> +{ >> + return 0; >> +} >> + >> +static inline int >> +devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, >> + void *value, u16 value_len, u8 value_nla_type) >> +{ >> + return 0; >> +} >> + >> +static inline int >> +devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, >> + char *name) >> +{ >> + return 0; >> +} >> + >> +static inline int >> +devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, >> + char *name, void *value, u16 value_len, >> + u8 value_nla_type) >> +{ >> + return 0; >> +} >> + >> +static inline int >> +devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, >> + char *name, void **value, u16 *value_len, >> + u8 *value_nla_type, int array_size) >> +{ >> + return 0; >> +} >> + >> +static inline int >> +devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name) >> +{ >> + return 0; >> +} >> + >> +static inline int >> +devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name) >> +{ >> + return 0; >> +} >> + >> static inline int >> devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer, >> int attrtype) >> diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h >> index 6b26bb2ce4dc..68eeda1d0455 100644 >> --- a/include/uapi/linux/devlink.h >> +++ b/include/uapi/linux/devlink.h >> @@ -300,6 +300,14 @@ enum devlink_attr { >> DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE, /* u8 */ >> DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA, /* dynamic */ >> >> + DEVLINK_ATTR_MSG_OBJECT, /* nested */ >> + DEVLINK_ATTR_MSG_OBJECT_PAIR, /* nested */ >> + DEVLINK_ATTR_MSG_OBJECT_NAME, /* string */ >> + DEVLINK_ATTR_MSG_OBJECT_VALUE, /* nested */ >> + DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY, /* nested */ >> + DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, /* u8 */ >> + DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, /* dynamic */ >> + >> DEVLINK_ATTR_HEALTH_REPORTER, /* nested */ >> DEVLINK_ATTR_HEALTH_REPORTER_NAME, /* string */ >> DEVLINK_ATTR_HEALTH_REPORTER_STATE, /* u8 */ >> diff --git a/net/core/devlink.c b/net/core/devlink.c >> index 60248a53c0ad..57ca096849b3 100644 >> --- a/net/core/devlink.c >> +++ b/net/core/devlink.c >> @@ -4098,6 +4098,461 @@ devlink_health_buffer_snd(struct genl_info *info, >> return err; >> } >> >> +struct devlink_msg { > > Let's call this "struct devlink_msg_item" Ack. > > >> + struct list_head list; >> + int attrtype; >> + u8 nla_type; >> + u16 len; >> + int value[0]; >> +}; >> + >> +struct devlink_msg_ctx { >> + struct list_head msg_list; >> + int max_nested_depth; >> + int curr_nest; >> +}; >> + >> +#define DEVLINK_MSG_MAX_SIZE (4096 - GENL_HDRLEN) >> + >> +static struct devlink_msg_ctx *devlink_msg_ctx_alloc(void) >> +{ >> + struct devlink_msg_ctx *msg_ctx; >> + >> + msg_ctx = kzalloc(sizeof(*msg_ctx), GFP_KERNEL); >> + if (!msg_ctx) >> + return ERR_PTR(-ENOMEM); >> + >> + INIT_LIST_HEAD(&msg_ctx->msg_list); >> + >> + return msg_ctx; >> +} >> + >> +static void devlink_msg_ctx_free(struct devlink_msg_ctx *msg_ctx) >> +{ >> + struct devlink_msg *msg, *tm; >> + >> + list_for_each_entry_safe(msg, tm, &msg_ctx->msg_list, list) { >> + list_del(&msg->list); >> + kfree(msg); >> + } >> + kfree(msg_ctx); >> +} >> + >> +int devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype) >> +{ >> + struct devlink_msg *nest_msg; >> + >> + if (attrtype != DEVLINK_ATTR_MSG_OBJECT && >> + attrtype != DEVLINK_ATTR_MSG_OBJECT_PAIR && >> + attrtype != DEVLINK_ATTR_MSG_OBJECT_VALUE && >> + attrtype != DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY) >> + return -EINVAL; >> + >> + nest_msg = kzalloc(sizeof(*nest_msg), GFP_KERNEL); >> + if (!nest_msg) >> + return -ENOMEM; >> + >> + nest_msg->attrtype = attrtype; >> + msg_ctx->curr_nest++; >> + msg_ctx->max_nested_depth = max(msg_ctx->max_nested_depth, >> + msg_ctx->curr_nest); >> + list_add_tail(&nest_msg->list, &msg_ctx->msg_list); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devlink_msg_nest_start); >> + >> +int devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx) >> +{ >> + struct devlink_msg *nest_msg; >> + >> + WARN_ON(!msg_ctx->curr_nest); >> + nest_msg = kzalloc(sizeof(*nest_msg), GFP_KERNEL); >> + if (!nest_msg) >> + return -ENOMEM; >> + >> + msg_ctx->curr_nest--; >> + list_add_tail(&nest_msg->list, &msg_ctx->msg_list); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devlink_msg_nest_end); >> + >> +int devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, >> + void *value, u16 value_len, u8 value_nla_type) >> +{ >> + struct devlink_msg *value_msg; >> + >> + if (value_len > DEVLINK_MSG_MAX_SIZE) >> + return -EMSGSIZE; >> + >> + if (value_nla_type != NLA_U8 && >> + value_nla_type != NLA_U32 && >> + value_nla_type != NLA_U64 && >> + value_nla_type != NLA_NUL_STRING && >> + value_nla_type != NLA_BINARY) >> + return -EINVAL; >> + >> + value_msg = kzalloc(sizeof(*value_msg) + value_len, GFP_KERNEL); > > Looks fine. > > >> + if (!value_msg) >> + return -ENOMEM; >> + >> + value_msg->nla_type = value_nla_type; >> + value_msg->len = value_len; >> + value_msg->attrtype = DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA; >> + memcpy(&value_msg->value, value, value_len); >> + list_add_tail(&value_msg->list, &msg_ctx->msg_list); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devlink_msg_put_value); >> + >> +int devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, char *name) >> +{ >> + struct devlink_msg *name_msg; >> + >> + if (strlen(name) + 1 > DEVLINK_MSG_MAX_SIZE) >> + return -EMSGSIZE; >> + >> + name_msg = kzalloc(sizeof(*name_msg) + strlen(name) + 1, GFP_KERNEL); >> + if (!name_msg) >> + return -ENOMEM; >> + >> + name_msg->nla_type = NLA_NUL_STRING; >> + name_msg->len = strlen(name) + 1; >> + name_msg->attrtype = DEVLINK_ATTR_MSG_OBJECT_NAME; >> + memcpy(&name_msg->value, name, name_msg->len); >> + list_add_tail(&name_msg->list, &msg_ctx->msg_list); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devlink_msg_put_name); >> + >> +int devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, >> + char *name, void *value, u16 value_len, >> + u8 value_nla_type) >> +{ >> + int err; >> + >> + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); >> + if (err) >> + return err; >> + >> + err = devlink_msg_put_name(msg_ctx, name); >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); >> + if (err) >> + return err; >> + >> + err = devlink_msg_put_value(msg_ctx, value, value_len, value_nla_type); >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_end(msg_ctx); >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_end(msg_ctx); >> + if (err) >> + return err; >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devlink_msg_put_name_value_pair); >> + >> +int devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, >> + char *name, void **value, u16 *value_len, >> + u8 *value_nla_type, int array_size) > > This is limitting the arrays to plain values. One should be able to nest > objects inside. If fact, that is what you can to do with sqs: > > object start > name sqs > array start > object start > index 0 > xxx yyy > object end > object start > index 1 > xxx yyy > object end > array end > object end > > So you need to have: > devlink_msg_put_array_start() > devlink_msg_put_array_end() Sounds good, will do. > > >> +{ >> + int err, i; >> + >> + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); >> + if (err) >> + return err; >> + >> + err = devlink_msg_put_name(msg_ctx, name); >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_start(msg_ctx, >> + DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY); >> + if (err) >> + return err; >> + >> + for (i = 0; i < array_size; i++) { >> + err = devlink_msg_nest_start(msg_ctx, >> + DEVLINK_ATTR_MSG_OBJECT_VALUE); >> + if (err) >> + return err; >> + >> + err = devlink_msg_put_value(msg_ctx, value[i], >> + value_len[i], value_nla_type[i]); >> + if (err) >> + return err; >> + err = devlink_msg_nest_end(msg_ctx); >> + if (err) >> + return err; >> + } >> + >> + err = devlink_msg_nest_end(msg_ctx); >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_end(msg_ctx); >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_end(msg_ctx); >> + if (err) >> + return err; >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devlink_msg_put_name_value_array); >> + >> +int devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name) >> +{ >> + int err; >> + >> + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT); >> + if (err) >> + return err; >> + >> + if (!name) >> + return 0; >> + >> + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); >> + if (err) >> + return err; >> + >> + err = devlink_msg_put_name(msg_ctx, name); >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); >> + if (err) >> + return err; >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devlink_msg_object_start); >> + >> +int devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name) >> +{ >> + int err; >> + >> + if (!name) >> + goto end_object; >> + >> + err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT_VALUE */ >> + if (err) >> + return err; >> + >> + err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT_PAIR */ >> + if (err) >> + return err; >> + >> +end_object: >> + err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT */ >> + if (err) >> + return err; >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devlink_msg_object_end); >> + >> +static int >> +devlink_msg_fill_data(struct sk_buff *skb, struct devlink_msg *msg) >> +{ >> + int err; >> + >> + switch (msg->nla_type) { >> + case NLA_U8: >> + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >> + *(u8 *)msg->value); >> + break; >> + case NLA_U32: >> + err = nla_put_u32(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >> + *(u32 *)msg->value); >> + break; >> + case NLA_U64: >> + err = nla_put_u64_64bit(skb, >> + DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >> + *(u64 *)msg->value, DEVLINK_ATTR_PAD); >> + break; >> + case NLA_NUL_STRING: >> + err = nla_put_string(skb, >> + DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >> + (char *)&msg->value); >> + break; >> + case NLA_BINARY: >> + err = nla_put(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, >> + msg->len, (void *)&msg->value); >> + break; >> + default: >> + err = -EINVAL; >> + break; >> + } >> + >> + return err; >> +} >> + >> +static int >> +devlink_msg_fill_type(struct sk_buff *skb, struct devlink_msg *msg) >> +{ >> + int err; >> + >> + switch (msg->nla_type) { >> + case NLA_U8: >> + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >> + NLA_U8); >> + break; >> + case NLA_U32: >> + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >> + NLA_U32); >> + break; >> + case NLA_U64: >> + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >> + NLA_U64); >> + break; >> + case NLA_NUL_STRING: >> + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >> + NLA_NUL_STRING); >> + break; >> + case NLA_BINARY: >> + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, >> + NLA_BINARY); >> + break; >> + default: >> + err = -EINVAL; >> + break; >> + } >> + >> + return err; >> +} >> + >> +static int >> +devlink_msg_prepare_skb(struct sk_buff *skb, struct devlink_msg_ctx *msg_ctx, >> + int *start) >> +{ >> + struct nlattr **nlattr_arr; >> + struct devlink_msg *msg; >> + int j = 0, i = 0; >> + int err; >> + >> + nlattr_arr = kcalloc(msg_ctx->max_nested_depth, >> + sizeof(*nlattr_arr), GFP_KERNEL); >> + if (!nlattr_arr) >> + return -EINVAL; >> + >> + list_for_each_entry(msg, &msg_ctx->msg_list, list) { >> + if (j < *start) { >> + j++; >> + continue; >> + } >> + >> + switch (msg->attrtype) { >> + case DEVLINK_ATTR_MSG_OBJECT: >> + case DEVLINK_ATTR_MSG_OBJECT_PAIR: >> + case DEVLINK_ATTR_MSG_OBJECT_VALUE: >> + case DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY: >> + nlattr_arr[i] = nla_nest_start(skb, msg->attrtype); >> + if (!nlattr_arr[i]) { >> + err = -EMSGSIZE; >> + goto nla_put_failure; >> + } >> + i++; >> + break; >> + case DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA: >> + err = devlink_msg_fill_data(skb, msg); >> + if (err) >> + goto nla_put_failure; >> + err = devlink_msg_fill_type(skb, msg); >> + if (err) >> + goto nla_put_failure; >> + break; >> + case DEVLINK_ATTR_MSG_OBJECT_NAME: >> + err = nla_put_string(skb, msg->attrtype, >> + (char *)&msg->value); >> + if (err) >> + goto nla_put_failure; >> + break; >> + default: /* No attrtype */ >> + nla_nest_end(skb, nlattr_arr[--i]); >> + break; >> + } >> + j++; >> + if (!i) >> + *start = j; >> + } >> + >> + kfree(nlattr_arr); >> + return 0; >> + >> +nla_put_failure: >> + for (j = 0; j < i; j++) >> + nla_nest_cancel(skb, nlattr_arr[j]); >> + kfree(nlattr_arr); >> + return err; >> +} >> + >> +static int devlink_msg_snd(struct genl_info *info, >> + enum devlink_command cmd, int flags, >> + struct devlink_msg_ctx *msg_ctx) >> +{ >> + struct sk_buff *skb; >> + struct nlmsghdr *nlh; >> + int err, index = 0; >> + bool last = false; >> + void *hdr; >> + >> + 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) >> + goto nla_put_failure; >> + >> + err = devlink_msg_prepare_skb(skb, msg_ctx, &index); >> + if (!err) >> + last = true; >> + else if (err != -EMSGSIZE || tmp_index == index) >> + goto nla_put_failure; >> + >> + genlmsg_end(skb, hdr); >> + err = genlmsg_reply(skb, info); >> + if (err) >> + return err; > > > Looks fine. > > >> + } >> + >> + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); >> + if (!skb) >> + 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; >> + >> + return 0; >> + >> +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; >> -- >> 2.17.1 >>
diff --git a/include/net/devlink.h b/include/net/devlink.h index a81a1b7a67d7..fe323e9b14e1 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -424,6 +424,7 @@ struct devlink_region; typedef void devlink_snapshot_data_dest_t(const void *data); +struct devlink_msg_ctx; struct devlink_health_buffer; struct devlink_health_reporter; @@ -615,6 +616,21 @@ int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, u8 *data, u32 snapshot_id, devlink_snapshot_data_dest_t *data_destructor); +int devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype); +int devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx); +int devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, + void *value, u16 value_len, u8 value_nla_type); +int devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, + char *name); +int devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, + char *name, void *value, u16 value_len, + u8 value_nla_type); +int devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, + char *name, void **value, u16 *value_len, + u8 *value_nla_type, int array_size); +int devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name); +int devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name); + int devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer, int attrtype); void devlink_health_buffer_nest_end(struct devlink_health_buffer *buffer); @@ -903,6 +919,60 @@ devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, return 0; } +static inline int +devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype) +{ + return 0; +} + +static inline int +devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx) +{ + return 0; +} + +static inline int +devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, + void *value, u16 value_len, u8 value_nla_type) +{ + return 0; +} + +static inline int +devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, + char *name) +{ + return 0; +} + +static inline int +devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, + char *name, void *value, u16 value_len, + u8 value_nla_type) +{ + return 0; +} + +static inline int +devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, + char *name, void **value, u16 *value_len, + u8 *value_nla_type, int array_size) +{ + return 0; +} + +static inline int +devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name) +{ + return 0; +} + +static inline int +devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name) +{ + return 0; +} + static inline int devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer, int attrtype) diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 6b26bb2ce4dc..68eeda1d0455 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -300,6 +300,14 @@ enum devlink_attr { DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE, /* u8 */ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA, /* dynamic */ + DEVLINK_ATTR_MSG_OBJECT, /* nested */ + DEVLINK_ATTR_MSG_OBJECT_PAIR, /* nested */ + DEVLINK_ATTR_MSG_OBJECT_NAME, /* string */ + DEVLINK_ATTR_MSG_OBJECT_VALUE, /* nested */ + DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY, /* nested */ + DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, /* u8 */ + DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, /* dynamic */ + DEVLINK_ATTR_HEALTH_REPORTER, /* nested */ DEVLINK_ATTR_HEALTH_REPORTER_NAME, /* string */ DEVLINK_ATTR_HEALTH_REPORTER_STATE, /* u8 */ diff --git a/net/core/devlink.c b/net/core/devlink.c index 60248a53c0ad..57ca096849b3 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -4098,6 +4098,461 @@ devlink_health_buffer_snd(struct genl_info *info, return err; } +struct devlink_msg { + struct list_head list; + int attrtype; + u8 nla_type; + u16 len; + int value[0]; +}; + +struct devlink_msg_ctx { + struct list_head msg_list; + int max_nested_depth; + int curr_nest; +}; + +#define DEVLINK_MSG_MAX_SIZE (4096 - GENL_HDRLEN) + +static struct devlink_msg_ctx *devlink_msg_ctx_alloc(void) +{ + struct devlink_msg_ctx *msg_ctx; + + msg_ctx = kzalloc(sizeof(*msg_ctx), GFP_KERNEL); + if (!msg_ctx) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&msg_ctx->msg_list); + + return msg_ctx; +} + +static void devlink_msg_ctx_free(struct devlink_msg_ctx *msg_ctx) +{ + struct devlink_msg *msg, *tm; + + list_for_each_entry_safe(msg, tm, &msg_ctx->msg_list, list) { + list_del(&msg->list); + kfree(msg); + } + kfree(msg_ctx); +} + +int devlink_msg_nest_start(struct devlink_msg_ctx *msg_ctx, int attrtype) +{ + struct devlink_msg *nest_msg; + + if (attrtype != DEVLINK_ATTR_MSG_OBJECT && + attrtype != DEVLINK_ATTR_MSG_OBJECT_PAIR && + attrtype != DEVLINK_ATTR_MSG_OBJECT_VALUE && + attrtype != DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY) + return -EINVAL; + + nest_msg = kzalloc(sizeof(*nest_msg), GFP_KERNEL); + if (!nest_msg) + return -ENOMEM; + + nest_msg->attrtype = attrtype; + msg_ctx->curr_nest++; + msg_ctx->max_nested_depth = max(msg_ctx->max_nested_depth, + msg_ctx->curr_nest); + list_add_tail(&nest_msg->list, &msg_ctx->msg_list); + + return 0; +} +EXPORT_SYMBOL_GPL(devlink_msg_nest_start); + +int devlink_msg_nest_end(struct devlink_msg_ctx *msg_ctx) +{ + struct devlink_msg *nest_msg; + + WARN_ON(!msg_ctx->curr_nest); + nest_msg = kzalloc(sizeof(*nest_msg), GFP_KERNEL); + if (!nest_msg) + return -ENOMEM; + + msg_ctx->curr_nest--; + list_add_tail(&nest_msg->list, &msg_ctx->msg_list); + + return 0; +} +EXPORT_SYMBOL_GPL(devlink_msg_nest_end); + +int devlink_msg_put_value(struct devlink_msg_ctx *msg_ctx, + void *value, u16 value_len, u8 value_nla_type) +{ + struct devlink_msg *value_msg; + + if (value_len > DEVLINK_MSG_MAX_SIZE) + return -EMSGSIZE; + + if (value_nla_type != NLA_U8 && + value_nla_type != NLA_U32 && + value_nla_type != NLA_U64 && + value_nla_type != NLA_NUL_STRING && + value_nla_type != NLA_BINARY) + return -EINVAL; + + value_msg = kzalloc(sizeof(*value_msg) + value_len, GFP_KERNEL); + if (!value_msg) + return -ENOMEM; + + value_msg->nla_type = value_nla_type; + value_msg->len = value_len; + value_msg->attrtype = DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA; + memcpy(&value_msg->value, value, value_len); + list_add_tail(&value_msg->list, &msg_ctx->msg_list); + + return 0; +} +EXPORT_SYMBOL_GPL(devlink_msg_put_value); + +int devlink_msg_put_name(struct devlink_msg_ctx *msg_ctx, char *name) +{ + struct devlink_msg *name_msg; + + if (strlen(name) + 1 > DEVLINK_MSG_MAX_SIZE) + return -EMSGSIZE; + + name_msg = kzalloc(sizeof(*name_msg) + strlen(name) + 1, GFP_KERNEL); + if (!name_msg) + return -ENOMEM; + + name_msg->nla_type = NLA_NUL_STRING; + name_msg->len = strlen(name) + 1; + name_msg->attrtype = DEVLINK_ATTR_MSG_OBJECT_NAME; + memcpy(&name_msg->value, name, name_msg->len); + list_add_tail(&name_msg->list, &msg_ctx->msg_list); + + return 0; +} +EXPORT_SYMBOL_GPL(devlink_msg_put_name); + +int devlink_msg_put_name_value_pair(struct devlink_msg_ctx *msg_ctx, + char *name, void *value, u16 value_len, + u8 value_nla_type) +{ + int err; + + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); + if (err) + return err; + + err = devlink_msg_put_name(msg_ctx, name); + if (err) + return err; + + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); + if (err) + return err; + + err = devlink_msg_put_value(msg_ctx, value, value_len, value_nla_type); + if (err) + return err; + + err = devlink_msg_nest_end(msg_ctx); + if (err) + return err; + + err = devlink_msg_nest_end(msg_ctx); + if (err) + return err; + + return 0; +} +EXPORT_SYMBOL_GPL(devlink_msg_put_name_value_pair); + +int devlink_msg_put_name_value_array(struct devlink_msg_ctx *msg_ctx, + char *name, void **value, u16 *value_len, + u8 *value_nla_type, int array_size) +{ + int err, i; + + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); + if (err) + return err; + + err = devlink_msg_put_name(msg_ctx, name); + if (err) + return err; + + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); + if (err) + return err; + + err = devlink_msg_nest_start(msg_ctx, + DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY); + if (err) + return err; + + for (i = 0; i < array_size; i++) { + err = devlink_msg_nest_start(msg_ctx, + DEVLINK_ATTR_MSG_OBJECT_VALUE); + if (err) + return err; + + err = devlink_msg_put_value(msg_ctx, value[i], + value_len[i], value_nla_type[i]); + if (err) + return err; + err = devlink_msg_nest_end(msg_ctx); + if (err) + return err; + } + + err = devlink_msg_nest_end(msg_ctx); + if (err) + return err; + + err = devlink_msg_nest_end(msg_ctx); + if (err) + return err; + + err = devlink_msg_nest_end(msg_ctx); + if (err) + return err; + + return 0; +} +EXPORT_SYMBOL_GPL(devlink_msg_put_name_value_array); + +int devlink_msg_object_start(struct devlink_msg_ctx *msg_ctx, char *name) +{ + int err; + + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT); + if (err) + return err; + + if (!name) + return 0; + + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_PAIR); + if (err) + return err; + + err = devlink_msg_put_name(msg_ctx, name); + if (err) + return err; + + err = devlink_msg_nest_start(msg_ctx, DEVLINK_ATTR_MSG_OBJECT_VALUE); + if (err) + return err; + + return 0; +} +EXPORT_SYMBOL_GPL(devlink_msg_object_start); + +int devlink_msg_object_end(struct devlink_msg_ctx *msg_ctx, char *name) +{ + int err; + + if (!name) + goto end_object; + + err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT_VALUE */ + if (err) + return err; + + err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT_PAIR */ + if (err) + return err; + +end_object: + err = devlink_msg_nest_end(msg_ctx); /* DEVLINK_ATTR_MSG_OBJECT */ + if (err) + return err; + + return 0; +} +EXPORT_SYMBOL_GPL(devlink_msg_object_end); + +static int +devlink_msg_fill_data(struct sk_buff *skb, struct devlink_msg *msg) +{ + int err; + + switch (msg->nla_type) { + case NLA_U8: + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, + *(u8 *)msg->value); + break; + case NLA_U32: + err = nla_put_u32(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, + *(u32 *)msg->value); + break; + case NLA_U64: + err = nla_put_u64_64bit(skb, + DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, + *(u64 *)msg->value, DEVLINK_ATTR_PAD); + break; + case NLA_NUL_STRING: + err = nla_put_string(skb, + DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, + (char *)&msg->value); + break; + case NLA_BINARY: + err = nla_put(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA, + msg->len, (void *)&msg->value); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +static int +devlink_msg_fill_type(struct sk_buff *skb, struct devlink_msg *msg) +{ + int err; + + switch (msg->nla_type) { + case NLA_U8: + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, + NLA_U8); + break; + case NLA_U32: + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, + NLA_U32); + break; + case NLA_U64: + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, + NLA_U64); + break; + case NLA_NUL_STRING: + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, + NLA_NUL_STRING); + break; + case NLA_BINARY: + err = nla_put_u8(skb, DEVLINK_ATTR_MSG_OBJECT_VALUE_TYPE, + NLA_BINARY); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +static int +devlink_msg_prepare_skb(struct sk_buff *skb, struct devlink_msg_ctx *msg_ctx, + int *start) +{ + struct nlattr **nlattr_arr; + struct devlink_msg *msg; + int j = 0, i = 0; + int err; + + nlattr_arr = kcalloc(msg_ctx->max_nested_depth, + sizeof(*nlattr_arr), GFP_KERNEL); + if (!nlattr_arr) + return -EINVAL; + + list_for_each_entry(msg, &msg_ctx->msg_list, list) { + if (j < *start) { + j++; + continue; + } + + switch (msg->attrtype) { + case DEVLINK_ATTR_MSG_OBJECT: + case DEVLINK_ATTR_MSG_OBJECT_PAIR: + case DEVLINK_ATTR_MSG_OBJECT_VALUE: + case DEVLINK_ATTR_MSG_OBJECT_VALUE_ARRAY: + nlattr_arr[i] = nla_nest_start(skb, msg->attrtype); + if (!nlattr_arr[i]) { + err = -EMSGSIZE; + goto nla_put_failure; + } + i++; + break; + case DEVLINK_ATTR_MSG_OBJECT_VALUE_DATA: + err = devlink_msg_fill_data(skb, msg); + if (err) + goto nla_put_failure; + err = devlink_msg_fill_type(skb, msg); + if (err) + goto nla_put_failure; + break; + case DEVLINK_ATTR_MSG_OBJECT_NAME: + err = nla_put_string(skb, msg->attrtype, + (char *)&msg->value); + if (err) + goto nla_put_failure; + break; + default: /* No attrtype */ + nla_nest_end(skb, nlattr_arr[--i]); + break; + } + j++; + if (!i) + *start = j; + } + + kfree(nlattr_arr); + return 0; + +nla_put_failure: + for (j = 0; j < i; j++) + nla_nest_cancel(skb, nlattr_arr[j]); + kfree(nlattr_arr); + return err; +} + +static int devlink_msg_snd(struct genl_info *info, + enum devlink_command cmd, int flags, + struct devlink_msg_ctx *msg_ctx) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + int err, index = 0; + bool last = false; + void *hdr; + + 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) + goto nla_put_failure; + + err = devlink_msg_prepare_skb(skb, msg_ctx, &index); + if (!err) + last = true; + else if (err != -EMSGSIZE || tmp_index == index) + goto nla_put_failure; + + genlmsg_end(skb, hdr); + err = genlmsg_reply(skb, info); + if (err) + return err; + } + + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!skb) + 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; + + return 0; + +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;