devlink: fix condition for compat device info
[sfrench/cifs-2.6.git] / net / core / devlink.c
index abb0da9d7b4b7c8a21a91341f56852db41f06c67..61fab0dc0166ee5e27262835f1876dea4a090280 100644 (file)
@@ -81,6 +81,7 @@ struct devlink_dpipe_header devlink_dpipe_header_ipv6 = {
 EXPORT_SYMBOL(devlink_dpipe_header_ipv6);
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
+EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);
 
 static LIST_HEAD(devlink_list);
 
@@ -932,6 +933,9 @@ static int devlink_nl_sb_pool_fill(struct sk_buff *msg, struct devlink *devlink,
        if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
                       pool_info.threshold_type))
                goto nla_put_failure;
+       if (nla_put_u32(msg, DEVLINK_ATTR_SB_POOL_CELL_SIZE,
+                       pool_info.cell_size))
+               goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
        return 0;
@@ -2697,6 +2701,11 @@ static const struct devlink_param devlink_param_generic[] = {
                .name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
                .type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
        },
+       {
+               .id = DEVLINK_PARAM_GENERIC_ID_WOL,
+               .name = DEVLINK_PARAM_GENERIC_WOL_NAME,
+               .type = DEVLINK_PARAM_GENERIC_WOL_TYPE,
+       },
 };
 
 static int devlink_param_generic_verify(const struct devlink_param *param)
@@ -2843,11 +2852,13 @@ nla_put_failure:
 }
 
 static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
+                                unsigned int port_index,
                                 struct devlink_param_item *param_item,
                                 enum devlink_command cmd,
                                 u32 portid, u32 seq, int flags)
 {
        union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1];
+       bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
        const struct devlink_param *param = param_item->param;
        struct devlink_param_gset_ctx ctx;
        struct nlattr *param_values_list;
@@ -2866,12 +2877,15 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
                                return -EOPNOTSUPP;
                        param_value[i] = param_item->driverinit_value;
                } else {
+                       if (!param_item->published)
+                               continue;
                        ctx.cmode = i;
                        err = devlink_param_get(devlink, param, &ctx);
                        if (err)
                                return err;
                        param_value[i] = ctx.val;
                }
+               param_value_set[i] = true;
        }
 
        hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
@@ -2880,6 +2894,13 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
 
        if (devlink_nl_put_handle(msg, devlink))
                goto genlmsg_cancel;
+
+       if (cmd == DEVLINK_CMD_PORT_PARAM_GET ||
+           cmd == DEVLINK_CMD_PORT_PARAM_NEW ||
+           cmd == DEVLINK_CMD_PORT_PARAM_DEL)
+               if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, port_index))
+                       goto genlmsg_cancel;
+
        param_attr = nla_nest_start(msg, DEVLINK_ATTR_PARAM);
        if (!param_attr)
                goto genlmsg_cancel;
@@ -2899,7 +2920,7 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
                goto param_nest_cancel;
 
        for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
-               if (!devlink_param_cmode_is_supported(param, i))
+               if (!param_value_set[i])
                        continue;
                err = devlink_nl_param_value_fill_one(msg, param->type,
                                                      i, param_value[i]);
@@ -2922,18 +2943,22 @@ genlmsg_cancel:
 }
 
 static void devlink_param_notify(struct devlink *devlink,
+                                unsigned int port_index,
                                 struct devlink_param_item *param_item,
                                 enum devlink_command cmd)
 {
        struct sk_buff *msg;
        int err;
 
-       WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL);
+       WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL &&
+               cmd != DEVLINK_CMD_PORT_PARAM_NEW &&
+               cmd != DEVLINK_CMD_PORT_PARAM_DEL);
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
-       err = devlink_nl_param_fill(msg, devlink, param_item, cmd, 0, 0, 0);
+       err = devlink_nl_param_fill(msg, devlink, port_index, param_item, cmd,
+                                   0, 0, 0);
        if (err) {
                nlmsg_free(msg);
                return;
@@ -2962,7 +2987,7 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg,
                                idx++;
                                continue;
                        }
-                       err = devlink_nl_param_fill(msg, devlink, param_item,
+                       err = devlink_nl_param_fill(msg, devlink, 0, param_item,
                                                    DEVLINK_CMD_PARAM_GET,
                                                    NETLINK_CB(cb->skb).portid,
                                                    cb->nlh->nlmsg_seq,
@@ -3051,7 +3076,7 @@ devlink_param_value_get_from_info(const struct devlink_param *param,
 }
 
 static struct devlink_param_item *
-devlink_param_get_from_info(struct devlink *devlink,
+devlink_param_get_from_info(struct list_head *param_list,
                            struct genl_info *info)
 {
        char *param_name;
@@ -3060,7 +3085,7 @@ devlink_param_get_from_info(struct devlink *devlink,
                return NULL;
 
        param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]);
-       return devlink_param_find_by_name(&devlink->param_list, param_name);
+       return devlink_param_find_by_name(param_list, param_name);
 }
 
 static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
@@ -3071,7 +3096,7 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
        struct sk_buff *msg;
        int err;
 
-       param_item = devlink_param_get_from_info(devlink, info);
+       param_item = devlink_param_get_from_info(&devlink->param_list, info);
        if (!param_item)
                return -EINVAL;
 
@@ -3079,7 +3104,7 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
        if (!msg)
                return -ENOMEM;
 
-       err = devlink_nl_param_fill(msg, devlink, param_item,
+       err = devlink_nl_param_fill(msg, devlink, 0, param_item,
                                    DEVLINK_CMD_PARAM_GET,
                                    info->snd_portid, info->snd_seq, 0);
        if (err) {
@@ -3090,10 +3115,12 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
        return genlmsg_reply(msg, info);
 }
 
-static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
-                                        struct genl_info *info)
+static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
+                                          unsigned int port_index,
+                                          struct list_head *param_list,
+                                          struct genl_info *info,
+                                          enum devlink_command cmd)
 {
-       struct devlink *devlink = info->user_ptr[0];
        enum devlink_param_type param_type;
        struct devlink_param_gset_ctx ctx;
        enum devlink_param_cmode cmode;
@@ -3102,7 +3129,7 @@ static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
        union devlink_param_value value;
        int err = 0;
 
-       param_item = devlink_param_get_from_info(devlink, info);
+       param_item = devlink_param_get_from_info(param_list, info);
        if (!param_item)
                return -EINVAL;
        param = param_item->param;
@@ -3142,17 +3169,28 @@ static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
                        return err;
        }
 
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+       devlink_param_notify(devlink, port_index, param_item, cmd);
        return 0;
 }
 
