History

devlink: Add health buffer support

Devlink health buffer is a mechanism to pass descriptors between drivers and devlink. 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 buffers in a format which can be translated by the devlink to the netlink message. In order to fulfill it, an internal buffer descriptor is defined. This will hold the data and metadata per each attribute and by used to pass actual commands to the netlink. This mechanism will be later used in devlink health for dump and diagnose data store by the drivers.

Eran Ben Elisha committed 7 years ago

Commit cb5ccfb1 parent f88c19a3 files · +585 −0Browse files

Unified · Split

3 files
  1. include/net/devlink.h +76 −0
  2. include/uapi/linux/devlink.h +8 −0
  3. net/core/devlink.c +501 −0
include/net/devlink.h+76−0
@@ -423,6 +423,8 @@ struct devlink_region; · 422 unmodified lines
423423
424424 typedef void devlink_snapshot_data_dest_t(const void *data);
425425
426+struct devlink_health_buffer;
427+
426428 struct devlink_ops {
427429 int (*reload)(struct devlink *devlink, struct netlink_ext_ack *extack);
428430 int (*port_type_set)(struct devlink_port *devlink_port,
@@ -584,6 +586,22 @@ int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, · 155 unmodified lines
584586 u8 *data, u32 snapshot_id,
585587 devlink_snapshot_data_dest_t *data_destructor);
586588
589+int devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer,
590+ int attrtype);
591+void devlink_health_buffer_nest_end(struct devlink_health_buffer *buffer);
592+void devlink_health_buffer_nest_cancel(struct devlink_health_buffer *buffer);
593+int devlink_health_buffer_put_object_name(struct devlink_health_buffer *buffer,
594+ char *name);
595+int devlink_health_buffer_put_value_u8(struct devlink_health_buffer *buffer,
596+ u8 value);
597+int devlink_health_buffer_put_value_u32(struct devlink_health_buffer *buffer,
598+ u32 value);
599+int devlink_health_buffer_put_value_u64(struct devlink_health_buffer *buffer,
600+ u64 value);
601+int devlink_health_buffer_put_value_string(struct devlink_health_buffer *buffer,
602+ char *name);
603+int devlink_health_buffer_put_value_data(struct devlink_health_buffer *buffer,
604+ void *data, int len);
587605 #else
588606
589607 static inline struct devlink *devlink_alloc(const struct devlink_ops *ops,
@@ -844,6 +862,64 @@ devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, · 254 unmodified lines
844862 return 0;
845863 }
846864
865+static inline int
866+devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer,
867+ int attrtype)
868+{
869+ return 0;
870+}
871+
872+static inline void
873+devlink_health_buffer_nest_end(struct devlink_health_buffer *buffer)
874+{
875+}
876+
877+static inline void
878+devlink_health_buffer_nest_cancel(struct devlink_health_buffer *buffer)
879+{
880+}
881+
882+static inline int
883+devlink_health_buffer_put_object_name(struct devlink_health_buffer *buffer,
884+ char *name)
885+{
886+ return 0;
887+}
888+
889+static inline int
890+devlink_health_buffer_put_value_u8(struct devlink_health_buffer *buffer,
891+ u8 value)
892+{
893+ return 0;
894+}
895+
896+static inline int
897+devlink_health_buffer_put_value_u32(struct devlink_health_buffer *buffer,
898+ u32 value)
899+{
900+ return 0;
901+}
902+
903+static inline int
904+devlink_health_buffer_put_value_u64(struct devlink_health_buffer *buffer,
905+ u64 value)
906+{
907+ return 0;
908+}
909+
910+static inline int
911+devlink_health_buffer_put_value_string(struct devlink_health_buffer *buffer,
912+ char *name)
913+{
914+ return 0;
915+}
916+
917+static inline int
918+devlink_health_buffer_put_value_data(struct devlink_health_buffer *buffer,
919+ void *data, int len)
920+{
921+ return 0;
922+}
847923 #endif
848924
849925 #endif /* _NET_DEVLINK_H_ */
include/uapi/linux/devlink.h+8−0
@@ -285,6 +285,14 @@ enum devlink_attr { · 284 unmodified lines
285285 DEVLINK_ATTR_REGION_CHUNK_ADDR, /* u64 */
286286 DEVLINK_ATTR_REGION_CHUNK_LEN, /* u64 */
287287
288+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT, /* nested */
289+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_PAIR, /* nested */
290+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_NAME, /* string */
291+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE, /* nested */
292+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_ARRAY, /* nested */
293+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE, /* u8 */
294+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA, /* dynamic */
295+
288296 /* add new attributes above here, update the policy in devlink.c */
289297
290298 __DEVLINK_ATTR_MAX,
net/core/devlink.c+501−0
@@ -3597,6 +3597,507 @@ out: · 3596 unmodified lines
35973597 return 0;
35983598 }
35993599
3600+#define DEVLINK_HEALTH_BUFFER_SIZE (4096 - GENL_HDRLEN)
3601+#define DEVLINK_HEALTH_BUFFER_DATA_SIZE (DEVLINK_HEALTH_BUFFER_SIZE / 2)
3602+#define DEVLINK_HEALTH_SIZE_TO_BUFFERS(size) DIV_ROUND_UP(size, DEVLINK_HEALTH_BUFFER_DATA_SIZE)
3603+#define DEVLINK_HEALTH_BUFFER_MAX_CHUNK 1024
3604+
3605+struct devlink_health_buffer {
3606+ void *data;
3607+ u64 offset;
3608+ u64 bytes_left;
3609+ u64 bytes_left_metadata;
3610+ u64 max_nested_depth;
3611+ u64 curr_nest;
3612+};
3613+
3614+struct devlink_health_buffer_desc {
3615+ int attrtype;
3616+ u16 len;
3617+ u8 nla_type;
3618+ u8 nest_end;
3619+ int value[0];
3620+};
3621+
3622+static void
3623+devlink_health_buffers_reset(struct devlink_health_buffer **buffers_list,
3624+ u64 num_of_buffers)
3625+{
3626+ u64 i;
3627+
3628+ for (i = 0; i < num_of_buffers; i++) {
3629+ memset(buffers_list[i]->data, 0, DEVLINK_HEALTH_BUFFER_SIZE);
3630+ buffers_list[i]->offset = 0;
3631+ buffers_list[i]->bytes_left = DEVLINK_HEALTH_BUFFER_DATA_SIZE;
3632+ buffers_list[i]->bytes_left_metadata =
3633+ DEVLINK_HEALTH_BUFFER_DATA_SIZE;
3634+ buffers_list[i]->max_nested_depth = 0;
3635+ buffers_list[i]->curr_nest = 0;
3636+ }
3637+}
3638+
3639+static void
3640+devlink_health_buffers_destroy(struct devlink_health_buffer **buffers_list,
3641+ u64 size);
3642+
3643+static struct devlink_health_buffer **
3644+devlink_health_buffers_create(u64 size)
3645+{
3646+ struct devlink_health_buffer **buffers_list;
3647+ u64 num_of_buffers = DEVLINK_HEALTH_SIZE_TO_BUFFERS(size);
3648+ u64 i;
3649+
3650+ buffers_list = kcalloc(num_of_buffers,
3651+ sizeof(struct devlink_health_buffer *),
3652+ GFP_KERNEL);
3653+ if (!buffers_list)
3654+ return NULL;
3655+
3656+ for (i = 0; i < num_of_buffers; i++) {
3657+ struct devlink_health_buffer *buffer;
3658+ void *data;
3659+
3660+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
3661+ data = kzalloc(DEVLINK_HEALTH_BUFFER_SIZE, GFP_KERNEL);
3662+ if (!buffer || !data) {
3663+ kfree(buffer);
3664+ kfree(data);
3665+ goto buffers_cleanup;
3666+ }
3667+ buffers_list[i] = buffer;
3668+ buffer->data = data;
3669+ }
3670+ devlink_health_buffers_reset(buffers_list, num_of_buffers);
3671+
3672+ return buffers_list;
3673+
3674+buffers_cleanup:
3675+ devlink_health_buffers_destroy(buffers_list, --i);
3676+ kfree(buffers_list);
3677+ return NULL;
3678+}
3679+
3680+static void
3681+devlink_health_buffers_destroy(struct devlink_health_buffer **buffers_list,
3682+ u64 num_of_buffers)
3683+{
3684+ u64 i;
3685+
3686+ for (i = 0; i < num_of_buffers; i++) {
3687+ kfree(buffers_list[i]->data);
3688+ kfree(buffers_list[i]);
3689+ }
3690+}
3691+
3692+void
3693+devlink_health_buffer_offset_inc(struct devlink_health_buffer *buffer,
3694+ int len)
3695+{
3696+ buffer->offset += len;
3697+}
3698+
3699+/* In order to store a nest, need two descriptors, for start and end */
3700+#define DEVLINK_HEALTH_BUFFER_NEST_SIZE (sizeof(struct devlink_health_buffer_desc) * 2)
3701+
3702+int devlink_health_buffer_verify_len(struct devlink_health_buffer *buffer,
3703+ int len, int metadata_len)
3704+{
3705+ if (len > DEVLINK_HEALTH_BUFFER_DATA_SIZE)
3706+ return -EINVAL;
3707+
3708+ if (buffer->bytes_left < len ||
3709+ buffer->bytes_left_metadata < metadata_len)
3710+ return -ENOMEM;
3711+
3712+ return 0;
3713+}
3714+
3715+static struct devlink_health_buffer_desc *
3716+devlink_health_buffer_get_desc_from_offset(struct devlink_health_buffer *buffer)
3717+{
3718+ return buffer->data + buffer->offset;
3719+}
3720+
3721+int
3722+devlink_health_buffer_nest_start(struct devlink_health_buffer *buffer,
3723+ int attrtype)
3724+{
3725+ struct devlink_health_buffer_desc *desc;
3726+ int err;
3727+
3728+ err = devlink_health_buffer_verify_len(buffer, 0,
3729+ DEVLINK_HEALTH_BUFFER_NEST_SIZE);
3730+ if (err)
3731+ return err;
3732+
3733+ if (attrtype != DEVLINK_ATTR_HEALTH_BUFFER_OBJECT &&
3734+ attrtype != DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_PAIR &&
3735+ attrtype != DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE &&
3736+ attrtype != DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_ARRAY)
3737+ return -EINVAL;
3738+
3739+ desc = devlink_health_buffer_get_desc_from_offset(buffer);
3740+
3741+ desc->attrtype = attrtype;
3742+ buffer->bytes_left_metadata -= DEVLINK_HEALTH_BUFFER_NEST_SIZE;
3743+ devlink_health_buffer_offset_inc(buffer, sizeof(*desc));
3744+
3745+ buffer->curr_nest++;
3746+ buffer->max_nested_depth = max(buffer->max_nested_depth,
3747+ buffer->curr_nest);
3748+
3749+ return 0;
3750+}
3751+EXPORT_SYMBOL_GPL(devlink_health_buffer_nest_start);
3752+
3753+enum devlink_health_buffer_nest_end_cancel {
3754+ DEVLINK_HEALTH_BUFFER_NEST_END = 1,
3755+ DEVLINK_HEALTH_BUFFER_NEST_CANCEL,
3756+};
3757+
3758+static void
3759+devlink_health_buffer_nest_end_cancel(struct devlink_health_buffer *buffer,
3760+ enum devlink_health_buffer_nest_end_cancel nest)
3761+{
3762+ struct devlink_health_buffer_desc *desc;
3763+
3764+ WARN_ON(!buffer->curr_nest);
3765+ buffer->curr_nest--;
3766+
3767+ desc = devlink_health_buffer_get_desc_from_offset(buffer);
3768+ desc->nest_end = nest;
3769+ devlink_health_buffer_offset_inc(buffer, sizeof(*desc));
3770+}
3771+
3772+void devlink_health_buffer_nest_end(struct devlink_health_buffer *buffer)
3773+{
3774+ devlink_health_buffer_nest_end_cancel(buffer,
3775+ DEVLINK_HEALTH_BUFFER_NEST_END);
3776+}
3777+EXPORT_SYMBOL_GPL(devlink_health_buffer_nest_end);
3778+
3779+void devlink_health_buffer_nest_cancel(struct devlink_health_buffer *buffer)
3780+{
3781+ devlink_health_buffer_nest_end_cancel(buffer,
3782+ DEVLINK_HEALTH_BUFFER_NEST_CANCEL);
3783+}
3784+EXPORT_SYMBOL_GPL(devlink_health_buffer_nest_cancel);
3785+
3786+int
3787+devlink_health_buffer_put_object_name(struct devlink_health_buffer *buffer,
3788+ char *name)
3789+{
3790+ struct devlink_health_buffer_desc *desc;
3791+ int err;
3792+
3793+ err = devlink_health_buffer_verify_len(buffer, strlen(name) + 1,
3794+ sizeof(*desc));
3795+ if (err)
3796+ return err;
3797+
3798+ desc = devlink_health_buffer_get_desc_from_offset(buffer);
3799+ desc->attrtype = DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_NAME;
3800+ desc->nla_type = NLA_NUL_STRING;
3801+ desc->len = strlen(name) + 1;
3802+ memcpy(&desc->value, name, desc->len);
3803+ devlink_health_buffer_offset_inc(buffer, sizeof(*desc) + desc->len);
3804+
3805+ buffer->bytes_left_metadata -= sizeof(*desc);
3806+ buffer->bytes_left -= (strlen(name) + 1);
3807+
3808+ return 0;
3809+}
3810+EXPORT_SYMBOL_GPL(devlink_health_buffer_put_object_name);
3811+
3812+static int
3813+devlink_health_buffer_put_value(struct devlink_health_buffer *buffer,
3814+ u8 nla_type, void *value, int len)
3815+{
3816+ struct devlink_health_buffer_desc *desc;
3817+ int err;
3818+
3819+ err = devlink_health_buffer_verify_len(buffer, len, sizeof(*desc));
3820+ if (err)
3821+ return err;
3822+
3823+ desc = devlink_health_buffer_get_desc_from_offset(buffer);
3824+ desc->attrtype = DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA;
3825+ desc->nla_type = nla_type;
3826+ desc->len = len;
3827+ memcpy(&desc->value, value, len);
3828+ devlink_health_buffer_offset_inc(buffer, sizeof(*desc) + desc->len);
3829+
3830+ buffer->bytes_left_metadata -= sizeof(*desc);
3831+ buffer->bytes_left -= len;
3832+
3833+ return 0;
3834+}
3835+
3836+int
3837+devlink_health_buffer_put_value_u8(struct devlink_health_buffer *buffer,
3838+ u8 value)
3839+{
3840+ int err;
3841+
3842+ err = devlink_health_buffer_put_value(buffer, NLA_U8, &value,
3843+ sizeof(value));
3844+ if (err)
3845+ return err;
3846+
3847+ return 0;
3848+}
3849+EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_u8);
3850+
3851+int
3852+devlink_health_buffer_put_value_u32(struct devlink_health_buffer *buffer,
3853+ u32 value)
3854+{
3855+ int err;
3856+
3857+ err = devlink_health_buffer_put_value(buffer, NLA_U32, &value,
3858+ sizeof(value));
3859+ if (err)
3860+ return err;
3861+
3862+ return 0;
3863+}
3864+EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_u32);
3865+
3866+int
3867+devlink_health_buffer_put_value_u64(struct devlink_health_buffer *buffer,
3868+ u64 value)
3869+{
3870+ int err;
3871+
3872+ err = devlink_health_buffer_put_value(buffer, NLA_U64, &value,
3873+ sizeof(value));
3874+ if (err)
3875+ return err;
3876+
3877+ return 0;
3878+}
3879+EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_u64);
3880+
3881+int
3882+devlink_health_buffer_put_value_string(struct devlink_health_buffer *buffer,
3883+ char *name)
3884+{
3885+ int err;
3886+
3887+ if (strlen(name) + 1 > DEVLINK_HEALTH_BUFFER_MAX_CHUNK)
3888+ return -EINVAL;
3889+
3890+ err = devlink_health_buffer_put_value(buffer, NLA_NUL_STRING, name,
3891+ strlen(name) + 1);
3892+ if (err)
3893+ return err;
3894+
3895+ return 0;
3896+}
3897+EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_string);
3898+
3899+int
3900+devlink_health_buffer_put_value_data(struct devlink_health_buffer *buffer,
3901+ void *data, int len)
3902+{
3903+ int err;
3904+
3905+ if (len > DEVLINK_HEALTH_BUFFER_MAX_CHUNK)
3906+ return -EINVAL;
3907+
3908+ err = devlink_health_buffer_put_value(buffer, NLA_BINARY, data, len);
3909+ if (err)
3910+ return err;
3911+
3912+ return 0;
3913+}
3914+EXPORT_SYMBOL_GPL(devlink_health_buffer_put_value_data);
3915+
3916+static int
3917+devlink_health_buffer_fill_data(struct sk_buff *skb,
3918+ struct devlink_health_buffer_desc *desc)
3919+{
3920+ int err = -EINVAL;
3921+
3922+ switch (desc->nla_type) {
3923+ case NLA_U8:
3924+ err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
3925+ *(u8 *)desc->value);
3926+ break;
3927+ case NLA_U32:
3928+ err = nla_put_u32(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
3929+ *(u32 *)desc->value);
3930+ break;
3931+ case NLA_U64:
3932+ err = nla_put_u64_64bit(skb,
3933+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
3934+ *(u64 *)desc->value, DEVLINK_ATTR_PAD);
3935+ break;
3936+ case NLA_NUL_STRING:
3937+ err = nla_put_string(skb,
3938+ DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
3939+ (char *)&desc->value);
3940+ break;
3941+ case NLA_BINARY:
3942+ err = nla_put(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA,
3943+ desc->len, (void *)&desc->value);
3944+ break;
3945+ }
3946+
3947+ return err;
3948+}
3949+
3950+static int
3951+devlink_health_buffer_fill_type(struct sk_buff *skb,
3952+ struct devlink_health_buffer_desc *desc)
3953+{
3954+ int err = -EINVAL;
3955+
3956+ switch (desc->nla_type) {
3957+ case NLA_U8:
3958+ err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
3959+ NLA_U8);
3960+ break;
3961+ case NLA_U32:
3962+ err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
3963+ NLA_U32);
3964+ break;
3965+ case NLA_U64:
3966+ err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
3967+ NLA_U64);
3968+ break;
3969+ case NLA_NUL_STRING:
3970+ err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
3971+ NLA_NUL_STRING);
3972+ break;
3973+ case NLA_BINARY:
3974+ err = nla_put_u8(skb, DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_TYPE,
3975+ NLA_BINARY);
3976+ break;
3977+ }
3978+
3979+ return err;
3980+}
3981+
3982+static inline struct devlink_health_buffer_desc *
3983+devlink_health_buffer_get_next_desc(struct devlink_health_buffer_desc *desc)
3984+{
3985+ return (void *)&desc->value + desc->len;
3986+}
3987+
3988+static int
3989+devlink_health_buffer_prepare_skb(struct sk_buff *skb,
3990+ struct devlink_health_buffer *buffer)
3991+{
3992+ struct devlink_health_buffer_desc *last_desc, *desc;
3993+ struct nlattr **buffer_nlattr;
3994+ int err;
3995+ int i = 0;
3996+
3997+ buffer_nlattr = kcalloc(buffer->max_nested_depth,
3998+ sizeof(*buffer_nlattr), GFP_KERNEL);
3999+ if (!buffer_nlattr)
4000+ return -EINVAL;
4001+
4002+ last_desc = devlink_health_buffer_get_desc_from_offset(buffer);
4003+ desc = buffer->data;
4004+ while (desc != last_desc) {
4005+ switch (desc->attrtype) {
4006+ case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT:
4007+ case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_PAIR:
4008+ case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE:
4009+ case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_ARRAY:
4010+ buffer_nlattr[i] = nla_nest_start(skb, desc->attrtype);
4011+ if (!buffer_nlattr[i])
4012+ goto nla_put_failure;
4013+ i++;
4014+ break;
4015+ case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_VALUE_DATA:
4016+ err = devlink_health_buffer_fill_data(skb, desc);
4017+ if (err)
4018+ goto nla_put_failure;
4019+ err = devlink_health_buffer_fill_type(skb, desc);
4020+ if (err)
4021+ goto nla_put_failure;
4022+ break;
4023+ case DEVLINK_ATTR_HEALTH_BUFFER_OBJECT_NAME:
4024+ err = nla_put_string(skb, desc->attrtype,
4025+ (char *)&desc->value);
4026+ if (err)
4027+ goto nla_put_failure;
4028+ break;
4029+ default:
4030+ WARN_ON(!desc->nest_end);
4031+ WARN_ON(i <= 0);
4032+ if (desc->nest_end == DEVLINK_HEALTH_BUFFER_NEST_END)
4033+ nla_nest_end(skb, buffer_nlattr[--i]);
4034+ else
4035+ nla_nest_cancel(skb, buffer_nlattr[--i]);
4036+ break;
4037+ }
4038+ desc = devlink_health_buffer_get_next_desc(desc);
4039+ }
4040+
4041+ return 0;
4042+
4043+nla_put_failure:
4044+ kfree(buffer_nlattr);
4045+ return err;
4046+}
4047+
4048+static int
4049+devlink_health_buffer_snd(struct genl_info *info,
4050+ enum devlink_command cmd, int flags,
4051+ struct devlink_health_buffer **buffers_array,
4052+ u64 num_of_buffers)
4053+{
4054+ struct sk_buff *skb;
4055+ struct nlmsghdr *nlh;
4056+ void *hdr;
4057+ int err;
4058+ u64 i;
4059+
4060+ for (i = 0; i < num_of_buffers; i++) {
4061+ /* Skip buffer if driver did not fill it up with any data */
4062+ if (!buffers_array[i]->offset)
4063+ continue;
4064+
4065+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
4066+ if (!skb)
4067+ return -ENOMEM;
4068+
4069+ hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
4070+ &devlink_nl_family, NLM_F_MULTI, cmd);
4071+ if (!hdr)
4072+ goto nla_put_failure;
4073+
4074+ err = devlink_health_buffer_prepare_skb(skb, buffers_array[i]);
4075+ if (err)
4076+ goto nla_put_failure;
4077+
4078+ genlmsg_end(skb, hdr);
4079+ err = genlmsg_reply(skb, info);
4080+ if (err)
4081+ return err;
4082+ }
4083+
4084+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
4085+ if (!skb)
4086+ return -ENOMEM;
4087+ nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
4088+ NLMSG_DONE, 0, flags | NLM_F_MULTI);
4089+ err = genlmsg_reply(skb, info);
4090+ if (err)
4091+ return err;
4092+
4093+ return 0;
4094+
4095+nla_put_failure:
4096+ err = -EIO;
4097+ nlmsg_free(skb);
4098+ return err;
4099+}
4100+
36004101 static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
36014102 [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
36024103 [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },