int devlink_health_do_dump(struct devlink_health_reporter *reporter,
                           void *priv_ctx,
                           struct netlink_ext_ack *extack);
+int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
+                       struct netlink_callback *cb,
+                       enum devlink_command cmd);
 
+struct devlink_fmsg *devlink_fmsg_alloc(void);
 void devlink_fmsg_free(struct devlink_fmsg *fmsg);
 
 /* Line cards */
                                            struct genl_info *info);
 int devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff *skb,
                                                struct genl_info *info);
+int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
+                                                struct genl_info *info);
 
 #include <trace/events/devlink.h>
 #include "devl_internal.h"
 
+struct devlink_fmsg_item {
+       struct list_head list;
+       int attrtype;
+       u8 nla_type;
+       u16 len;
+       int value[];
+};
+
+struct devlink_fmsg {
+       struct list_head item_list;
+       bool putting_binary; /* This flag forces enclosing of binary data
+                             * in an array brackets. It forces using
+                             * of designated API:
+                             * devlink_fmsg_binary_pair_nest_start()
+                             * devlink_fmsg_binary_pair_nest_end()
+                             */
+};
+
+struct devlink_fmsg *devlink_fmsg_alloc(void)
+{
+       struct devlink_fmsg *fmsg;
+
+       fmsg = kzalloc(sizeof(*fmsg), GFP_KERNEL);
+       if (!fmsg)
+               return NULL;
+
+       INIT_LIST_HEAD(&fmsg->item_list);
+
+       return fmsg;
+}
+
+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);
+       }
+       kfree(fmsg);
+}
+
 void *
 devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
 {
 
        return devlink_health_reporter_recover(reporter, NULL, info->extack);
 }