+static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
+                                        struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+
+       return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->param_list,
+                                              info, DEVLINK_CMD_PARAM_NEW);
+}
+
 static int devlink_param_register_one(struct devlink *devlink,
-                                     const struct devlink_param *param)
+                                     unsigned int port_index,
+                                     struct list_head *param_list,
+                                     const struct devlink_param *param,
+                                     enum devlink_command cmd)
 {
        struct devlink_param_item *param_item;
 
-       if (devlink_param_find_by_name(&devlink->param_list,
-                                      param->name))
+       if (devlink_param_find_by_name(param_list, param->name))
                return -EEXIST;
 
        if (param->supported_cmodes == BIT(DEVLINK_PARAM_CMODE_DRIVERINIT))
@@ -3165,24 +3203,111 @@ static int devlink_param_register_one(struct devlink *devlink,
                return -ENOMEM;
        param_item->param = param;
 
-       list_add_tail(&param_item->list, &devlink->param_list);
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+       list_add_tail(&param_item->list, param_list);
+       devlink_param_notify(devlink, port_index, param_item, cmd);
        return 0;
 }
 
 static void devlink_param_unregister_one(struct devlink *devlink,
-                                        const struct devlink_param *param)
+                                        unsigned int port_index,
+                                        struct list_head *param_list,
+                                        const struct devlink_param *param,
+                                        enum devlink_command cmd)
 {
        struct devlink_param_item *param_item;
 
-       param_item = devlink_param_find_by_name(&devlink->param_list,
-                                               param->name);
+       param_item = devlink_param_find_by_name(param_list, param->name);
        WARN_ON(!param_item);
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_DEL);
+       devlink_param_notify(devlink, port_index, param_item, cmd);
        list_del(&param_item->list);
        kfree(param_item);
 }
 
+static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg,
+                                               struct netlink_callback *cb)
+{
+       struct devlink_param_item *param_item;
+       struct devlink_port *devlink_port;
+       struct devlink *devlink;
+       int start = cb->args[0];
+       int idx = 0;
+       int err;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       continue;
+               mutex_lock(&devlink->lock);
+               list_for_each_entry(devlink_port, &devlink->port_list, list) {
+                       list_for_each_entry(param_item,
+                                           &devlink_port->param_list, list) {
+                               if (idx < start) {
+                                       idx++;
+                                       continue;
+                               }
+                               err = devlink_nl_param_fill(msg,
+                                               devlink_port->devlink,
+                                               devlink_port->index, param_item,
+                                               DEVLINK_CMD_PORT_PARAM_GET,
+                                               NETLINK_CB(cb->skb).portid,
+                                               cb->nlh->nlmsg_seq,
+                                               NLM_F_MULTI);
+                               if (err) {
+                                       mutex_unlock(&devlink->lock);
+                                       goto out;
+                               }
+                               idx++;
+                       }
+               }
+               mutex_unlock(&devlink->lock);
+       }
+out:
+       mutex_unlock(&devlink_mutex);
+
+       cb->args[0] = idx;
+       return msg->len;
+}
+
+static int devlink_nl_cmd_port_param_get_doit(struct sk_buff *skb,
+                                             struct genl_info *info)
+{
+       struct devlink_port *devlink_port = info->user_ptr[0];
+       struct devlink_param_item *param_item;
+       struct sk_buff *msg;
+       int err;
+
+       param_item = devlink_param_get_from_info(&devlink_port->param_list,
+                                                info);
+       if (!param_item)
+               return -EINVAL;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       err = devlink_nl_param_fill(msg, devlink_port->devlink,
+                                   devlink_port->index, param_item,
+                                   DEVLINK_CMD_PORT_PARAM_GET,
+                                   info->snd_portid, info->snd_seq, 0);
+       if (err) {
+               nlmsg_free(msg);
+               return err;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int devlink_nl_cmd_port_param_set_doit(struct sk_buff *skb,
+                                             struct genl_info *info)
+{
+       struct devlink_port *devlink_port = info->user_ptr[0];
+
+       return __devlink_nl_cmd_param_set_doit(devlink_port->devlink,
+                                              devlink_port->index,
+                                              &devlink_port->param_list, info,
+                                              DEVLINK_CMD_PORT_PARAM_NEW);
+}
+
 static int devlink_nl_region_snapshot_id_put(struct sk_buff *msg,
                                             struct devlink *devlink,
                                             struct devlink_snapshot *snapshot)
@@ -3597,193 +3722,1316 @@ out:
        return 0;
 }
 
-static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
-       [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_PORT_TYPE] = { .type = NLA_U16 },
-       [DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_SB_INDEX] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_SB_POOL_INDEX] = { .type = NLA_U16 },
-       [DEVLINK_ATTR_SB_POOL_TYPE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_SB_POOL_SIZE] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 },
-       [DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 },
-       [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64},
-       [DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64},
-       [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_PARAM_TYPE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_PARAM_VALUE_CMODE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_REGION_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_REGION_SNAPSHOT_ID] = { .type = NLA_U32 },
+struct devlink_info_req {
+       struct sk_buff *msg;
 };
 
-static const struct genl_ops devlink_nl_ops[] = {
-       {
-               .cmd = DEVLINK_CMD_GET,
-               .doit = devlink_nl_cmd_get_doit,
-               .dumpit = devlink_nl_cmd_get_dumpit,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_PORT_GET,
-               .doit = devlink_nl_cmd_port_get_doit,
-               .dumpit = devlink_nl_cmd_port_get_dumpit,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_PORT_SET,
-               .doit = devlink_nl_cmd_port_set_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
-       },
-       {
-               .cmd = DEVLINK_CMD_PORT_SPLIT,
-               .doit = devlink_nl_cmd_port_split_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
-       },
-       {
-               .cmd = DEVLINK_CMD_PORT_UNSPLIT,
-               .doit = devlink_nl_cmd_port_unsplit_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_GET,
-               .doit = devlink_nl_cmd_sb_get_doit,
-               .dumpit = devlink_nl_cmd_sb_get_dumpit,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_POOL_GET,
-               .doit = devlink_nl_cmd_sb_pool_get_doit,
-               .dumpit = devlink_nl_cmd_sb_pool_get_dumpit,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_POOL_SET,
-               .doit = devlink_nl_cmd_sb_pool_set_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_PORT_POOL_GET,
-               .doit = devlink_nl_cmd_sb_port_pool_get_doit,
-               .dumpit = devlink_nl_cmd_sb_port_pool_get_dumpit,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_PORT_POOL_SET,
-               .doit = devlink_nl_cmd_sb_port_pool_set_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_TC_POOL_BIND_GET,
-               .doit = devlink_nl_cmd_sb_tc_pool_bind_get_doit,
-               .dumpit = devlink_nl_cmd_sb_tc_pool_bind_get_dumpit,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_TC_POOL_BIND_SET,
-               .doit = devlink_nl_cmd_sb_tc_pool_bind_set_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_OCC_SNAPSHOT,
-               .doit = devlink_nl_cmd_sb_occ_snapshot_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-       },
-       {
-               .cmd = DEVLINK_CMD_SB_OCC_MAX_CLEAR,
-               .doit = devlink_nl_cmd_sb_occ_max_clear_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
-       },
-       {
-               .cmd = DEVLINK_CMD_ESWITCH_GET,
-               .doit = devlink_nl_cmd_eswitch_get_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
-       },
-       {
-               .cmd = DEVLINK_CMD_ESWITCH_SET,
-               .doit = devlink_nl_cmd_eswitch_set_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
-       },
-       {
-               .cmd = DEVLINK_CMD_DPIPE_TABLE_GET,
-               .doit = devlink_nl_cmd_dpipe_table_get,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_DPIPE_ENTRIES_GET,
-               .doit = devlink_nl_cmd_dpipe_entries_get,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_DPIPE_HEADERS_GET,
-               .doit = devlink_nl_cmd_dpipe_headers_get,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
-               .doit = devlink_nl_cmd_dpipe_table_counters_set,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
-       },
-       {
+int devlink_info_driver_name_put(struct devlink_info_req *req, const char *name)
+{
+       return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME, name);
+}
+EXPORT_SYMBOL_GPL(devlink_info_driver_name_put);
+
+int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
+{
+       return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn);
+}
+EXPORT_SYMBOL_GPL(devlink_info_serial_number_put);
+
+static int devlink_info_version_put(struct devlink_info_req *req, int attr,
+                                   const char *version_name,
+                                   const char *version_value)
+{
+       struct nlattr *nest;
+       int err;
+
+       nest = nla_nest_start(req->msg, attr);
+       if (!nest)
+               return -EMSGSIZE;
+
+       err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_NAME,
+                            version_name);
+       if (err)
+               goto nla_put_failure;
+
+       err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_VALUE,
+                            version_value);
+       if (err)
+               goto nla_put_failure;
+
+       nla_nest_end(req->msg, nest);
+
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(req->msg, nest);
+       return err;
+}
+
+int devlink_info_version_fixed_put(struct devlink_info_req *req,
+                                  const char *version_name,
+                                  const char *version_value)
+{
+       return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_FIXED,
+                                       version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_fixed_put);
+
+int devlink_info_version_stored_put(struct devlink_info_req *req,
+                                   const char *version_name,
+                                   const char *version_value)
+{
+       return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
+                                       version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_stored_put);
+
+int devlink_info_version_running_put(struct devlink_info_req *req,
+                                    const char *version_name,
+                                    const char *version_value)
+{
+       return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
+                                       version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_running_put);
+
+static int
+devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
+                    enum devlink_command cmd, u32 portid,
+                    u32 seq, int flags, struct netlink_ext_ack *extack)
+{
+       struct devlink_info_req req;
+       void *hdr;
+       int err;
+
+       hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       err = -EMSGSIZE;
+       if (devlink_nl_put_handle(msg, devlink))
+               goto err_cancel_msg;
+
+       req.msg = msg;
+       err = devlink->ops->info_get(devlink, &req, extack);
+       if (err)
+               goto err_cancel_msg;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+err_cancel_msg:
+       genlmsg_cancel(msg, hdr);
+       return err;
+}
+
+static int devlink_nl_cmd_info_get_doit(struct sk_buff *skb,
+                                       struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct sk_buff *msg;
+       int err;
+
+       if (!devlink->ops || !devlink->ops->info_get)
+               return -EOPNOTSUPP;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
+                                  info->snd_portid, info->snd_seq, 0,
+                                  info->extack);
+       if (err) {
+               nlmsg_free(msg);
+               return err;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg,
+                                         struct netlink_callback *cb)
+{
+       struct devlink *devlink;
+       int start = cb->args[0];
+       int idx = 0;
+       int err;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       continue;
+               if (idx < start) {
+                       idx++;
+                       continue;
+               }
+
+               mutex_lock(&devlink->lock);
+               err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
+                                          NETLINK_CB(cb->skb).portid,
+                                          cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                          cb->extack);
+               mutex_unlock(&devlink->lock);
+               if (err)
+                       break;
+               idx++;
+       }
+       mutex_unlock(&devlink_mutex);
+
+       cb->args[0] = idx;
+       return msg->len;
+}
+
+struct devlink_fmsg_item {
+       struct list_head list;
+       int attrtype;
+       u8 nla_type;
+       u16 len;
+       int value[0];
+};
+
+struct devlink_fmsg {
+       struct list_head item_list;
+};
+
+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;
+}
+
+static void devlink_fmsg_free(struct devlink_fmsg *fmsg)
+{
+       struct devlink_fmsg_item *item, *tmp;
+
+       list_for_each_entry_safe(item, tmp, &fmsg->item_list, list) {
+               list_del(&item->list);
+               kfree(item);
+       }
+       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)
+{
+       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)
+{
+       return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
+}
+
+int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
+{
+       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 (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;
+
+       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)
+{
+       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;
+
+       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;
+
+       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);
+
+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;
+}
+
+int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
+{
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_bool_put);
+
+int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
+{
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u8_put);
+
+int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
+{
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
+
+int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
+{
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u64_put);
+
+int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
+{
+       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)
+{
+       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, u16 value_len)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_binary_put(fmsg, value, value_len);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_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 i = 0;
+       int err;
+
+       fmsg_nlattr = nla_nest_start(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;
+       }
+       err = genlmsg_reply(skb, info);
+       if (err)
+               return err;
+
+       return 0;
+
+nla_put_failure:
+       nlmsg_free(skb);
+       return err;
+}
+
+struct devlink_health_reporter {
+       struct list_head list;
+       void *priv;
+       const struct devlink_health_reporter_ops *ops;
+       struct devlink *devlink;
+       struct devlink_fmsg *dump_fmsg;
+       struct mutex dump_lock; /* lock parallel read/write from dump buffers */
+       u64 graceful_period;
+       bool auto_recover;
+       u8 health_state;
+       u64 dump_ts;
+       u64 error_count;
+       u64 recovery_count;
+       u64 last_recovery_ts;
+};
+
+enum devlink_health_reporter_state {
+       DEVLINK_HEALTH_REPORTER_STATE_HEALTHY,
+       DEVLINK_HEALTH_REPORTER_STATE_ERROR,
+};
+
+void *
+devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
+{
+       return reporter->priv;
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_priv);
+
+static struct devlink_health_reporter *
+devlink_health_reporter_find_by_name(struct devlink *devlink,
+                                    const char *reporter_name)
+{
+       struct devlink_health_reporter *reporter;
+
+       list_for_each_entry(reporter, &devlink->reporter_list, list)
+               if (!strcmp(reporter->ops->name, reporter_name))
+                       return reporter;
+       return NULL;
+}
+
+/**
+ *     devlink_health_reporter_create - create devlink health reporter
+ *
+ *     @devlink: devlink
+ *     @ops: ops
+ *     @graceful_period: to avoid recovery loops, in msecs
+ *     @auto_recover: auto recover when error occurs
+ *     @priv: priv
+ */
+struct devlink_health_reporter *
+devlink_health_reporter_create(struct devlink *devlink,
+                              const struct devlink_health_reporter_ops *ops,
+                              u64 graceful_period, bool auto_recover,
+                              void *priv)
+{
+       struct devlink_health_reporter *reporter;
+
+       mutex_lock(&devlink->lock);
+       if (devlink_health_reporter_find_by_name(devlink, ops->name)) {
+               reporter = ERR_PTR(-EEXIST);
+               goto unlock;
+       }
+
+       if (WARN_ON(auto_recover && !ops->recover) ||
+           WARN_ON(graceful_period && !ops->recover)) {
+               reporter = ERR_PTR(-EINVAL);
+               goto unlock;
+       }
+
+       reporter = kzalloc(sizeof(*reporter), GFP_KERNEL);
+       if (!reporter) {
+               reporter = ERR_PTR(-ENOMEM);
+               goto unlock;
+       }
+
+       reporter->priv = priv;
+       reporter->ops = ops;
+       reporter->devlink = devlink;
+       reporter->graceful_period = graceful_period;
+       reporter->auto_recover = auto_recover;
+       mutex_init(&reporter->dump_lock);
+       list_add_tail(&reporter->list, &devlink->reporter_list);
+unlock:
+       mutex_unlock(&devlink->lock);
+       return reporter;
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
+
+/**
+ *     devlink_health_reporter_destroy - destroy devlink health reporter
+ *
+ *     @reporter: devlink health reporter to destroy
+ */
+void
+devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
+{
+       mutex_lock(&reporter->devlink->lock);
+       list_del(&reporter->list);
+       mutex_unlock(&reporter->devlink->lock);
+       if (reporter->dump_fmsg)
+               devlink_fmsg_free(reporter->dump_fmsg);
+       kfree(reporter);
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
+
+static int
+devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
+                               void *priv_ctx)
+{
+       int err;
+
+       if (!reporter->ops->recover)
+               return -EOPNOTSUPP;
+
+       err = reporter->ops->recover(reporter, priv_ctx);
+       if (err)
+               return err;
+
+       reporter->recovery_count++;
+       reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
+       reporter->last_recovery_ts = jiffies;
+
+       return 0;
+}
+
+static void
+devlink_health_dump_clear(struct devlink_health_reporter *reporter)
+{
+       if (!reporter->dump_fmsg)
+               return;
+       devlink_fmsg_free(reporter->dump_fmsg);
+       reporter->dump_fmsg = NULL;
+}
+
+static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
+                                 void *priv_ctx)
+{
+       int err;
+
+       if (!reporter->ops->dump)
+               return 0;
+
+       if (reporter->dump_fmsg)
+               return 0;
+
+       reporter->dump_fmsg = devlink_fmsg_alloc();
+       if (!reporter->dump_fmsg) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       err = devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
+       if (err)
+               goto dump_err;
+
+       err = reporter->ops->dump(reporter, reporter->dump_fmsg,
+                                 priv_ctx);
+       if (err)
+               goto dump_err;
+
+       err = devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
+       if (err)
+               goto dump_err;
+
+       reporter->dump_ts = jiffies;
+
+       return 0;
+
+dump_err:
+       devlink_health_dump_clear(reporter);
+       return err;
+}
+
+int devlink_health_report(struct devlink_health_reporter *reporter,
+                         const char *msg, void *priv_ctx)
+{
+       struct devlink *devlink = reporter->devlink;
+
+       /* write a log message of the current error */
+       WARN_ON(!msg);
+       trace_devlink_health_report(devlink, reporter->ops->name, msg);
+       reporter->error_count++;
+
+       /* abort if the previous error wasn't recovered */
+       if (reporter->auto_recover &&
+           (reporter->health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
+            jiffies - reporter->last_recovery_ts <
+            msecs_to_jiffies(reporter->graceful_period))) {
+               trace_devlink_health_recover_aborted(devlink,
+                                                    reporter->ops->name,
+                                                    reporter->health_state,
+                                                    jiffies -
+                                                    reporter->last_recovery_ts);
+               return -ECANCELED;
+       }
+
+       reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
+
+       mutex_lock(&reporter->dump_lock);
+       /* store current dump of current error, for later analysis */
+       devlink_health_do_dump(reporter, priv_ctx);
+       mutex_unlock(&reporter->dump_lock);
+
+       if (reporter->auto_recover)
+               return devlink_health_reporter_recover(reporter, priv_ctx);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_health_report);
+
+static struct devlink_health_reporter *
+devlink_health_reporter_get_from_info(struct devlink *devlink,
+                                     struct genl_info *info)
+{
+       char *reporter_name;
+
+       if (!info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
+               return NULL;
+
+       reporter_name =
+               nla_data(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
+       return devlink_health_reporter_find_by_name(devlink, reporter_name);
+}
+
+static int
+devlink_nl_health_reporter_fill(struct sk_buff *msg,
+                               struct devlink *devlink,
+                               struct devlink_health_reporter *reporter,
+                               enum devlink_command cmd, u32 portid,
+                               u32 seq, int flags)
+{
+       struct nlattr *reporter_attr;
+       void *hdr;
+
+       hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       if (devlink_nl_put_handle(msg, devlink))
+               goto genlmsg_cancel;
+
+       reporter_attr = nla_nest_start(msg, DEVLINK_ATTR_HEALTH_REPORTER);
+       if (!reporter_attr)
+               goto genlmsg_cancel;
+       if (nla_put_string(msg, DEVLINK_ATTR_HEALTH_REPORTER_NAME,
+                          reporter->ops->name))
+               goto reporter_nest_cancel;
+       if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_STATE,
+                      reporter->health_state))
+               goto reporter_nest_cancel;
+       if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR,
+                             reporter->error_count, DEVLINK_ATTR_PAD))
+               goto reporter_nest_cancel;
+       if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER,
+                             reporter->recovery_count, DEVLINK_ATTR_PAD))
+               goto reporter_nest_cancel;
+       if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
+                             reporter->graceful_period,
+                             DEVLINK_ATTR_PAD))
+               goto reporter_nest_cancel;
+       if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
+                      reporter->auto_recover))
+               goto reporter_nest_cancel;
+       if (reporter->dump_fmsg &&
+           nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
+                             jiffies_to_msecs(reporter->dump_ts),
+                             DEVLINK_ATTR_PAD))
+               goto reporter_nest_cancel;
+
+       nla_nest_end(msg, reporter_attr);
+       genlmsg_end(msg, hdr);
+       return 0;
+
+reporter_nest_cancel:
+       nla_nest_end(msg, reporter_attr);
+genlmsg_cancel:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int devlink_nl_cmd_health_reporter_get_doit(struct sk_buff *skb,
+                                                  struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+       struct sk_buff *msg;
+       int err;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       err = devlink_nl_health_reporter_fill(msg, devlink, reporter,
+                                             DEVLINK_CMD_HEALTH_REPORTER_GET,
+                                             info->snd_portid, info->snd_seq,
+                                             0);
+       if (err) {
+               nlmsg_free(msg);
+               return err;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int
+devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
+                                         struct netlink_callback *cb)
+{
+       struct devlink_health_reporter *reporter;
+       struct devlink *devlink;
+       int start = cb->args[0];
+       int idx = 0;
+       int err;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       continue;
+               mutex_lock(&devlink->lock);
+               list_for_each_entry(reporter, &devlink->reporter_list,
+                                   list) {
+                       if (idx < start) {
+                               idx++;
+                               continue;
+                       }
+                       err = devlink_nl_health_reporter_fill(msg, devlink,
+                                                             reporter,
+                                                             DEVLINK_CMD_HEALTH_REPORTER_GET,
+                                                             NETLINK_CB(cb->skb).portid,
+                                                             cb->nlh->nlmsg_seq,
+                                                             NLM_F_MULTI);
+                       if (err) {
+                               mutex_unlock(&devlink->lock);
+                               goto out;
+                       }
+                       idx++;
+               }
+               mutex_unlock(&devlink->lock);
+       }
+out:
+       mutex_unlock(&devlink_mutex);
+
+       cb->args[0] = idx;
+       return msg->len;
+}
+
+static int
+devlink_nl_cmd_health_reporter_set_doit(struct sk_buff *skb,
+                                       struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       if (!reporter->ops->recover &&
+           (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
+            info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
+               return -EOPNOTSUPP;
+
+       if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
+               reporter->graceful_period =
+                       nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
+
+       if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
+               reporter->auto_recover =
+                       nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
+
+       return 0;
+}
+
+static int devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff *skb,
+                                                      struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       return devlink_health_reporter_recover(reporter, NULL);
+}
+
+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);
+       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_doit(struct sk_buff *skb,
+                                                       struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+       int err;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       if (!reporter->ops->dump)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&reporter->dump_lock);
+       err = devlink_health_do_dump(reporter, NULL);
+       if (err)
+               goto out;
+
+       err = devlink_fmsg_snd(reporter->dump_fmsg, info,
+                              DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET, 0);
+
+out:
+       mutex_unlock(&reporter->dump_lock);
+       return err;
+}
+
+static int
+devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb,
+                                              struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       if (!reporter->ops->dump)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&reporter->dump_lock);
+       devlink_health_dump_clear(reporter);
+       mutex_unlock(&reporter->dump_lock);
+       return 0;
+}
+
+static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
+       [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_PORT_TYPE] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_SB_INDEX] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_SB_POOL_INDEX] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_SB_POOL_TYPE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_SB_POOL_SIZE] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64},
+       [DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64},
+       [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_PARAM_TYPE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_PARAM_VALUE_CMODE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_REGION_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_REGION_SNAPSHOT_ID] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_HEALTH_REPORTER_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = { .type = NLA_U64 },
+       [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 },
+};
+
+static const struct genl_ops devlink_nl_ops[] = {
+       {
+               .cmd = DEVLINK_CMD_GET,
+               .doit = devlink_nl_cmd_get_doit,
+               .dumpit = devlink_nl_cmd_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_GET,
+               .doit = devlink_nl_cmd_port_get_doit,
+               .dumpit = devlink_nl_cmd_port_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_SET,
+               .doit = devlink_nl_cmd_port_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_SPLIT,
+               .doit = devlink_nl_cmd_port_split_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_UNSPLIT,
+               .doit = devlink_nl_cmd_port_unsplit_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_GET,
+               .doit = devlink_nl_cmd_sb_get_doit,
+               .dumpit = devlink_nl_cmd_sb_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_POOL_GET,
+               .doit = devlink_nl_cmd_sb_pool_get_doit,
+               .dumpit = devlink_nl_cmd_sb_pool_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_POOL_SET,
+               .doit = devlink_nl_cmd_sb_pool_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_PORT_POOL_GET,
+               .doit = devlink_nl_cmd_sb_port_pool_get_doit,
+               .dumpit = devlink_nl_cmd_sb_port_pool_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_PORT_POOL_SET,
+               .doit = devlink_nl_cmd_sb_port_pool_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_TC_POOL_BIND_GET,
+               .doit = devlink_nl_cmd_sb_tc_pool_bind_get_doit,
+               .dumpit = devlink_nl_cmd_sb_tc_pool_bind_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_TC_POOL_BIND_SET,
+               .doit = devlink_nl_cmd_sb_tc_pool_bind_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_OCC_SNAPSHOT,
+               .doit = devlink_nl_cmd_sb_occ_snapshot_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+       },
+       {
+               .cmd = DEVLINK_CMD_SB_OCC_MAX_CLEAR,
+               .doit = devlink_nl_cmd_sb_occ_max_clear_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NEED_SB,
+       },
+       {
+               .cmd = DEVLINK_CMD_ESWITCH_GET,
+               .doit = devlink_nl_cmd_eswitch_get_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_ESWITCH_SET,
+               .doit = devlink_nl_cmd_eswitch_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
+       },
+       {
+               .cmd = DEVLINK_CMD_DPIPE_TABLE_GET,
+               .doit = devlink_nl_cmd_dpipe_table_get,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_DPIPE_ENTRIES_GET,
+               .doit = devlink_nl_cmd_dpipe_entries_get,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_DPIPE_HEADERS_GET,
+               .doit = devlink_nl_cmd_dpipe_headers_get,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
+               .doit = devlink_nl_cmd_dpipe_table_counters_set,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
                .cmd = DEVLINK_CMD_RESOURCE_SET,
                .doit = devlink_nl_cmd_resource_set,
                .policy = devlink_nl_policy,
@@ -3791,56 +5039,124 @@ static const struct genl_ops devlink_nl_ops[] = {
                .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
-               .cmd = DEVLINK_CMD_RESOURCE_DUMP,
-               .doit = devlink_nl_cmd_resource_dump,
+               .cmd = DEVLINK_CMD_RESOURCE_DUMP,
+               .doit = devlink_nl_cmd_resource_dump,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_RELOAD,
+               .doit = devlink_nl_cmd_reload,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
+       },
+       {
+               .cmd = DEVLINK_CMD_PARAM_GET,
+               .doit = devlink_nl_cmd_param_get_doit,
+               .dumpit = devlink_nl_cmd_param_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_PARAM_SET,
+               .doit = devlink_nl_cmd_param_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_PARAM_GET,
+               .doit = devlink_nl_cmd_port_param_get_doit,
+               .dumpit = devlink_nl_cmd_port_param_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_PARAM_SET,
+               .doit = devlink_nl_cmd_port_param_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+       },
+       {
+               .cmd = DEVLINK_CMD_REGION_GET,
+               .doit = devlink_nl_cmd_region_get_doit,
+               .dumpit = devlink_nl_cmd_region_get_dumpit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_REGION_DEL,
+               .doit = devlink_nl_cmd_region_del,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_REGION_READ,
+               .dumpit = devlink_nl_cmd_region_read_dumpit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_INFO_GET,
+               .doit = devlink_nl_cmd_info_get_doit,
+               .dumpit = devlink_nl_cmd_info_get_dumpit,
                .policy = devlink_nl_policy,
                .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
-               .cmd = DEVLINK_CMD_RELOAD,
-               .doit = devlink_nl_cmd_reload,
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_GET,
+               .doit = devlink_nl_cmd_health_reporter_get_doit,
+               .dumpit = devlink_nl_cmd_health_reporter_get_dumpit,
                .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
        },
        {
-               .cmd = DEVLINK_CMD_PARAM_GET,
-               .doit = devlink_nl_cmd_param_get_doit,
-               .dumpit = devlink_nl_cmd_param_get_dumpit,
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_SET,
+               .doit = devlink_nl_cmd_health_reporter_set_doit,
                .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
                .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
-               /* can be retrieved by unprivileged users */
        },
        {
-               .cmd = DEVLINK_CMD_PARAM_SET,
-               .doit = devlink_nl_cmd_param_set_doit,
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_RECOVER,
+               .doit = devlink_nl_cmd_health_reporter_recover_doit,
                .policy = devlink_nl_policy,
                .flags = GENL_ADMIN_PERM,
                .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
-               .cmd = DEVLINK_CMD_REGION_GET,
-               .doit = devlink_nl_cmd_region_get_doit,
-               .dumpit = devlink_nl_cmd_region_get_dumpit,
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
+               .doit = devlink_nl_cmd_health_reporter_diagnose_doit,
                .policy = devlink_nl_policy,
                .flags = GENL_ADMIN_PERM,
                .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
-               .cmd = DEVLINK_CMD_REGION_DEL,
-               .doit = devlink_nl_cmd_region_del,
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
+               .doit = devlink_nl_cmd_health_reporter_dump_get_doit,
                .policy = devlink_nl_policy,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
-               .cmd = DEVLINK_CMD_REGION_READ,
-               .dumpit = devlink_nl_cmd_region_read_dumpit,
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
+               .doit = devlink_nl_cmd_health_reporter_dump_clear_doit,
                .policy = devlink_nl_policy,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
        },
 };
 
@@ -3882,6 +5198,7 @@ struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
        INIT_LIST_HEAD(&devlink->resource_list);
        INIT_LIST_HEAD(&devlink->param_list);
        INIT_LIST_HEAD(&devlink->region_list);
+       INIT_LIST_HEAD(&devlink->reporter_list);
        mutex_init(&devlink->lock);
        return devlink;
 }
