]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - net/core/devlink.c
devlink: Always use user_ptr[0] for devlink and simplify post_doit
[mirror_ubuntu-hirsute-kernel.git] / net / core / devlink.c
index 05197631d52ae54b03ec79fbb453a4f75c8b1853..0ca89196a3678bb036f86af200b1de3642644444 100644 (file)
@@ -85,6 +85,10 @@ EXPORT_SYMBOL(devlink_dpipe_header_ipv6);
 EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
 EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);
 
+static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {
+       [DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY },
+};
+
 static LIST_HEAD(devlink_list);
 
 /* devlink_mutex
@@ -382,19 +386,19 @@ devlink_region_snapshot_get_by_id(struct devlink_region *region, u32 id)
        return NULL;
 }
 
-#define DEVLINK_NL_FLAG_NEED_DEVLINK   BIT(0)
-#define DEVLINK_NL_FLAG_NEED_PORT      BIT(1)
-#define DEVLINK_NL_FLAG_NEED_SB                BIT(2)
+#define DEVLINK_NL_FLAG_NEED_PORT              BIT(0)
+#define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT   BIT(1)
 
 /* The per devlink instance lock is taken by default in the pre-doit
  * operation, yet several commands do not require this. The global
  * devlink lock is taken and protects from disruption by user-calls.
  */
-#define DEVLINK_NL_FLAG_NO_LOCK                BIT(3)
+#define DEVLINK_NL_FLAG_NO_LOCK                        BIT(2)
 
 static int devlink_nl_pre_doit(const struct genl_ops *ops,
                               struct sk_buff *skb, struct genl_info *info)
 {
+       struct devlink_port *devlink_port;
        struct devlink *devlink;
        int err;
 
@@ -406,27 +410,18 @@ static int devlink_nl_pre_doit(const struct genl_ops *ops,
        }
        if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
                mutex_lock(&devlink->lock);
-       if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK) {
-               info->user_ptr[0] = devlink;
-       } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) {
-               struct devlink_port *devlink_port;
-
+       info->user_ptr[0] = devlink;
+       if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) {
                devlink_port = devlink_port_get_from_info(devlink, info);
                if (IS_ERR(devlink_port)) {
                        err = PTR_ERR(devlink_port);
                        goto unlock;
                }
-               info->user_ptr[0] = devlink_port;
-       }
-       if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_SB) {
-               struct devlink_sb *devlink_sb;
-
-               devlink_sb = devlink_sb_get_from_info(devlink, info);
-               if (IS_ERR(devlink_sb)) {
-                       err = PTR_ERR(devlink_sb);
-                       goto unlock;
-               }
-               info->user_ptr[1] = devlink_sb;
+               info->user_ptr[1] = devlink_port;
+       } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) {
+               devlink_port = devlink_port_get_from_info(devlink, info);
+               if (!IS_ERR(devlink_port))
+                       info->user_ptr[1] = devlink_port;
        }
        return 0;
 
@@ -442,16 +437,8 @@ static void devlink_nl_post_doit(const struct genl_ops *ops,
 {
        struct devlink *devlink;
 
-       /* When devlink changes netns, it would not be found
-        * by devlink_get_from_info(). So try if it is stored first.
-        */
-       if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK) {
-               devlink = info->user_ptr[0];
-       } else {
-               devlink = devlink_get_from_info(info);
-               WARN_ON(IS_ERR(devlink));
-       }
-       if (!IS_ERR(devlink) && ~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
+       devlink = info->user_ptr[0];
+       if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
                mutex_unlock(&devlink->lock);
        mutex_unlock(&devlink_mutex);
 }
@@ -524,8 +511,14 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
 {
        struct devlink_port_attrs *attrs = &devlink_port->attrs;
 
-       if (!attrs->set)
+       if (!devlink_port->attrs_set)
                return 0;
+       if (attrs->lanes) {
+               if (nla_put_u32(msg, DEVLINK_ATTR_PORT_LANES, attrs->lanes))
+                       return -EMSGSIZE;
+       }
+       if (nla_put_u8(msg, DEVLINK_ATTR_PORT_SPLITTABLE, attrs->splittable))
+               return -EMSGSIZE;
        if (nla_put_u16(msg, DEVLINK_ATTR_PORT_FLAVOUR, attrs->flavour))
                return -EMSGSIZE;
        switch (devlink_port->attrs.flavour) {
@@ -563,6 +556,49 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
        return 0;
 }
 
+static int
+devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *port,
+                                  struct netlink_ext_ack *extack)
+{
+       struct devlink *devlink = port->devlink;
+       const struct devlink_ops *ops;
+       struct nlattr *function_attr;
+       bool empty_nest = true;
+       int err = 0;
+
+       function_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PORT_FUNCTION);
+       if (!function_attr)
+               return -EMSGSIZE;
+
+       ops = devlink->ops;
+       if (ops->port_function_hw_addr_get) {
+               int hw_addr_len;
+               u8 hw_addr[MAX_ADDR_LEN];
+
+               err = ops->port_function_hw_addr_get(devlink, port, hw_addr, &hw_addr_len, extack);
+               if (err == -EOPNOTSUPP) {
+                       /* Port function attributes are optional for a port. If port doesn't
+                        * support function attribute, returning -EOPNOTSUPP is not an error.
+                        */
+                       err = 0;
+                       goto out;
+               } else if (err) {
+                       goto out;
+               }
+               err = nla_put(msg, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, hw_addr_len, hw_addr);
+               if (err)
+                       goto out;
+               empty_nest = false;
+       }
+
+out:
+       if (err || empty_nest)
+               nla_nest_cancel(msg, function_attr);
+       else
+               nla_nest_end(msg, function_attr);
+       return err;
+}
+
 static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
                                struct devlink_port *devlink_port,
                                enum devlink_command cmd, u32 portid,
@@ -608,6 +644,8 @@ static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
        spin_unlock_bh(&devlink_port->type_lock);
        if (devlink_nl_port_attrs_put(msg, devlink_port))
                goto nla_put_failure;
+       if (devlink_nl_port_function_attrs_put(msg, devlink_port, extack))
+               goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
        return 0;
@@ -699,7 +737,7 @@ out:
 static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb,
                                        struct genl_info *info)
 {
-       struct devlink_port *devlink_port = info->user_ptr[0];
+       struct devlink_port *devlink_port = info->user_ptr[1];
        struct devlink *devlink = devlink_port->devlink;
        struct sk_buff *msg;
        int err;
@@ -782,10 +820,71 @@ static int devlink_port_type_set(struct devlink *devlink,
        return -EOPNOTSUPP;
 }
 
+static int
+devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *port,
+                                 const struct nlattr *attr, struct netlink_ext_ack *extack)
+{
+       const struct devlink_ops *ops;
+       const u8 *hw_addr;
+       int hw_addr_len;
+       int err;
+
+       hw_addr = nla_data(attr);
+       hw_addr_len = nla_len(attr);
+       if (hw_addr_len > MAX_ADDR_LEN) {
+               NL_SET_ERR_MSG_MOD(extack, "Port function hardware address too long");
+               return -EINVAL;
+       }
+       if (port->type == DEVLINK_PORT_TYPE_ETH) {
+               if (hw_addr_len != ETH_ALEN) {
+                       NL_SET_ERR_MSG_MOD(extack, "Address must be 6 bytes for Ethernet device");
+                       return -EINVAL;
+               }
+               if (!is_unicast_ether_addr(hw_addr)) {
+                       NL_SET_ERR_MSG_MOD(extack, "Non-unicast hardware address unsupported");
+                       return -EINVAL;
+               }
+       }
+
+       ops = devlink->ops;
+       if (!ops->port_function_hw_addr_set) {
+               NL_SET_ERR_MSG_MOD(extack, "Port doesn't support function attributes");
+               return -EOPNOTSUPP;
+       }
+
+       err = ops->port_function_hw_addr_set(devlink, port, hw_addr, hw_addr_len, extack);
+       if (err)
+               return err;
+
+       devlink_port_notify(port, DEVLINK_CMD_PORT_NEW);
+       return 0;
+}
+
+static int
+devlink_port_function_set(struct devlink *devlink, struct devlink_port *port,
+                         const struct nlattr *attr, struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1];
+       int err;
+
+       err = nla_parse_nested(tb, DEVLINK_PORT_FUNCTION_ATTR_MAX, attr,
+                              devlink_function_nl_policy, extack);
+       if (err < 0) {
+               NL_SET_ERR_MSG_MOD(extack, "Fail to parse port function attributes");
+               return err;
+       }
+
+       attr = tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR];
+       if (attr)
+               err = devlink_port_function_hw_addr_set(devlink, port, attr, extack);
+
+       return err;
+}
+
 static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,
                                        struct genl_info *info)
 {
-       struct devlink_port *devlink_port = info->user_ptr[0];
+       struct devlink_port *devlink_port = info->user_ptr[1];
        struct devlink *devlink = devlink_port->devlink;
        int err;
 
@@ -797,6 +896,16 @@ static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,
                if (err)
                        return err;
        }
+
+       if (info->attrs[DEVLINK_ATTR_PORT_FUNCTION]) {
+               struct nlattr *attr = info->attrs[DEVLINK_ATTR_PORT_FUNCTION];
+               struct netlink_ext_ack *extack = info->extack;
+
+               err = devlink_port_function_set(devlink, devlink_port, attr, extack);
+               if (err)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -814,6 +923,7 @@ static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb,
                                          struct genl_info *info)
 {
        struct devlink *devlink = info->user_ptr[0];
+       struct devlink_port *devlink_port;
        u32 port_index;
        u32 count;
 
@@ -821,8 +931,27 @@ static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb,
            !info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT])
                return -EINVAL;
 
+       devlink_port = devlink_port_get_from_info(devlink, info);
        port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
        count = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]);
+
+       if (IS_ERR(devlink_port))
+               return -EINVAL;
+
+       if (!devlink_port->attrs.splittable) {
+               /* Split ports cannot be split. */
+               if (devlink_port->attrs.split)
+                       NL_SET_ERR_MSG_MOD(info->extack, "Port cannot be split further");
+               else
+                       NL_SET_ERR_MSG_MOD(info->extack, "Port cannot be split");
+               return -EINVAL;
+       }
+
+       if (count < 2 || !is_power_of_2(count) || count > devlink_port->attrs.lanes) {
+               NL_SET_ERR_MSG_MOD(info->extack, "Invalid split count");
+               return -EINVAL;
+       }
+
        return devlink_port_split(devlink, port_index, count, info->extack);
 }
 
@@ -890,10 +1019,14 @@ static int devlink_nl_cmd_sb_get_doit(struct sk_buff *skb,
                                      struct genl_info *info)
 {
        struct devlink *devlink = info->user_ptr[0];
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
+       struct devlink_sb *devlink_sb;
        struct sk_buff *msg;
        int err;
 
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
@@ -995,11 +1128,15 @@ static int devlink_nl_cmd_sb_pool_get_doit(struct sk_buff *skb,
                                           struct genl_info *info)
 {
        struct devlink *devlink = info->user_ptr[0];
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
+       struct devlink_sb *devlink_sb;
        struct sk_buff *msg;
        u16 pool_index;
        int err;
 
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
+
        err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
                                                  &pool_index);
        if (err)
@@ -1104,12 +1241,16 @@ static int devlink_nl_cmd_sb_pool_set_doit(struct sk_buff *skb,
                                           struct genl_info *info)
 {
        struct devlink *devlink = info->user_ptr[0];
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
        enum devlink_sb_threshold_type threshold_type;
+       struct devlink_sb *devlink_sb;
        u16 pool_index;
        u32 size;
        int err;
 
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
+
        err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
                                                  &pool_index);
        if (err)
@@ -1188,13 +1329,17 @@ nla_put_failure:
 static int devlink_nl_cmd_sb_port_pool_get_doit(struct sk_buff *skb,
                                                struct genl_info *info)
 {
-       struct devlink_port *devlink_port = info->user_ptr[0];
+       struct devlink_port *devlink_port = info->user_ptr[1];
        struct devlink *devlink = devlink_port->devlink;
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
+       struct devlink_sb *devlink_sb;
        struct sk_buff *msg;
        u16 pool_index;
        int err;
 
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
+
        err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
                                                  &pool_index);
        if (err)
@@ -1304,12 +1449,17 @@ static int devlink_sb_port_pool_set(struct devlink_port *devlink_port,
 static int devlink_nl_cmd_sb_port_pool_set_doit(struct sk_buff *skb,
                                                struct genl_info *info)
 {
-       struct devlink_port *devlink_port = info->user_ptr[0];
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
+       struct devlink_port *devlink_port = info->user_ptr[1];
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_sb *devlink_sb;
        u16 pool_index;
        u32 threshold;
        int err;
 
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
+
        err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
                                                  &pool_index);
        if (err)
@@ -1391,14 +1541,18 @@ nla_put_failure:
 static int devlink_nl_cmd_sb_tc_pool_bind_get_doit(struct sk_buff *skb,
                                                   struct genl_info *info)
 {
-       struct devlink_port *devlink_port = info->user_ptr[0];
+       struct devlink_port *devlink_port = info->user_ptr[1];
        struct devlink *devlink = devlink_port->devlink;
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
+       struct devlink_sb *devlink_sb;
        struct sk_buff *msg;
        enum devlink_sb_pool_type pool_type;
        u16 tc_index;
        int err;
 
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
+
        err = devlink_sb_pool_type_get_from_info(info, &pool_type);
        if (err)
                return err;
@@ -1538,14 +1692,19 @@ static int devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port,
 static int devlink_nl_cmd_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
                                                   struct genl_info *info)
 {
-       struct devlink_port *devlink_port = info->user_ptr[0];
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
+       struct devlink_port *devlink_port = info->user_ptr[1];
+       struct devlink *devlink = info->user_ptr[0];
        enum devlink_sb_pool_type pool_type;
+       struct devlink_sb *devlink_sb;
        u16 tc_index;
        u16 pool_index;
        u32 threshold;
        int err;
 
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
+
        err = devlink_sb_pool_type_get_from_info(info, &pool_type);
        if (err)
                return err;
@@ -1573,8 +1732,12 @@ static int devlink_nl_cmd_sb_occ_snapshot_doit(struct sk_buff *skb,
                                               struct genl_info *info)
 {
        struct devlink *devlink = info->user_ptr[0];
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
        const struct devlink_ops *ops = devlink->ops;
+       struct devlink_sb *devlink_sb;
+
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
 
        if (ops->sb_occ_snapshot)
                return ops->sb_occ_snapshot(devlink, devlink_sb->index);
@@ -1585,8 +1748,12 @@ static int devlink_nl_cmd_sb_occ_max_clear_doit(struct sk_buff *skb,
                                                struct genl_info *info)
 {
        struct devlink *devlink = info->user_ptr[0];
-       struct devlink_sb *devlink_sb = info->user_ptr[1];
        const struct devlink_ops *ops = devlink->ops;
+       struct devlink_sb *devlink_sb;
+
+       devlink_sb = devlink_sb_get_from_info(devlink, info);
+       if (IS_ERR(devlink_sb))
+               return PTR_ERR(devlink_sb);
 
        if (ops->sb_occ_max_clear)
                return ops->sb_occ_max_clear(devlink, devlink_sb->index);
@@ -2770,7 +2937,7 @@ static void devlink_reload_netns_change(struct devlink *devlink,
                                     DEVLINK_CMD_PARAM_NEW);
 }
 
-static bool devlink_reload_supported(struct devlink *devlink)
+static bool devlink_reload_supported(const struct devlink *devlink)
 {
        return devlink->ops->reload_down && devlink->ops->reload_up;
 }
@@ -2816,7 +2983,7 @@ static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
        struct net *dest_net = NULL;
        int err;
 
-       if (!devlink_reload_supported(devlink) || !devlink->reload_enabled)
+       if (!devlink_reload_supported(devlink))
                return -EOPNOTSUPP;
 
        err = devlink_resources_validate(devlink, NULL, info);
@@ -4382,6 +4549,14 @@ int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
 }
 EXPORT_SYMBOL_GPL(devlink_info_serial_number_put);
 
+int devlink_info_board_serial_number_put(struct devlink_info_req *req,
+                                        const char *bsn)
+{
+       return nla_put_string(req->msg, DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER,
+                             bsn);
+}
+EXPORT_SYMBOL_GPL(devlink_info_board_serial_number_put);
+
 static int devlink_info_version_put(struct devlink_info_req *req, int attr,
                                    const char *version_name,
                                    const char *version_value)
@@ -5133,6 +5308,7 @@ struct devlink_health_reporter {
        void *priv;
        const struct devlink_health_reporter_ops *ops;
        struct devlink *devlink;
+       struct devlink_port *devlink_port;
        struct devlink_fmsg *dump_fmsg;
        struct mutex dump_lock; /* lock parallel read/write from dump buffers */
        u64 graceful_period;
@@ -5155,18 +5331,98 @@ devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
 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)
+__devlink_health_reporter_find_by_name(struct list_head *reporter_list,
+                                      struct mutex *list_lock,
+                                      const char *reporter_name)
 {
        struct devlink_health_reporter *reporter;
 
-       lockdep_assert_held(&devlink->reporters_lock);
-       list_for_each_entry(reporter, &devlink->reporter_list, list)
+       lockdep_assert_held(list_lock);
+       list_for_each_entry(reporter, reporter_list, list)
                if (!strcmp(reporter->ops->name, reporter_name))
                        return reporter;
        return NULL;
 }
 
+static struct devlink_health_reporter *
+devlink_health_reporter_find_by_name(struct devlink *devlink,
+                                    const char *reporter_name)
+{
+       return __devlink_health_reporter_find_by_name(&devlink->reporter_list,
+                                                     &devlink->reporters_lock,
+                                                     reporter_name);
+}
+
+static struct devlink_health_reporter *
+devlink_port_health_reporter_find_by_name(struct devlink_port *devlink_port,
+                                         const char *reporter_name)
+{
+       return __devlink_health_reporter_find_by_name(&devlink_port->reporter_list,
+                                                     &devlink_port->reporters_lock,
+                                                     reporter_name);
+}
+
+static struct devlink_health_reporter *
+__devlink_health_reporter_create(struct devlink *devlink,
+                                const struct devlink_health_reporter_ops *ops,
+                                u64 graceful_period, void *priv)
+{
+       struct devlink_health_reporter *reporter;
+
+       if (WARN_ON(graceful_period && !ops->recover))
+               return ERR_PTR(-EINVAL);
+
+       reporter = kzalloc(sizeof(*reporter), GFP_KERNEL);
+       if (!reporter)
+               return ERR_PTR(-ENOMEM);
+
+       reporter->priv = priv;
+       reporter->ops = ops;
+       reporter->devlink = devlink;
+       reporter->graceful_period = graceful_period;
+       reporter->auto_recover = !!ops->recover;
+       reporter->auto_dump = !!ops->dump;
+       mutex_init(&reporter->dump_lock);
+       refcount_set(&reporter->refcount, 1);
+       return reporter;
+}
+
+/**
+ *     devlink_port_health_reporter_create - create devlink health reporter for
+ *                                           specified port instance
+ *
+ *     @port: devlink_port which should contain the new reporter
+ *     @ops: ops
+ *     @graceful_period: to avoid recovery loops, in msecs
+ *     @priv: priv
+ */
+struct devlink_health_reporter *
+devlink_port_health_reporter_create(struct devlink_port *port,
+                                   const struct devlink_health_reporter_ops *ops,
+                                   u64 graceful_period, void *priv)
+{
+       struct devlink_health_reporter *reporter;
+
+       mutex_lock(&port->reporters_lock);
+       if (__devlink_health_reporter_find_by_name(&port->reporter_list,
+                                                  &port->reporters_lock, ops->name)) {
+               reporter = ERR_PTR(-EEXIST);
+               goto unlock;
+       }
+
+       reporter = __devlink_health_reporter_create(port->devlink, ops,
+                                                   graceful_period, priv);
+       if (IS_ERR(reporter))
+               goto unlock;
+
+       reporter->devlink_port = port;
+       list_add_tail(&reporter->list, &port->reporter_list);
+unlock:
+       mutex_unlock(&port->reporters_lock);
+       return reporter;
+}
+EXPORT_SYMBOL_GPL(devlink_port_health_reporter_create);
+
 /**
  *     devlink_health_reporter_create - create devlink health reporter
  *
@@ -5188,25 +5444,11 @@ devlink_health_reporter_create(struct devlink *devlink,
                goto unlock;
        }
 
-       if (WARN_ON(graceful_period && !ops->recover)) {
-               reporter = ERR_PTR(-EINVAL);
+       reporter = __devlink_health_reporter_create(devlink, ops,
+                                                   graceful_period, priv);
+       if (IS_ERR(reporter))
                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 = !!ops->recover;
-       reporter->auto_dump = !!ops->dump;
-       mutex_init(&reporter->dump_lock);
-       refcount_set(&reporter->refcount, 1);
        list_add_tail(&reporter->list, &devlink->reporter_list);
 unlock:
        mutex_unlock(&devlink->reporters_lock);
@@ -5214,6 +5456,29 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
 
+static void
+devlink_health_reporter_free(struct devlink_health_reporter *reporter)
+{
+       mutex_destroy(&reporter->dump_lock);
+       if (reporter->dump_fmsg)
+               devlink_fmsg_free(reporter->dump_fmsg);
+       kfree(reporter);
+}
+
+static void
+devlink_health_reporter_put(struct devlink_health_reporter *reporter)
+{
+       if (refcount_dec_and_test(&reporter->refcount))
+               devlink_health_reporter_free(reporter);
+}
+
+static void
+__devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
+{
+       list_del(&reporter->list);
+       devlink_health_reporter_put(reporter);
+}
+
 /**
  *     devlink_health_reporter_destroy - destroy devlink health reporter
  *
@@ -5222,18 +5487,30 @@ EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
 void
 devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
 {
-       mutex_lock(&reporter->devlink->reporters_lock);
-       list_del(&reporter->list);
-       mutex_unlock(&reporter->devlink->reporters_lock);
-       while (refcount_read(&reporter->refcount) > 1)
-               msleep(100);
-       mutex_destroy(&reporter->dump_lock);
-       if (reporter->dump_fmsg)
-               devlink_fmsg_free(reporter->dump_fmsg);
-       kfree(reporter);
+       struct mutex *lock = &reporter->devlink->reporters_lock;
+
+       mutex_lock(lock);
+       __devlink_health_reporter_destroy(reporter);
+       mutex_unlock(lock);
 }
 EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
 
+/**
+ *     devlink_port_health_reporter_destroy - destroy devlink port health reporter
+ *
+ *     @reporter: devlink health reporter to destroy
+ */
+void
+devlink_port_health_reporter_destroy(struct devlink_health_reporter *reporter)
+{
+       struct mutex *lock = &reporter->devlink_port->reporters_lock;
+
+       mutex_lock(lock);
+       __devlink_health_reporter_destroy(reporter);
+       mutex_unlock(lock);
+}
+EXPORT_SYMBOL_GPL(devlink_port_health_reporter_destroy);
+
 static int
 devlink_nl_health_reporter_fill(struct sk_buff *msg,
                                struct devlink *devlink,
@@ -5251,6 +5528,10 @@ devlink_nl_health_reporter_fill(struct sk_buff *msg,
        if (devlink_nl_put_handle(msg, devlink))
                goto genlmsg_cancel;
 
+       if (reporter->devlink_port) {
+               if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, reporter->devlink_port->index))
+                       goto genlmsg_cancel;
+       }
        reporter_attr = nla_nest_start_noflag(msg,
                                              DEVLINK_ATTR_HEALTH_REPORTER);
        if (!reporter_attr)
@@ -5458,17 +5739,28 @@ devlink_health_reporter_get_from_attrs(struct devlink *devlink,
                                       struct nlattr **attrs)
 {
        struct devlink_health_reporter *reporter;
+       struct devlink_port *devlink_port;
        char *reporter_name;
 
        if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
                return NULL;
 
        reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
-       mutex_lock(&devlink->reporters_lock);
-       reporter = devlink_health_reporter_find_by_name(devlink, reporter_name);
-       if (reporter)
-               refcount_inc(&reporter->refcount);
-       mutex_unlock(&devlink->reporters_lock);
+       devlink_port = devlink_port_get_from_attrs(devlink, attrs);
+       if (IS_ERR(devlink_port)) {
+               mutex_lock(&devlink->reporters_lock);
+               reporter = devlink_health_reporter_find_by_name(devlink, reporter_name);
+               if (reporter)
+                       refcount_inc(&reporter->refcount);
+               mutex_unlock(&devlink->reporters_lock);
+       } else {
+               mutex_lock(&devlink_port->reporters_lock);
+               reporter = devlink_port_health_reporter_find_by_name(devlink_port, reporter_name);
+               if (reporter)
+                       refcount_inc(&reporter->refcount);
+               mutex_unlock(&devlink_port->reporters_lock);
+       }
+
        return reporter;
 }
 
@@ -5500,12 +5792,6 @@ unlock:
        return NULL;
 }
 
-static void
-devlink_health_reporter_put(struct devlink_health_reporter *reporter)
-{
-       refcount_dec(&reporter->refcount);
-}
-
 void
 devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
                                     enum devlink_health_reporter_state state)
@@ -5562,6 +5848,7 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
                                          struct netlink_callback *cb)
 {
        struct devlink_health_reporter *reporter;
+       struct devlink_port *port;
        struct devlink *devlink;
        int start = cb->args[0];
        int idx = 0;
@@ -5592,6 +5879,31 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
                }
                mutex_unlock(&devlink->reporters_lock);
        }
+
+       list_for_each_entry(devlink, &devlink_list, list) {
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       continue;
+               list_for_each_entry(port, &devlink->port_list, list) {
+                       mutex_lock(&port->reporters_lock);
+                       list_for_each_entry(reporter, &port->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(&port->reporters_lock);
+                                       goto out;
+                               }
+                               idx++;
+                       }
+                       mutex_unlock(&port->reporters_lock);
+               }
+       }
 out:
        mutex_unlock(&devlink_mutex);
 
@@ -6713,6 +7025,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
        [DEVLINK_ATTR_TRAP_POLICER_ID] = { .type = NLA_U32 },
        [DEVLINK_ATTR_TRAP_POLICER_RATE] = { .type = NLA_U64 },
        [DEVLINK_ATTR_TRAP_POLICER_BURST] = { .type = NLA_U64 },
+       [DEVLINK_ATTR_PORT_FUNCTION] = { .type = NLA_NESTED },
 };
 
 static const struct genl_ops devlink_nl_ops[] = {
@@ -6721,7 +7034,6 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_get_doit,
                .dumpit = devlink_nl_cmd_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6744,24 +7056,20 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_port_split_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
                .cmd = DEVLINK_CMD_PORT_UNSPLIT,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_port_unsplit_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
                .cmd = DEVLINK_CMD_SB_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_get_doit,
                .dumpit = devlink_nl_cmd_sb_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6769,8 +7077,6 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_pool_get_doit,
                .dumpit = devlink_nl_cmd_sb_pool_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6778,16 +7084,13 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_pool_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
        },
        {
                .cmd = DEVLINK_CMD_SB_PORT_POOL_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_port_pool_get_doit,
                .dumpit = devlink_nl_cmd_sb_port_pool_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
-                                 DEVLINK_NL_FLAG_NEED_SB,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6795,16 +7098,14 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_port_pool_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
-                                 DEVLINK_NL_FLAG_NEED_SB,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
        },
        {
                .cmd = DEVLINK_CMD_SB_TC_POOL_BIND_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_tc_pool_bind_get_doit,
                .dumpit = devlink_nl_cmd_sb_tc_pool_bind_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
-                                 DEVLINK_NL_FLAG_NEED_SB,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6812,60 +7113,50 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_tc_pool_bind_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
-                                 DEVLINK_NL_FLAG_NEED_SB,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
        },
        {
                .cmd = DEVLINK_CMD_SB_OCC_SNAPSHOT,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_occ_snapshot_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
        },
        {
                .cmd = DEVLINK_CMD_SB_OCC_MAX_CLEAR,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_sb_occ_max_clear_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NEED_SB,
        },
        {
                .cmd = DEVLINK_CMD_ESWITCH_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_eswitch_get_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
                .cmd = DEVLINK_CMD_ESWITCH_SET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_eswitch_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
                .cmd = DEVLINK_CMD_DPIPE_TABLE_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_dpipe_table_get,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
                .cmd = DEVLINK_CMD_DPIPE_ENTRIES_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_dpipe_entries_get,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
                .cmd = DEVLINK_CMD_DPIPE_HEADERS_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_dpipe_headers_get,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6873,20 +7164,17 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_dpipe_table_counters_set,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_RESOURCE_SET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_resource_set,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_RESOURCE_DUMP,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_resource_dump,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6894,15 +7182,13 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_reload,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
-                                 DEVLINK_NL_FLAG_NO_LOCK,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
                .cmd = DEVLINK_CMD_PARAM_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_param_get_doit,
                .dumpit = devlink_nl_cmd_param_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6910,7 +7196,6 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_param_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_PORT_PARAM_GET,
@@ -6933,21 +7218,18 @@ static const struct genl_ops devlink_nl_ops[] = {
                .doit = devlink_nl_cmd_region_get_doit,
                .dumpit = devlink_nl_cmd_region_get_dumpit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_REGION_NEW,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_region_new,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_REGION_DEL,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_region_del,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_REGION_READ,
@@ -6955,14 +7237,12 @@ static const struct genl_ops devlink_nl_ops[] = {
                            GENL_DONT_VALIDATE_DUMP_STRICT,
                .dumpit = devlink_nl_cmd_region_read_dumpit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_INFO_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_info_get_doit,
                .dumpit = devlink_nl_cmd_info_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -6970,7 +7250,7 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_health_reporter_get_doit,
                .dumpit = devlink_nl_cmd_health_reporter_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
                                  DEVLINK_NL_FLAG_NO_LOCK,
                /* can be retrieved by unprivileged users */
        },
@@ -6979,7 +7259,7 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_health_reporter_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
                                  DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
@@ -6987,7 +7267,7 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_health_reporter_recover_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
                                  DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
@@ -6995,7 +7275,7 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_health_reporter_diagnose_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
                                  DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
@@ -7004,7 +7284,7 @@ static const struct genl_ops devlink_nl_ops[] = {
                            GENL_DONT_VALIDATE_DUMP_STRICT,
                .dumpit = devlink_nl_cmd_health_reporter_dump_get_dumpit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
                                  DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
@@ -7012,7 +7292,7 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_health_reporter_dump_clear_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
                                  DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
@@ -7020,46 +7300,39 @@ static const struct genl_ops devlink_nl_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = devlink_nl_cmd_flash_update,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_TRAP_GET,
                .doit = devlink_nl_cmd_trap_get_doit,
                .dumpit = devlink_nl_cmd_trap_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
                .cmd = DEVLINK_CMD_TRAP_SET,
                .doit = devlink_nl_cmd_trap_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_TRAP_GROUP_GET,
                .doit = devlink_nl_cmd_trap_group_get_doit,
                .dumpit = devlink_nl_cmd_trap_group_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
                .cmd = DEVLINK_CMD_TRAP_GROUP_SET,
                .doit = devlink_nl_cmd_trap_group_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
                .cmd = DEVLINK_CMD_TRAP_POLICER_GET,
                .doit = devlink_nl_cmd_trap_policer_get_doit,
                .dumpit = devlink_nl_cmd_trap_policer_get_dumpit,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
        {
                .cmd = DEVLINK_CMD_TRAP_POLICER_SET,
                .doit = devlink_nl_cmd_trap_policer_set_doit,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
 };
 
@@ -7124,9 +7397,9 @@ EXPORT_SYMBOL_GPL(devlink_alloc);
  */
 int devlink_register(struct devlink *devlink, struct device *dev)
 {
-       mutex_lock(&devlink_mutex);
        devlink->dev = dev;
        devlink->registered = true;
+       mutex_lock(&devlink_mutex);
        list_add_tail(&devlink->list, &devlink_list);
        devlink_notify(devlink, DEVLINK_CMD_NEW);
        mutex_unlock(&devlink_mutex);
@@ -7272,6 +7545,8 @@ int devlink_port_register(struct devlink *devlink,
        list_add_tail(&devlink_port->list, &devlink->port_list);
        INIT_LIST_HEAD(&devlink_port->param_list);
        mutex_unlock(&devlink->lock);
+       INIT_LIST_HEAD(&devlink_port->reporter_list);
+       mutex_init(&devlink_port->reporters_lock);
        INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
        devlink_port_type_warn_schedule(devlink_port);
        devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
@@ -7288,6 +7563,8 @@ void devlink_port_unregister(struct devlink_port *devlink_port)
 {
        struct devlink *devlink = devlink_port->devlink;
 
+       WARN_ON(!list_empty(&devlink_port->reporter_list));
+       mutex_destroy(&devlink_port->reporters_lock);
        devlink_port_type_warn_cancel(devlink_port);
        devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
        mutex_lock(&devlink->lock);
@@ -7381,24 +7658,20 @@ void devlink_port_type_clear(struct devlink_port *devlink_port)
 EXPORT_SYMBOL_GPL(devlink_port_type_clear);
 
 static int __devlink_port_attrs_set(struct devlink_port *devlink_port,
-                                   enum devlink_port_flavour flavour,
-                                   const unsigned char *switch_id,
-                                   unsigned char switch_id_len)
+                                   enum devlink_port_flavour flavour)
 {
        struct devlink_port_attrs *attrs = &devlink_port->attrs;
 
        if (WARN_ON(devlink_port->registered))
                return -EEXIST;
-       attrs->set = true;
+       devlink_port->attrs_set = true;
        attrs->flavour = flavour;
-       if (switch_id) {
-               attrs->switch_port = true;
-               if (WARN_ON(switch_id_len > MAX_PHYS_ITEM_ID_LEN))
-                       switch_id_len = MAX_PHYS_ITEM_ID_LEN;
-               memcpy(attrs->switch_id.id, switch_id, switch_id_len);
-               attrs->switch_id.id_len = switch_id_len;
+       if (attrs->switch_id.id_len) {
+               devlink_port->switch_port = true;
+               if (WARN_ON(attrs->switch_id.id_len > MAX_PHYS_ITEM_ID_LEN))
+                       attrs->switch_id.id_len = MAX_PHYS_ITEM_ID_LEN;
        } else {
-               attrs->switch_port = false;
+               devlink_port->switch_port = false;
        }
        return 0;
 }
@@ -7407,33 +7680,18 @@ static int __devlink_port_attrs_set(struct devlink_port *devlink_port,
  *     devlink_port_attrs_set - Set port attributes
  *
  *     @devlink_port: devlink port
- *     @flavour: flavour of the port
- *     @port_number: number of the port that is facing user, for example
- *                   the front panel port number
- *     @split: indicates if this is split port
- *     @split_subport_number: if the port is split, this is the number
- *                            of subport.
- *     @switch_id: if the port is part of switch, this is buffer with ID,
- *                 otwerwise this is NULL
- *     @switch_id_len: length of the switch_id buffer
+ *     @attrs: devlink port attrs
  */
 void devlink_port_attrs_set(struct devlink_port *devlink_port,
-                           enum devlink_port_flavour flavour,
-                           u32 port_number, bool split,
-                           u32 split_subport_number,
-                           const unsigned char *switch_id,
-                           unsigned char switch_id_len)
+                           struct devlink_port_attrs *attrs)
 {
-       struct devlink_port_attrs *attrs = &devlink_port->attrs;
        int ret;
 
-       ret = __devlink_port_attrs_set(devlink_port, flavour,
-                                      switch_id, switch_id_len);
+       devlink_port->attrs = *attrs;
+       ret = __devlink_port_attrs_set(devlink_port, attrs->flavour);
        if (ret)
                return;
-       attrs->split = split;
-       attrs->phys.port_number = port_number;
-       attrs->phys.split_subport_number = split_subport_number;
+       WARN_ON(attrs->splittable && attrs->split);
 }
 EXPORT_SYMBOL_GPL(devlink_port_attrs_set);
 
@@ -7442,20 +7700,14 @@ EXPORT_SYMBOL_GPL(devlink_port_attrs_set);
  *
  *     @devlink_port: devlink port
  *     @pf: associated PF for the devlink port instance
- *     @switch_id: if the port is part of switch, this is buffer with ID,
- *                 otherwise this is NULL
- *     @switch_id_len: length of the switch_id buffer
  */
-void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port,
-                                  const unsigned char *switch_id,
-                                  unsigned char switch_id_len, u16 pf)
+void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port, u16 pf)
 {
        struct devlink_port_attrs *attrs = &devlink_port->attrs;
        int ret;
 
        ret = __devlink_port_attrs_set(devlink_port,
-                                      DEVLINK_PORT_FLAVOUR_PCI_PF,
-                                      switch_id, switch_id_len);
+                                      DEVLINK_PORT_FLAVOUR_PCI_PF);
        if (ret)
                return;
 
@@ -7469,21 +7721,15 @@ EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_pf_set);
  *     @devlink_port: devlink port
  *     @pf: associated PF for the devlink port instance
  *     @vf: associated VF of a PF for the devlink port instance
- *     @switch_id: if the port is part of switch, this is buffer with ID,
- *                 otherwise this is NULL
- *     @switch_id_len: length of the switch_id buffer
  */
 void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port,
-                                  const unsigned char *switch_id,
-                                  unsigned char switch_id_len,
                                   u16 pf, u16 vf)
 {
        struct devlink_port_attrs *attrs = &devlink_port->attrs;
        int ret;
 
        ret = __devlink_port_attrs_set(devlink_port,
-                                      DEVLINK_PORT_FLAVOUR_PCI_VF,
-                                      switch_id, switch_id_len);
+                                      DEVLINK_PORT_FLAVOUR_PCI_VF);
        if (ret)
                return;
        attrs->pci_vf.pf = pf;
@@ -7497,7 +7743,7 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port,
        struct devlink_port_attrs *attrs = &devlink_port->attrs;
        int n = 0;
 
-       if (!attrs->set)
+       if (!devlink_port->attrs_set)
                return -EOPNOTSUPP;
 
        switch (attrs->flavour) {
@@ -9332,7 +9578,7 @@ int devlink_compat_switch_id_get(struct net_device *dev,
         * any devlink lock as only permanent values are accessed.
         */
        devlink_port = netdev_to_devlink_port(dev);
-       if (!devlink_port || !devlink_port->attrs.switch_port)
+       if (!devlink_port || !devlink_port->switch_port)
                return -EOPNOTSUPP;
 
        memcpy(ppid, &devlink_port->attrs.switch_id, sizeof(*ppid));