+
+static int devlink_fmsg_nest_common(struct devlink_fmsg *fmsg,
+                                   int attrtype)
+{
+       struct devlink_fmsg_item *item;
+
+       item = kzalloc(sizeof(*item), GFP_KERNEL);
+       if (!item)
+               return -ENOMEM;
+
+       item->attrtype = attrtype;
+       list_add_tail(&item->list, &fmsg->item_list);
+
+       return 0;
+}
+
+int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
+
+static int devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
+}
+
+int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_nest_end(fmsg);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
+
+#define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
+
+static int devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
+{
+       struct devlink_fmsg_item *item;
+
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       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;
+}
+
+int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
+{
+       int err;
+
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       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_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       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)
+{
+       int err;
+
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       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;
+
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       err = devlink_fmsg_nest_end(fmsg);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
+
+int devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
+                                       const char *name)
+{
+       int err;
+
+       err = devlink_fmsg_arr_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       fmsg->putting_binary = true;
+       return err;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
+
+int devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
+{
+       if (!fmsg->putting_binary)
+               return -EINVAL;
+
+       fmsg->putting_binary = false;
+       return devlink_fmsg_arr_pair_nest_end(fmsg);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
+
+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;
+}
+
+static int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
+}
+
+static int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
+}
+
+int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
+
+static int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
+}
+
+int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
+{
+       if (fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_put_value(fmsg, value, strlen(value) + 1,
+                                     NLA_NUL_STRING);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
+
+int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
+                           u16 value_len)
+{
+       if (!fmsg->putting_binary)
+               return -EINVAL;
+
+       return devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
+
+int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                              bool value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_bool_put(fmsg, value);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
+
+int devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                            u8 value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u8_put(fmsg, value);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
+
+int devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                             u32 value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u32_put(fmsg, value);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
+
+int devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                             u64 value)
+{
+       int err;
+
+       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_fmsg_u64_pair_put);
+
+int devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                                const char *value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       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_fmsg_string_pair_put);
+
+int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                                const void *value, u32 value_len)
+{
+       u32 data_size;
+       int end_err;
+       u32 offset;
+       int err;
+
+       err = devlink_fmsg_binary_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       for (offset = 0; offset < value_len; offset += data_size) {
+               data_size = value_len - offset;
+               if (data_size > DEVLINK_FMSG_MAX_SIZE)
+                       data_size = DEVLINK_FMSG_MAX_SIZE;
+               err = devlink_fmsg_binary_put(fmsg, value + offset, data_size);
+               if (err)
+                       break;
+               /* Exit from loop with a break (instead of
+                * return) to make sure putting_binary is turned off in
+                * devlink_fmsg_binary_pair_nest_end
+                */
+       }
+
+       end_err = devlink_fmsg_binary_pair_nest_end(fmsg);
+       if (end_err)
+               err = end_err;
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
+
+static int
+devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
+{
+       switch (msg->nla_type) {
+       case NLA_FLAG:
+       case NLA_U8:
+       case NLA_U32:
+       case NLA_U64:
+       case NLA_NUL_STRING:
+       case NLA_BINARY:
+               return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
+                                 msg->nla_type);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int
+devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
+{
+       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;
+
+               return nla_put_u8(skb, attrtype, tmp);
+       case NLA_U8:
+               return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
+       case NLA_U32:
+               return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
+       case NLA_U64:
+               return nla_put_u64_64bit(skb, attrtype, *(u64 *)msg->value,
+                                        DEVLINK_ATTR_PAD);
+       case NLA_NUL_STRING:
+               return nla_put_string(skb, attrtype, (char *)&msg->value);
+       case NLA_BINARY:
+               return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int
+devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
+                        int *start)
+{
+       struct devlink_fmsg_item *item;
+       struct nlattr *fmsg_nlattr;
+       int err = 0;
+       int i = 0;
+
+       fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
+       if (!fmsg_nlattr)
+               return -EMSGSIZE;
+
+       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_FMSG_OBJ_VALUE_DATA:
+                       err = devlink_fmsg_item_fill_type(item, skb);
+                       if (err)
+                               break;
+                       err = devlink_fmsg_item_fill_data(item, skb);
+                       break;
+               case DEVLINK_ATTR_FMSG_OBJ_NAME:
+                       err = nla_put_string(skb, item->attrtype,
+                                            (char *)&item->value);
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+               if (!err)
+                       *start = ++i;
+               else
+                       break;
+       }
+
+       nla_nest_end(skb, fmsg_nlattr);
+       return err;
+}
+
+static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
+                           struct genl_info *info,
+                           enum devlink_command cmd, int flags)
+{
+       struct nlmsghdr *nlh;
+       struct sk_buff *skb;
+       bool last = false;
+       int index = 0;
+       void *hdr;
+       int err;
+
+       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, flags | NLM_F_MULTI, cmd);
+               if (!hdr) {
+                       err = -EMSGSIZE;
+                       goto nla_put_failure;
+               }
+
+               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);
+               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);
+       if (!nlh) {
+               err = -EMSGSIZE;
+               goto nla_put_failure;
+       }
+
+       return genlmsg_reply(skb, info);
+
+nla_put_failure:
+       nlmsg_free(skb);
+       return err;
+}
+
+int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
+                       struct netlink_callback *cb,
+                       enum devlink_command cmd)
+{
+       struct devlink_nl_dump_state *state = devlink_dump_state(cb);
+       int index = state->idx;
+       int tmp_index = index;
+       void *hdr;
+       int err;
+
+       hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+                         &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
+       if (!hdr) {
+               err = -EMSGSIZE;
+               goto nla_put_failure;
+       }
+
+       err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
+       if ((err && err != -EMSGSIZE) || tmp_index == index)
+               goto nla_put_failure;
+
+       state->idx = index;
+       genlmsg_end(skb, hdr);
+       return skb->len;
+
+nla_put_failure:
+       genlmsg_cancel(skb, hdr);
+       return err;
+}
+
+int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
+                                                struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+       struct devlink_fmsg *fmsg;
+       int err;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       if (!reporter->ops->diagnose)
+               return -EOPNOTSUPP;
+
+       fmsg = devlink_fmsg_alloc();
+       if (!fmsg)
+               return -ENOMEM;
+
+       err = devlink_fmsg_obj_nest_start(fmsg);
+       if (err)
+               goto out;
+
+       err = reporter->ops->diagnose(reporter, fmsg, info->extack);
+       if (err)
+               goto out;
+
+       err = devlink_fmsg_obj_nest_end(fmsg);
+       if (err)
+               goto out;
+
+       err = devlink_fmsg_snd(fmsg, info,
+                              DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
+
+out:
+       devlink_fmsg_free(fmsg);
+       return err;
+}
 
        return err;
 }
 