@@ -3924,6 +5241,14 @@ EXPORT_SYMBOL_GPL(devlink_unregister);
  */
 void devlink_free(struct devlink *devlink)
 {
+       WARN_ON(!list_empty(&devlink->reporter_list));
+       WARN_ON(!list_empty(&devlink->region_list));
+       WARN_ON(!list_empty(&devlink->param_list));
+       WARN_ON(!list_empty(&devlink->resource_list));
+       WARN_ON(!list_empty(&devlink->dpipe_table_list));
+       WARN_ON(!list_empty(&devlink->sb_list));
+       WARN_ON(!list_empty(&devlink->port_list));
+
        kfree(devlink);
 }
 EXPORT_SYMBOL_GPL(devlink_free);
@@ -3954,6 +5279,7 @@ int devlink_port_register(struct devlink *devlink,
        devlink_port->index = port_index;
        devlink_port->registered = true;
        list_add_tail(&devlink_port->list, &devlink->port_list);
+       INIT_LIST_HEAD(&devlink_port->param_list);
        mutex_unlock(&devlink->lock);
        devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
        return 0;
@@ -4471,18 +5797,23 @@ out:
 }
 EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
 
-/**
- *     devlink_params_register - register configuration parameters
- *
- *     @devlink: devlink
- *     @params: configuration parameters array
- *     @params_count: number of parameters provided
- *
- *     Register the configuration parameters supported by the driver.
- */
-int devlink_params_register(struct devlink *devlink,
-                           const struct devlink_param *params,
-                           size_t params_count)
+static int devlink_param_verify(const struct devlink_param *param)
+{
+       if (!param || !param->name || !param->supported_cmodes)
+               return -EINVAL;
+       if (param->generic)
+               return devlink_param_generic_verify(param);
+       else
+               return devlink_param_driver_verify(param);
+}
+
+static int __devlink_params_register(struct devlink *devlink,
+                                    unsigned int port_index,
+                                    struct list_head *param_list,
+                                    const struct devlink_param *params,
+                                    size_t params_count,
+                                    enum devlink_command reg_cmd,
+                                    enum devlink_command unreg_cmd)
 {
        const struct devlink_param *param = params;
        int i;
@@ -4490,20 +5821,12 @@ int devlink_params_register(struct devlink *devlink,
 
        mutex_lock(&devlink->lock);
        for (i = 0; i < params_count; i++, param++) {
-               if (!param || !param->name || !param->supported_cmodes) {
-                       err = -EINVAL;
+               err = devlink_param_verify(param);
+               if (err)
                        goto rollback;
-               }
-               if (param->generic) {
-                       err = devlink_param_generic_verify(param);
-                       if (err)
-                               goto rollback;
-               } else {
-                       err = devlink_param_driver_verify(param);
-                       if (err)
-                               goto rollback;
-               }
-               err = devlink_param_register_one(devlink, param);
+
+               err = devlink_param_register_one(devlink, port_index,
+                                                param_list, param, reg_cmd);
                if (err)
                        goto rollback;
        }
@@ -4515,11 +5838,48 @@ rollback:
        if (!i)
                goto unlock;
        for (param--; i > 0; i--, param--)
-               devlink_param_unregister_one(devlink, param);
+               devlink_param_unregister_one(devlink, port_index, param_list,
+                                            param, unreg_cmd);
 unlock:
        mutex_unlock(&devlink->lock);
        return err;
 }
+
+static void __devlink_params_unregister(struct devlink *devlink,
+                                       unsigned int port_index,
+                                       struct list_head *param_list,
+                                       const struct devlink_param *params,
+                                       size_t params_count,
+                                       enum devlink_command cmd)
+{
+       const struct devlink_param *param = params;
+       int i;
+
+       mutex_lock(&devlink->lock);
+       for (i = 0; i < params_count; i++, param++)
+               devlink_param_unregister_one(devlink, 0, param_list, param,
+                                            cmd);
+       mutex_unlock(&devlink->lock);
+}
+
+/**
+ *     devlink_params_register - register configuration parameters
+ *
+ *     @devlink: devlink
+ *     @params: configuration parameters array
+ *     @params_count: number of parameters provided
+ *
+ *     Register the configuration parameters supported by the driver.
+ */
+int devlink_params_register(struct devlink *devlink,
+                           const struct devlink_param *params,
+                           size_t params_count)
+{
+       return __devlink_params_register(devlink, 0, &devlink->param_list,
+                                        params, params_count,
+                                        DEVLINK_CMD_PARAM_NEW,
+                                        DEVLINK_CMD_PARAM_DEL);
+}
 EXPORT_SYMBOL_GPL(devlink_params_register);
 
 /**
@@ -4532,36 +5892,103 @@ void devlink_params_unregister(struct devlink *devlink,
                               const struct devlink_param *params,
                               size_t params_count)
 {
-       const struct devlink_param *param = params;
-       int i;
-
-       mutex_lock(&devlink->lock);
-       for (i = 0; i < params_count; i++, param++)
-               devlink_param_unregister_one(devlink, param);
-       mutex_unlock(&devlink->lock);
+       return __devlink_params_unregister(devlink, 0, &devlink->param_list,
+                                          params, params_count,
+                                          DEVLINK_CMD_PARAM_DEL);
 }
 EXPORT_SYMBOL_GPL(devlink_params_unregister);
 
 /**
- *     devlink_param_driverinit_value_get - get configuration parameter
- *                                          value for driver initializing
+ *     devlink_params_publish - publish configuration parameters
  *
  *     @devlink: devlink
- *     @param_id: parameter ID
- *     @init_val: value of parameter in driverinit configuration mode
  *
- *     This function should be used by the driver to get driverinit
- *     configuration for initialization after reload command.
+ *     Publish previously registered configuration parameters.
  */
-int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
-                                      union devlink_param_value *init_val)
+void devlink_params_publish(struct devlink *devlink)
 {
        struct devlink_param_item *param_item;
 
-       if (!devlink->ops || !devlink->ops->reload)
-               return -EOPNOTSUPP;
+       list_for_each_entry(param_item, &devlink->param_list, list) {
+               if (param_item->published)
+                       continue;
+               param_item->published = true;
+               devlink_param_notify(devlink, 0, param_item,
+                                    DEVLINK_CMD_PARAM_NEW);
+       }
+}
+EXPORT_SYMBOL_GPL(devlink_params_publish);
 
-       param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
+/**
+ *     devlink_params_unpublish - unpublish configuration parameters
+ *
+ *     @devlink: devlink
+ *
+ *     Unpublish previously registered configuration parameters.
+ */
+void devlink_params_unpublish(struct devlink *devlink)
+{
+       struct devlink_param_item *param_item;
+
+       list_for_each_entry(param_item, &devlink->param_list, list) {
+               if (!param_item->published)
+                       continue;
+               param_item->published = false;
+               devlink_param_notify(devlink, 0, param_item,
+                                    DEVLINK_CMD_PARAM_DEL);
+       }
+}
+EXPORT_SYMBOL_GPL(devlink_params_unpublish);
+
+/**
+ *     devlink_port_params_register - register port configuration parameters
+ *
+ *     @devlink_port: devlink port
+ *     @params: configuration parameters array
+ *     @params_count: number of parameters provided
+ *
+ *     Register the configuration parameters supported by the port.
+ */
+int devlink_port_params_register(struct devlink_port *devlink_port,
+                                const struct devlink_param *params,
+                                size_t params_count)
+{
+       return __devlink_params_register(devlink_port->devlink,
+                                        devlink_port->index,
+                                        &devlink_port->param_list, params,
+                                        params_count,
+                                        DEVLINK_CMD_PORT_PARAM_NEW,
+                                        DEVLINK_CMD_PORT_PARAM_DEL);
+}
+EXPORT_SYMBOL_GPL(devlink_port_params_register);
+
+/**
+ *     devlink_port_params_unregister - unregister port configuration
+ *     parameters
+ *
+ *     @devlink_port: devlink port
+ *     @params: configuration parameters array
+ *     @params_count: number of parameters provided
+ */
+void devlink_port_params_unregister(struct devlink_port *devlink_port,
+                                   const struct devlink_param *params,
+                                   size_t params_count)
+{
+       return __devlink_params_unregister(devlink_port->devlink,
+                                          devlink_port->index,
+                                          &devlink_port->param_list,
+                                          params, params_count,
+                                          DEVLINK_CMD_PORT_PARAM_DEL);
+}
+EXPORT_SYMBOL_GPL(devlink_port_params_unregister);
+
+static int
+__devlink_param_driverinit_value_get(struct list_head *param_list, u32 param_id,
+                                    union devlink_param_value *init_val)
+{
+       struct devlink_param_item *param_item;
+
+       param_item = devlink_param_find_by_id(param_list, param_id);
        if (!param_item)
                return -EINVAL;
 
@@ -4577,6 +6004,54 @@ int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
 
        return 0;
 }