-struct devlink_fmsg_item {
-       struct list_head list;
-       int attrtype;
-       u8 nla_type;
-       u16 len;
-       int value[];
-};
-
-struct devlink_fmsg {
-       struct list_head item_list;
-       bool putting_binary; /* This flag forces enclosing of binary data
-                             * in an array brackets. It forces using
-                             * of designated API:
-                             * devlink_fmsg_binary_pair_nest_start()
-                             * devlink_fmsg_binary_pair_nest_end()
-                             */
-};
-
-static struct devlink_fmsg *devlink_fmsg_alloc(void)
-{
-       struct devlink_fmsg *fmsg;
-
-       fmsg = kzalloc(sizeof(*fmsg), GFP_KERNEL);
-       if (!fmsg)
-               return NULL;
-
-       INIT_LIST_HEAD(&fmsg->item_list);
-
-       return fmsg;
-}
-
-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);
-       }
-       kfree(fmsg);
-}
-
-static int devlink_fmsg_nest_common(struct devlink_fmsg *fmsg,
-                                   int attrtype)
-{
-       struct devlink_fmsg_item *item;
-
-       item = kzalloc(sizeof(*item), GFP_KERNEL);
-       if (!item)
-               return -ENOMEM;
-
-       item->attrtype = attrtype;
-       list_add_tail(&item->list, &fmsg->item_list);
-
-       return 0;
-}
-
-int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
-
-static int devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
-}
-
-int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_nest_end(fmsg);
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
-
-#define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
-
-static int devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
-{
-       struct devlink_fmsg_item *item;
-
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       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;
-}
-
-int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
-{
-       int err;
-
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       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_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       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)
-{
-       int err;
-
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       err = devlink_fmsg_pair_nest_start(fmsg, name);
-       if (err)
-               return err;
-
-       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;
-
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       err = devlink_fmsg_nest_end(fmsg);
-       if (err)
-               return err;
-
-       err = devlink_fmsg_nest_end(fmsg);
-       if (err)
-               return err;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
-
-int devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
-                                       const char *name)
-{
-       int err;
-
-       err = devlink_fmsg_arr_pair_nest_start(fmsg, name);
-       if (err)
-               return err;
-
-       fmsg->putting_binary = true;
-       return err;
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
-
-int devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
-{
-       if (!fmsg->putting_binary)
-               return -EINVAL;
-
-       fmsg->putting_binary = false;
-       return devlink_fmsg_arr_pair_nest_end(fmsg);
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
-
-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;
-}
-
-static int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
-}
-
-static int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
-}
-
-int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
-
-static int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
-}
-
-int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
-{
-       if (fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_put_value(fmsg, value, strlen(value) + 1,
-                                     NLA_NUL_STRING);
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
-
-int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
-                           u16 value_len)
-{
-       if (!fmsg->putting_binary)
-               return -EINVAL;
-
-       return devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
-
-int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
-                              bool value)
-{
-       int err;
-
-       err = devlink_fmsg_pair_nest_start(fmsg, name);
-       if (err)
-               return err;
-
-       err = devlink_fmsg_bool_put(fmsg, value);
-       if (err)
-               return err;
-
-       err = devlink_fmsg_pair_nest_end(fmsg);
-       if (err)
-               return err;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
-
-int devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
-                            u8 value)
-{
-       int err;
-
-       err = devlink_fmsg_pair_nest_start(fmsg, name);
-       if (err)
-               return err;
-
-       err = devlink_fmsg_u8_put(fmsg, value);
-       if (err)
-               return err;
-
-       err = devlink_fmsg_pair_nest_end(fmsg);
-       if (err)
-               return err;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
-
-int devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
-                             u32 value)
-{
-       int err;
-
-       err = devlink_fmsg_pair_nest_start(fmsg, name);
-       if (err)
-               return err;
-
-       err = devlink_fmsg_u32_put(fmsg, value);
-       if (err)
-               return err;
-
-       err = devlink_fmsg_pair_nest_end(fmsg);
-       if (err)
-               return err;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
-
-int devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
-                             u64 value)
-{
-       int err;
-
-       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_fmsg_u64_pair_put);
-
-int devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
-                                const char *value)
-{
-       int err;
-
-       err = devlink_fmsg_pair_nest_start(fmsg, name);
-       if (err)
-               return err;
-
-       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_fmsg_string_pair_put);
-
-int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
-                                const void *value, u32 value_len)
-{
-       u32 data_size;
-       int end_err;
-       u32 offset;
-       int err;
-
-       err = devlink_fmsg_binary_pair_nest_start(fmsg, name);
-       if (err)
-               return err;
-
-       for (offset = 0; offset < value_len; offset += data_size) {
-               data_size = value_len - offset;
-               if (data_size > DEVLINK_FMSG_MAX_SIZE)
-                       data_size = DEVLINK_FMSG_MAX_SIZE;
-               err = devlink_fmsg_binary_put(fmsg, value + offset, data_size);
-               if (err)
-                       break;
-               /* Exit from loop with a break (instead of
-                * return) to make sure putting_binary is turned off in
-                * devlink_fmsg_binary_pair_nest_end
-                */
-       }
-
-       end_err = devlink_fmsg_binary_pair_nest_end(fmsg);
-       if (end_err)
-               err = end_err;
-
-       return err;
-}
-EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
-
-static int
-devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
-{
-       switch (msg->nla_type) {
-       case NLA_FLAG:
-       case NLA_U8:
-       case NLA_U32:
-       case NLA_U64:
-       case NLA_NUL_STRING:
-       case NLA_BINARY:
-               return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
-                                 msg->nla_type);
-       default:
-               return -EINVAL;
-       }
-}
-
-static int
-devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
-{
-       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;
-
-               return nla_put_u8(skb, attrtype, tmp);
-       case NLA_U8:
-               return nla_put_u8(skb, attrtype, *(u8 *) msg->value);
-       case NLA_U32:
-               return nla_put_u32(skb, attrtype, *(u32 *) msg->value);
-       case NLA_U64:
-               return nla_put_u64_64bit(skb, attrtype, *(u64 *) msg->value,
-                                        DEVLINK_ATTR_PAD);
-       case NLA_NUL_STRING:
-               return nla_put_string(skb, attrtype, (char *) &msg->value);
-       case NLA_BINARY:
-               return nla_put(skb, attrtype, msg->len, (void *) &msg->value);
-       default:
-               return -EINVAL;
-       }
-}
-
-static int
-devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
-                        int *start)
-{
-       struct devlink_fmsg_item *item;
-       struct nlattr *fmsg_nlattr;
-       int err = 0;
-       int i = 0;
-
-       fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
-       if (!fmsg_nlattr)
-               return -EMSGSIZE;
-
-       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_FMSG_OBJ_VALUE_DATA:
-                       err = devlink_fmsg_item_fill_type(item, skb);
-                       if (err)
-                               break;
-                       err = devlink_fmsg_item_fill_data(item, skb);
-                       break;
-               case DEVLINK_ATTR_FMSG_OBJ_NAME:
-                       err = nla_put_string(skb, item->attrtype,
-                                            (char *) &item->value);
-                       break;
-               default:
-                       err = -EINVAL;
-                       break;
-               }
-               if (!err)
-                       *start = ++i;
-               else
-                       break;
-       }
-
-       nla_nest_end(skb, fmsg_nlattr);
-       return err;
-}
-
-static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
-                           struct genl_info *info,
-                           enum devlink_command cmd, int flags)
-{
-       struct nlmsghdr *nlh;
-       struct sk_buff *skb;
-       bool last = false;
-       int index = 0;
-       void *hdr;
-       int err;
-
-       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, flags | NLM_F_MULTI, cmd);
-               if (!hdr) {
-                       err = -EMSGSIZE;
-                       goto nla_put_failure;
-               }
-
-               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);
-               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);
-       if (!nlh) {
-               err = -EMSGSIZE;
-               goto nla_put_failure;
-       }
-
-       return genlmsg_reply(skb, info);
-
-nla_put_failure:
-       nlmsg_free(skb);
-       return err;
-}
-
-static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
-                              struct netlink_callback *cb,
-                              enum devlink_command cmd)
-{
-       struct devlink_nl_dump_state *state = devlink_dump_state(cb);
-       int index = state->idx;
-       int tmp_index = index;
-       void *hdr;
-       int err;
-
-       hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
-                         &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
-       if (!hdr) {
-               err = -EMSGSIZE;
-               goto nla_put_failure;
-       }
-
-       err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
-       if ((err && err != -EMSGSIZE) || tmp_index == index)
-               goto nla_put_failure;
-
-       state->idx = index;
-       genlmsg_end(skb, hdr);
-       return skb->len;
-
-nla_put_failure:
-       genlmsg_cancel(skb, hdr);
-       return err;
-}
-
 static void
 devlink_health_dump_clear(struct devlink_health_reporter *reporter)
 {
        return reporter;
 }
 
-static int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
-                                                       struct genl_info *info)
-{
-       struct devlink *devlink = info->user_ptr[0];
-       struct devlink_health_reporter *reporter;
-       struct devlink_fmsg *fmsg;
-       int err;
-
-       reporter = devlink_health_reporter_get_from_info(devlink, info);
-       if (!reporter)
-               return -EINVAL;
-
-       if (!reporter->ops->diagnose)
-               return -EOPNOTSUPP;
-
-       fmsg = devlink_fmsg_alloc();
-       if (!fmsg)
-               return -ENOMEM;
-
-       err = devlink_fmsg_obj_nest_start(fmsg);
-       if (err)
-               goto out;
-
-       err = reporter->ops->diagnose(reporter, fmsg, info->extack);
-       if (err)
-               goto out;
-
-       err = devlink_fmsg_obj_nest_end(fmsg);
-       if (err)
-               goto out;
-
-       err = devlink_fmsg_snd(fmsg, info,
-                              DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
-
-out:
-       devlink_fmsg_free(fmsg);
-       return err;
-}
-
 static int
 devlink_nl_cmd_health_reporter_dump_get_dumpit(struct sk_buff *skb,
                                               struct netlink_callback *cb)