+
+static int
+__devlink_param_driverinit_value_set(struct devlink *devlink,
+                                    unsigned int port_index,
+                                    struct list_head *param_list, u32 param_id,
+                                    union devlink_param_value init_val,
+                                    enum devlink_command cmd)
+{
+       struct devlink_param_item *param_item;
+
+       param_item = devlink_param_find_by_id(param_list, param_id);
+       if (!param_item)
+               return -EINVAL;
+
+       if (!devlink_param_cmode_is_supported(param_item->param,
+                                             DEVLINK_PARAM_CMODE_DRIVERINIT))
+               return -EOPNOTSUPP;
+
+       if (param_item->param->type == DEVLINK_PARAM_TYPE_STRING)
+               strcpy(param_item->driverinit_value.vstr, init_val.vstr);
+       else
+               param_item->driverinit_value = init_val;
+       param_item->driverinit_value_valid = true;
+
+       devlink_param_notify(devlink, port_index, param_item, cmd);
+       return 0;
+}
+
+/**
+ *     devlink_param_driverinit_value_get - get configuration parameter
+ *                                          value for driver initializing
+ *
+ *     @devlink: devlink
+ *     @param_id: parameter ID
+ *     @init_val: value of parameter in driverinit configuration mode
+ *
+ *     This function should be used by the driver to get driverinit
+ *     configuration for initialization after reload command.
+ */
+int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
+                                      union devlink_param_value *init_val)
+{
+       if (!devlink->ops || !devlink->ops->reload)
+               return -EOPNOTSUPP;
+
+       return __devlink_param_driverinit_value_get(&devlink->param_list,
+                                                   param_id, init_val);
+}
 EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_get);
 
 /**
@@ -4594,26 +6069,61 @@ EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_get);
 int devlink_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
                                       union devlink_param_value init_val)
 {
-       struct devlink_param_item *param_item;
+       return __devlink_param_driverinit_value_set(devlink, 0,
+                                                   &devlink->param_list,
+                                                   param_id, init_val,
+                                                   DEVLINK_CMD_PARAM_NEW);
+}
+EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
 
-       param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
-       if (!param_item)
-               return -EINVAL;
+/**
+ *     devlink_port_param_driverinit_value_get - get configuration parameter
+ *                                             value for driver initializing
+ *
+ *     @devlink_port: devlink_port
+ *     @param_id: parameter ID
+ *     @init_val: value of parameter in driverinit configuration mode
+ *
+ *     This function should be used by the driver to get driverinit
+ *     configuration for initialization after reload command.
+ */
+int devlink_port_param_driverinit_value_get(struct devlink_port *devlink_port,
+                                           u32 param_id,
+                                           union devlink_param_value *init_val)
+{
+       struct devlink *devlink = devlink_port->devlink;
 
-       if (!devlink_param_cmode_is_supported(param_item->param,
-                                             DEVLINK_PARAM_CMODE_DRIVERINIT))
+       if (!devlink->ops || !devlink->ops->reload)
                return -EOPNOTSUPP;
 
-       if (param_item->param->type == DEVLINK_PARAM_TYPE_STRING)
-               strcpy(param_item->driverinit_value.vstr, init_val.vstr);
-       else
-               param_item->driverinit_value = init_val;
-       param_item->driverinit_value_valid = true;
+       return __devlink_param_driverinit_value_get(&devlink_port->param_list,
+                                                   param_id, init_val);
+}
+EXPORT_SYMBOL_GPL(devlink_port_param_driverinit_value_get);
 
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
-       return 0;
+/**
+ *     devlink_port_param_driverinit_value_set - set value of configuration
+ *                                               parameter for driverinit
+ *                                               configuration mode
+ *
+ *     @devlink_port: devlink_port
+ *     @param_id: parameter ID
+ *     @init_val: value of parameter to set for driverinit configuration mode
+ *
+ *     This function should be used by the driver to set driverinit
+ *     configuration mode default value.
+ */
+int devlink_port_param_driverinit_value_set(struct devlink_port *devlink_port,
+                                           u32 param_id,
+                                           union devlink_param_value init_val)
+{
+       return __devlink_param_driverinit_value_set(devlink_port->devlink,
+                                                   devlink_port->index,
+                                                   &devlink_port->param_list,
+                                                   param_id, init_val,
+                                                   DEVLINK_CMD_PORT_PARAM_NEW);
 }
-EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
+EXPORT_SYMBOL_GPL(devlink_port_param_driverinit_value_set);
 
 /**
  *     devlink_param_value_changed - notify devlink on a parameter's value
@@ -4626,7 +6136,6 @@ EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
  *     This function should be used by the driver to notify devlink on value
  *     change, excluding driverinit configuration mode.
  *     For driverinit configuration mode driver should use the function
- *     devlink_param_driverinit_value_set() instead.
  */
 void devlink_param_value_changed(struct devlink *devlink, u32 param_id)
 {
@@ -4635,10 +6144,37 @@ void devlink_param_value_changed(struct devlink *devlink, u32 param_id)
        param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
        WARN_ON(!param_item);
 
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+       devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
 }
 EXPORT_SYMBOL_GPL(devlink_param_value_changed);
 
+/**
+ *     devlink_port_param_value_changed - notify devlink on a parameter's value
+ *                                      change. Should be called by the driver
+ *                                      right after the change.
+ *
+ *     @devlink_port: devlink_port
+ *     @param_id: parameter ID
+ *
+ *     This function should be used by the driver to notify devlink on value
+ *     change, excluding driverinit configuration mode.
+ *     For driverinit configuration mode driver should use the function
+ *     devlink_port_param_driverinit_value_set() instead.
+ */
+void devlink_port_param_value_changed(struct devlink_port *devlink_port,
+                                     u32 param_id)
+{
+       struct devlink_param_item *param_item;
+
+       param_item = devlink_param_find_by_id(&devlink_port->param_list,
+                                             param_id);
+       WARN_ON(!param_item);
+
+       devlink_param_notify(devlink_port->devlink, devlink_port->index,
+                            param_item, DEVLINK_CMD_PORT_PARAM_NEW);
+}
+EXPORT_SYMBOL_GPL(devlink_port_param_value_changed);
+
 /**
  *     devlink_param_value_str_fill - Safely fill-up the string preventing
  *                                    from overflow of the preallocated buffer
@@ -4808,6 +6344,69 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(devlink_region_snapshot_create);
 
+static void __devlink_compat_running_version(struct devlink *devlink,
+                                            char *buf, size_t len)
+{
+       const struct nlattr *nlattr;
+       struct devlink_info_req req;
+       struct sk_buff *msg;
+       int rem, err;
+
+       if (!devlink->ops->info_get)
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       req.msg = msg;
+       err = devlink->ops->info_get(devlink, &req, NULL);
+       if (err)
+               goto free_msg;
+
+       nla_for_each_attr(nlattr, (void *)msg->data, msg->len, rem) {
+               const struct nlattr *kv;
+               int rem_kv;
+
+               if (nla_type(nlattr) != DEVLINK_ATTR_INFO_VERSION_RUNNING)
+                       continue;
+
+               nla_for_each_nested(kv, nlattr, rem_kv) {
+                       if (nla_type(kv) != DEVLINK_ATTR_INFO_VERSION_VALUE)
+                               continue;
+
+                       strlcat(buf, nla_data(kv), len);
+                       strlcat(buf, " ", len);
+               }
+       }
+free_msg:
+       nlmsg_free(msg);
+}
+
+void devlink_compat_running_version(struct net_device *dev,
+                                   char *buf, size_t len)
+{
+       struct devlink_port *devlink_port;
+       struct devlink *devlink;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               mutex_lock(&devlink->lock);
+               list_for_each_entry(devlink_port, &devlink->port_list, list) {
+                       if (devlink_port->type == DEVLINK_PORT_TYPE_ETH &&
+                           devlink_port->type_dev == dev) {
+                               __devlink_compat_running_version(devlink,
+                                                                buf, len);
+                               mutex_unlock(&devlink->lock);
+                               goto out;
+                       }
+               }
+               mutex_unlock(&devlink->lock);
+       }
+out:
+       mutex_unlock(&devlink_mutex);
+}
+
 static int __init devlink_module_init(void)
 {
        return genl_register_family(&devlink_nl_family);