Merge tag 'block-5.15-2021-09-05' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / net / core / devlink.c
index 8503262..a856ae4 100644 (file)
@@ -92,7 +92,8 @@ static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_
                                 DEVLINK_PORT_FN_STATE_ACTIVE),
 };
 
-static LIST_HEAD(devlink_list);
+static DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC);
+#define DEVLINK_REGISTERED XA_MARK_1
 
 /* devlink_mutex
  *
@@ -108,23 +109,23 @@ struct net *devlink_net(const struct devlink *devlink)
 }
 EXPORT_SYMBOL_GPL(devlink_net);
 
-static void __devlink_net_set(struct devlink *devlink, struct net *net)
+static void devlink_put(struct devlink *devlink)
 {
-       write_pnet(&devlink->_net, net);
+       if (refcount_dec_and_test(&devlink->refcount))
+               complete(&devlink->comp);
 }
 
-void devlink_net_set(struct devlink *devlink, struct net *net)
+static bool __must_check devlink_try_get(struct devlink *devlink)
 {
-       if (WARN_ON(devlink->registered))
-               return;
-       __devlink_net_set(devlink, net);
+       return refcount_inc_not_zero(&devlink->refcount);
 }
-EXPORT_SYMBOL_GPL(devlink_net_set);
 
 static struct devlink *devlink_get_from_attrs(struct net *net,
                                              struct nlattr **attrs)
 {
        struct devlink *devlink;
+       unsigned long index;
+       bool found = false;
        char *busname;
        char *devname;
 
@@ -136,19 +137,19 @@ static struct devlink *devlink_get_from_attrs(struct net *net,
 
        lockdep_assert_held(&devlink_mutex);
 
-       list_for_each_entry(devlink, &devlink_list, list) {
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
                if (strcmp(devlink->dev->bus->name, busname) == 0 &&
                    strcmp(dev_name(devlink->dev), devname) == 0 &&
-                   net_eq(devlink_net(devlink), net))
-                       return devlink;
+                   net_eq(devlink_net(devlink), net)) {
+                       found = true;
+                       break;
+               }
        }
 
-       return ERR_PTR(-ENODEV);
-}
+       if (!found || !devlink_try_get(devlink))
+               devlink = ERR_PTR(-ENODEV);
 
-static struct devlink *devlink_get_from_info(struct genl_info *info)
-{
-       return devlink_get_from_attrs(genl_info_net(info), info->attrs);
+       return devlink;
 }
 
 static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,
@@ -499,7 +500,7 @@ static int devlink_nl_pre_doit(const struct genl_ops *ops,
        int err;
 
        mutex_lock(&devlink_mutex);
-       devlink = devlink_get_from_info(info);
+       devlink = devlink_get_from_attrs(genl_info_net(info), info->attrs);
        if (IS_ERR(devlink)) {
                mutex_unlock(&devlink_mutex);
                return PTR_ERR(devlink);
@@ -542,6 +543,7 @@ static int devlink_nl_pre_doit(const struct genl_ops *ops,
 unlock:
        if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
                mutex_unlock(&devlink->lock);
+       devlink_put(devlink);
        mutex_unlock(&devlink_mutex);
        return err;
 }
@@ -554,6 +556,7 @@ static void devlink_nl_post_doit(const struct genl_ops *ops,
        devlink = info->user_ptr[0];
        if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
                mutex_unlock(&devlink->lock);
+       devlink_put(devlink);
        mutex_unlock(&devlink_mutex);
 }
 
@@ -817,10 +820,11 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
        return 0;
 }
 
-static int
-devlink_port_fn_hw_addr_fill(struct devlink *devlink, const struct devlink_ops *ops,
-                            struct devlink_port *port, struct sk_buff *msg,
-                            struct netlink_ext_ack *extack, bool *msg_updated)
+static int devlink_port_fn_hw_addr_fill(const struct devlink_ops *ops,
+                                       struct devlink_port *port,
+                                       struct sk_buff *msg,
+                                       struct netlink_ext_ack *extack,
+                                       bool *msg_updated)
 {
        u8 hw_addr[MAX_ADDR_LEN];
        int hw_addr_len;
@@ -829,7 +833,8 @@ devlink_port_fn_hw_addr_fill(struct devlink *devlink, const struct devlink_ops *
        if (!ops->port_function_hw_addr_get)
                return 0;
 
-       err = ops->port_function_hw_addr_get(devlink, port, hw_addr, &hw_addr_len, extack);
+       err = ops->port_function_hw_addr_get(port, hw_addr, &hw_addr_len,
+                                            extack);
        if (err) {
                if (err == -EOPNOTSUPP)
                        return 0;
@@ -843,12 +848,11 @@ devlink_port_fn_hw_addr_fill(struct devlink *devlink, const struct devlink_ops *
 }
 
 static int devlink_nl_rate_fill(struct sk_buff *msg,
-                               struct devlink *devlink,
                                struct devlink_rate *devlink_rate,
-                               enum devlink_command cmd, u32 portid,
-                               u32 seq, int flags,
-                               struct netlink_ext_ack *extack)
+                               enum devlink_command cmd, u32 portid, u32 seq,
+                               int flags, struct netlink_ext_ack *extack)
 {
+       struct devlink *devlink = devlink_rate->devlink;
        void *hdr;
 
        hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
@@ -906,12 +910,11 @@ devlink_port_fn_opstate_valid(enum devlink_port_fn_opstate opstate)
               opstate == DEVLINK_PORT_FN_OPSTATE_ATTACHED;
 }
 
-static int
-devlink_port_fn_state_fill(struct devlink *devlink,
-                          const struct devlink_ops *ops,
-                          struct devlink_port *port, struct sk_buff *msg,
-                          struct netlink_ext_ack *extack,
-                          bool *msg_updated)
+static int devlink_port_fn_state_fill(const struct devlink_ops *ops,
+                                     struct devlink_port *port,
+                                     struct sk_buff *msg,
+                                     struct netlink_ext_ack *extack,
+                                     bool *msg_updated)
 {
        enum devlink_port_fn_opstate opstate;
        enum devlink_port_fn_state state;
@@ -920,7 +923,7 @@ devlink_port_fn_state_fill(struct devlink *devlink,
        if (!ops->port_fn_state_get)
                return 0;
 
-       err = ops->port_fn_state_get(devlink, port, &state, &opstate, extack);
+       err = ops->port_fn_state_get(port, &state, &opstate, extack);
        if (err) {
                if (err == -EOPNOTSUPP)
                        return 0;
@@ -948,7 +951,6 @@ 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 msg_updated = false;
@@ -958,13 +960,12 @@ devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *por
        if (!function_attr)
                return -EMSGSIZE;
 
-       ops = devlink->ops;
-       err = devlink_port_fn_hw_addr_fill(devlink, ops, port, msg,
-                                          extack, &msg_updated);
+       ops = port->devlink->ops;
+       err = devlink_port_fn_hw_addr_fill(ops, port, msg, extack,
+                                          &msg_updated);
        if (err)
                goto out;
-       err = devlink_port_fn_state_fill(devlink, ops, port, msg, extack,
-                                        &msg_updated);
+       err = devlink_port_fn_state_fill(ops, port, msg, extack, &msg_updated);
 out:
        if (err || !msg_updated)
                nla_nest_cancel(msg, function_attr);
@@ -973,12 +974,12 @@ out:
        return err;
 }
 
-static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
+static int devlink_nl_port_fill(struct sk_buff *msg,
                                struct devlink_port *devlink_port,
-                               enum devlink_command cmd, u32 portid,
-                               u32 seq, int flags,
-                               struct netlink_ext_ack *extack)
+                               enum devlink_command cmd, u32 portid, u32 seq,
+                               int flags, struct netlink_ext_ack *extack)
 {
+       struct devlink *devlink = devlink_port->devlink;
        void *hdr;
 
        hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
@@ -1039,53 +1040,47 @@ nla_put_failure:
 static void devlink_port_notify(struct devlink_port *devlink_port,
                                enum devlink_command cmd)
 {
-       struct devlink *devlink = devlink_port->devlink;
        struct sk_buff *msg;
        int err;
 
-       if (!devlink_port->registered)
-               return;
-
        WARN_ON(cmd != DEVLINK_CMD_PORT_NEW && cmd != DEVLINK_CMD_PORT_DEL);
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       err = devlink_nl_port_fill(msg, devlink, devlink_port, cmd, 0, 0, 0,
-                                  NULL);
+       err = devlink_nl_port_fill(msg, devlink_port, cmd, 0, 0, 0, NULL);
        if (err) {
                nlmsg_free(msg);
                return;
        }
 
-       genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
-                               msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
+       genlmsg_multicast_netns(&devlink_nl_family,
+                               devlink_net(devlink_port->devlink), msg, 0,
+                               DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 }
 
 static void devlink_rate_notify(struct devlink_rate *devlink_rate,
                                enum devlink_command cmd)
 {
-       struct devlink *devlink = devlink_rate->devlink;
        struct sk_buff *msg;
        int err;
 
-       WARN_ON(cmd != DEVLINK_CMD_RATE_NEW &&
-               cmd != DEVLINK_CMD_RATE_DEL);
+       WARN_ON(cmd != DEVLINK_CMD_RATE_NEW && cmd != DEVLINK_CMD_RATE_DEL);
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       err = devlink_nl_rate_fill(msg, devlink, devlink_rate,
-                                  cmd, 0, 0, 0, NULL);
+       err = devlink_nl_rate_fill(msg, devlink_rate, cmd, 0, 0, 0, NULL);
        if (err) {
                nlmsg_free(msg);
                return;
        }
 
-       genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
-                               msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
+       genlmsg_multicast_netns(&devlink_nl_family,
+                               devlink_net(devlink_rate->devlink), msg, 0,
+                               DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 }
 
 static int devlink_nl_cmd_rate_get_dumpit(struct sk_buff *msg,
@@ -1094,13 +1089,18 @@ static int devlink_nl_cmd_rate_get_dumpit(struct sk_buff *msg,
        struct devlink_rate *devlink_rate;
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        int idx = 0;
        int err = 0;
 
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
-               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
                        enum devlink_command cmd = DEVLINK_CMD_RATE_NEW;
@@ -1110,18 +1110,19 @@ static int devlink_nl_cmd_rate_get_dumpit(struct sk_buff *msg,
                                idx++;
                                continue;
                        }
-                       err = devlink_nl_rate_fill(msg, devlink,
-                                                  devlink_rate,
-                                                  cmd, id,
+                       err = devlink_nl_rate_fill(msg, devlink_rate, cmd, id,
                                                   cb->nlh->nlmsg_seq,
                                                   NLM_F_MULTI, NULL);
                        if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                        idx++;
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -1136,7 +1137,6 @@ static int devlink_nl_cmd_rate_get_doit(struct sk_buff *skb,
                                        struct genl_info *info)
 {
        struct devlink_rate *devlink_rate = info->user_ptr[1];
-       struct devlink *devlink = devlink_rate->devlink;
        struct sk_buff *msg;
        int err;
 
@@ -1144,8 +1144,7 @@ static int devlink_nl_cmd_rate_get_doit(struct sk_buff *skb,
        if (!msg)
                return -ENOMEM;
 
-       err = devlink_nl_rate_fill(msg, devlink, devlink_rate,
-                                  DEVLINK_CMD_RATE_NEW,
+       err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW,
                                   info->snd_portid, info->snd_seq, 0,
                                   info->extack);
        if (err) {
@@ -1193,20 +1192,30 @@ static int devlink_nl_cmd_get_dumpit(struct sk_buff *msg,
 {
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        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)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) {
+                       devlink_put(devlink);
+                       continue;
+               }
+
                if (idx < start) {
                        idx++;
+                       devlink_put(devlink);
                        continue;
                }
+
                err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
                                      NETLINK_CB(cb->skb).portid,
                                      cb->nlh->nlmsg_seq, NLM_F_MULTI);
+               devlink_put(devlink);
                if (err)
                        goto out;
                idx++;
@@ -1222,7 +1231,6 @@ static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb,
                                        struct genl_info *info)
 {
        struct devlink_port *devlink_port = info->user_ptr[1];
-       struct devlink *devlink = devlink_port->devlink;
        struct sk_buff *msg;
        int err;
 
@@ -1230,8 +1238,7 @@ static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb,
        if (!msg)
                return -ENOMEM;
 
-       err = devlink_nl_port_fill(msg, devlink, devlink_port,
-                                  DEVLINK_CMD_PORT_NEW,
+       err = devlink_nl_port_fill(msg, devlink_port, DEVLINK_CMD_PORT_NEW,
                                   info->snd_portid, info->snd_seq, 0,
                                   info->extack);
        if (err) {
@@ -1248,32 +1255,39 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg,
        struct devlink *devlink;
        struct devlink_port *devlink_port;
        int start = cb->args[0];
+       unsigned long index;
        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)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(devlink_port, &devlink->port_list, list) {
                        if (idx < start) {
                                idx++;
                                continue;
                        }
-                       err = devlink_nl_port_fill(msg, devlink, devlink_port,
+                       err = devlink_nl_port_fill(msg, devlink_port,
                                                   DEVLINK_CMD_NEW,
                                                   NETLINK_CB(cb->skb).portid,
                                                   cb->nlh->nlmsg_seq,
-                                                  NLM_F_MULTI,
-                                                  cb->extack);
+                                                  NLM_F_MULTI, cb->extack);
                        if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                        idx++;
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -1282,31 +1296,33 @@ out:
        return msg->len;
 }
 
-static int devlink_port_type_set(struct devlink *devlink,
-                                struct devlink_port *devlink_port,
+static int devlink_port_type_set(struct devlink_port *devlink_port,
                                 enum devlink_port_type port_type)
 
 {
        int err;
 
-       if (devlink->ops->port_type_set) {
-               if (port_type == devlink_port->type)
-                       return 0;
-               err = devlink->ops->port_type_set(devlink_port, port_type);
-               if (err)
-                       return err;
-               devlink_port->desired_type = port_type;
-               devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
+       if (!devlink_port->devlink->ops->port_type_set)
+               return -EOPNOTSUPP;
+
+       if (port_type == devlink_port->type)
                return 0;
-       }
-       return -EOPNOTSUPP;
+
+       err = devlink_port->devlink->ops->port_type_set(devlink_port,
+                                                       port_type);
+       if (err)
+               return err;
+
+       devlink_port->desired_type = port_type;
+       devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
+       return 0;
 }
 
-static int
-devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *port,
-                                 const struct nlattr *attr, struct netlink_ext_ack *extack)
+static int devlink_port_function_hw_addr_set(struct devlink_port *port,
+                                            const struct nlattr *attr,
+                                            struct netlink_ext_ack *extack)
 {
-       const struct devlink_ops *ops;
+       const struct devlink_ops *ops = port->devlink->ops;
        const u8 *hw_addr;
        int hw_addr_len;
 
@@ -1327,17 +1343,16 @@ devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *
                }
        }
 
-       ops = devlink->ops;
        if (!ops->port_function_hw_addr_set) {
                NL_SET_ERR_MSG_MOD(extack, "Port doesn't support function attributes");
                return -EOPNOTSUPP;
        }
 
-       return ops->port_function_hw_addr_set(devlink, port, hw_addr, hw_addr_len, extack);
+       return ops->port_function_hw_addr_set(port, hw_addr, hw_addr_len,
+                                             extack);
 }
 
-static int devlink_port_fn_state_set(struct devlink *devlink,
-                                    struct devlink_port *port,
+static int devlink_port_fn_state_set(struct devlink_port *port,
                                     const struct nlattr *attr,
                                     struct netlink_ext_ack *extack)
 {
@@ -1345,18 +1360,18 @@ static int devlink_port_fn_state_set(struct devlink *devlink,
        const struct devlink_ops *ops;
 
        state = nla_get_u8(attr);
-       ops = devlink->ops;
+       ops = port->devlink->ops;
        if (!ops->port_fn_state_set) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "Function does not support state setting");
                return -EOPNOTSUPP;
        }
-       return ops->port_fn_state_set(devlink, port, state, extack);
+       return ops->port_fn_state_set(port, state, extack);
 }
 
-static int
-devlink_port_function_set(struct devlink *devlink, struct devlink_port *port,
-                         const struct nlattr *attr, struct netlink_ext_ack *extack)
+static int devlink_port_function_set(struct devlink_port *port,
+                                    const struct nlattr *attr,
+                                    struct netlink_ext_ack *extack)
 {
        struct nlattr *tb[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1];
        int err;
@@ -1370,7 +1385,7 @@ devlink_port_function_set(struct devlink *devlink, struct devlink_port *port,
 
        attr = tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR];
        if (attr) {
-               err = devlink_port_function_hw_addr_set(devlink, port, attr, extack);
+               err = devlink_port_function_hw_addr_set(port, attr, extack);
                if (err)
                        return err;
        }
@@ -1380,7 +1395,7 @@ devlink_port_function_set(struct devlink *devlink, struct devlink_port *port,
         */
        attr = tb[DEVLINK_PORT_FN_ATTR_STATE];
        if (attr)
-               err = devlink_port_fn_state_set(devlink, port, attr, extack);
+               err = devlink_port_fn_state_set(port, attr, extack);
 
        if (!err)
                devlink_port_notify(port, DEVLINK_CMD_PORT_NEW);
@@ -1391,14 +1406,13 @@ static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,
                                        struct genl_info *info)
 {
        struct devlink_port *devlink_port = info->user_ptr[1];
-       struct devlink *devlink = devlink_port->devlink;
        int err;
 
        if (info->attrs[DEVLINK_ATTR_PORT_TYPE]) {
                enum devlink_port_type port_type;
 
                port_type = nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_TYPE]);
-               err = devlink_port_type_set(devlink, devlink_port, port_type);
+               err = devlink_port_type_set(devlink_port, port_type);
                if (err)
                        return err;
        }
@@ -1407,7 +1421,7 @@ static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,
                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);
+               err = devlink_port_function_set(devlink_port, attr, extack);
                if (err)
                        return err;
        }
@@ -1502,9 +1516,8 @@ static int devlink_port_new_notifiy(struct devlink *devlink,
                goto out;
        }
 
-       err = devlink_nl_port_fill(msg, devlink, devlink_port,
-                                  DEVLINK_CMD_NEW, info->snd_portid,
-                                  info->snd_seq, 0, NULL);
+       err = devlink_nl_port_fill(msg, devlink_port, DEVLINK_CMD_NEW,
+                                  info->snd_portid, info->snd_seq, 0, NULL);
        if (err)
                goto out;
 
@@ -1908,13 +1921,18 @@ static int devlink_nl_cmd_sb_get_dumpit(struct sk_buff *msg,
        struct devlink *devlink;
        struct devlink_sb *devlink_sb;
        int start = cb->args[0];
+       unsigned long index;
        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)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
                        if (idx < start) {
@@ -1928,11 +1946,14 @@ static int devlink_nl_cmd_sb_get_dumpit(struct sk_buff *msg,
                                                 NLM_F_MULTI);
                        if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                        idx++;
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -2052,14 +2073,19 @@ static int devlink_nl_cmd_sb_pool_get_dumpit(struct sk_buff *msg,
        struct devlink *devlink;
        struct devlink_sb *devlink_sb;
        int start = cb->args[0];
+       unsigned long index;
        int idx = 0;
        int err = 0;
 
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
+                       continue;
+
                if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
                    !devlink->ops->sb_pool_get)
-                       continue;
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
                        err = __sb_pool_get_dumpit(msg, start, &idx, devlink,
@@ -2070,10 +2096,13 @@ static int devlink_nl_cmd_sb_pool_get_dumpit(struct sk_buff *msg,
                                err = 0;
                        } else if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -2265,14 +2294,19 @@ static int devlink_nl_cmd_sb_port_pool_get_dumpit(struct sk_buff *msg,
        struct devlink *devlink;
        struct devlink_sb *devlink_sb;
        int start = cb->args[0];
+       unsigned long index;
        int idx = 0;
        int err = 0;
 
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
+                       continue;
+
                if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
                    !devlink->ops->sb_port_pool_get)
-                       continue;
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
                        err = __sb_port_pool_get_dumpit(msg, start, &idx,
@@ -2283,10 +2317,13 @@ static int devlink_nl_cmd_sb_port_pool_get_dumpit(struct sk_buff *msg,
                                err = 0;
                        } else if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -2506,14 +2543,18 @@ devlink_nl_cmd_sb_tc_pool_bind_get_dumpit(struct sk_buff *msg,
        struct devlink *devlink;
        struct devlink_sb *devlink_sb;
        int start = cb->args[0];
+       unsigned long index;
        int idx = 0;
        int err = 0;
 
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
+                       continue;
+
                if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
                    !devlink->ops->sb_tc_pool_bind_get)
-                       continue;
+                       goto retry;
 
                mutex_lock(&devlink->lock);
                list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
@@ -2526,10 +2567,13 @@ devlink_nl_cmd_sb_tc_pool_bind_get_dumpit(struct sk_buff *msg,
                                err = 0;
                        } else if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -3801,10 +3845,12 @@ static void devlink_param_notify(struct devlink *devlink,
                                 struct devlink_param_item *param_item,
                                 enum devlink_command cmd);
 
-static void devlink_reload_netns_change(struct devlink *devlink,
-                                       struct net *dest_net)
+static void devlink_ns_change_notify(struct devlink *devlink,
+                                    struct net *dest_net, struct net *curr_net,
+                                    bool new)
 {
        struct devlink_param_item *param_item;
+       enum devlink_command cmd;
 
        /* Userspace needs to be notified about devlink objects
         * removed from original and entering new network namespace.
@@ -3812,17 +3858,18 @@ static void devlink_reload_netns_change(struct devlink *devlink,
         * reload process so the notifications are generated separatelly.
         */
 
-       list_for_each_entry(param_item, &devlink->param_list, list)
-               devlink_param_notify(devlink, 0, param_item,
-                                    DEVLINK_CMD_PARAM_DEL);
-       devlink_notify(devlink, DEVLINK_CMD_DEL);
+       if (!dest_net || net_eq(dest_net, curr_net))
+               return;
 
-       __devlink_net_set(devlink, dest_net);
+       if (new)
+               devlink_notify(devlink, DEVLINK_CMD_NEW);
 
-       devlink_notify(devlink, DEVLINK_CMD_NEW);
+       cmd = new ? DEVLINK_CMD_PARAM_NEW : DEVLINK_CMD_PARAM_DEL;
        list_for_each_entry(param_item, &devlink->param_list, list)
-               devlink_param_notify(devlink, 0, param_item,
-                                    DEVLINK_CMD_PARAM_NEW);
+               devlink_param_notify(devlink, 0, param_item, cmd);
+
+       if (!new)
+               devlink_notify(devlink, DEVLINK_CMD_DEL);
 }
 
 static bool devlink_reload_supported(const struct devlink_ops *ops)
@@ -3902,6 +3949,7 @@ static int devlink_reload(struct devlink *devlink, struct net *dest_net,
                          u32 *actions_performed, struct netlink_ext_ack *extack)
 {
        u32 remote_reload_stats[DEVLINK_RELOAD_STATS_ARRAY_SIZE];
+       struct net *curr_net;
        int err;
 
        if (!devlink->reload_enabled)
@@ -3909,18 +3957,22 @@ static int devlink_reload(struct devlink *devlink, struct net *dest_net,
 
        memcpy(remote_reload_stats, devlink->stats.remote_reload_stats,
               sizeof(remote_reload_stats));
+
+       curr_net = devlink_net(devlink);
+       devlink_ns_change_notify(devlink, dest_net, curr_net, false);
        err = devlink->ops->reload_down(devlink, !!dest_net, action, limit, extack);
        if (err)
                return err;
 
-       if (dest_net && !net_eq(dest_net, devlink_net(devlink)))
-               devlink_reload_netns_change(devlink, dest_net);
+       if (dest_net && !net_eq(dest_net, curr_net))
+               write_pnet(&devlink->_net, dest_net);
 
        err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack);
        devlink_reload_failed_set(devlink, !!err);
        if (err)
                return err;
 
+       devlink_ns_change_notify(devlink, dest_net, curr_net, true);
        WARN_ON(!(*actions_performed & BIT(action)));
        /* Catch driver on updating the remote action within devlink reload */
        WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats,
@@ -4117,7 +4169,7 @@ out_free_msg:
 
 static void devlink_flash_update_begin_notify(struct devlink *devlink)
 {
-       struct devlink_flash_notify params = { 0 };
+       struct devlink_flash_notify params = {};
 
        __devlink_flash_update_notify(devlink,
                                      DEVLINK_CMD_FLASH_UPDATE,
@@ -4126,7 +4178,7 @@ static void devlink_flash_update_begin_notify(struct devlink *devlink)
 
 static void devlink_flash_update_end_notify(struct devlink *devlink)
 {
-       struct devlink_flash_notify params = { 0 };
+       struct devlink_flash_notify params = {};
 
        __devlink_flash_update_notify(devlink,
                                      DEVLINK_CMD_FLASH_UPDATE_END,
@@ -4283,6 +4335,21 @@ static const struct devlink_param devlink_param_generic[] = {
                .name = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_NAME,
                .type = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_TYPE,
        },
+       {
+               .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH,
+               .name = DEVLINK_PARAM_GENERIC_ENABLE_ETH_NAME,
+               .type = DEVLINK_PARAM_GENERIC_ENABLE_ETH_TYPE,
+       },
+       {
+               .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,
+               .name = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_NAME,
+               .type = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_TYPE,
+       },
+       {
+               .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,
+               .name = DEVLINK_PARAM_GENERIC_ENABLE_VNET_NAME,
+               .type = DEVLINK_PARAM_GENERIC_ENABLE_VNET_TYPE,
+       },
 };
 
 static int devlink_param_generic_verify(const struct devlink_param *param)
@@ -4553,13 +4620,18 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg,
        struct devlink_param_item *param_item;
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        int idx = 0;
        int err = 0;
 
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
-               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(param_item, &devlink->param_list, list) {
                        if (idx < start) {
@@ -4575,11 +4647,14 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg,
                                err = 0;
                        } else if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                        idx++;
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -4821,13 +4896,18 @@ static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg,
        struct devlink_port *devlink_port;
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        int idx = 0;
        int err = 0;
 
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
-               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(devlink_port, &devlink->port_list, list) {
                        list_for_each_entry(param_item,
@@ -4847,12 +4927,15 @@ static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg,
                                        err = 0;
                                } else if (err) {
                                        mutex_unlock(&devlink->lock);
+                                       devlink_put(devlink);
                                        goto out;
                                }
                                idx++;
                        }
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -5062,7 +5145,6 @@ static void devlink_nl_region_notify(struct devlink_region *region,
                                     struct devlink_snapshot *snapshot,
                                     enum devlink_command cmd)
 {
-       struct devlink *devlink = region->devlink;
        struct sk_buff *msg;
 
        WARN_ON(cmd != DEVLINK_CMD_REGION_NEW && cmd != DEVLINK_CMD_REGION_DEL);
@@ -5071,8 +5153,9 @@ static void devlink_nl_region_notify(struct devlink_region *region,
        if (IS_ERR(msg))
                return;
 
-       genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
-                               msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
+       genlmsg_multicast_netns(&devlink_nl_family,
+                               devlink_net(region->devlink), msg, 0,
+                               DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 }
 
 /**
@@ -5390,15 +5473,22 @@ static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg,
 {
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        int idx = 0;
-       int err;
+       int err = 0;
 
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
-               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                err = devlink_nl_cmd_region_get_devlink_dumpit(msg, cb, devlink,
                                                               &idx, start);
+retry:
+               devlink_put(devlink);
                if (err)
                        goto out;
        }
@@ -5761,6 +5851,7 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
        nla_nest_end(skb, chunks_attr);
        genlmsg_end(skb, hdr);
        mutex_unlock(&devlink->lock);
+       devlink_put(devlink);
        mutex_unlock(&devlink_mutex);
 
        return skb->len;
@@ -5769,6 +5860,7 @@ nla_put_failure:
        genlmsg_cancel(skb, hdr);
 out_unlock:
        mutex_unlock(&devlink->lock);
+       devlink_put(devlink);
 out_dev:
        mutex_unlock(&devlink_mutex);
        return err;
@@ -5915,22 +6007,20 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg,
 {
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        int idx = 0;
        int err = 0;
 
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
-               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
-               if (idx < start) {
-                       idx++;
-                       continue;
-               }
 
-               if (!devlink->ops->info_get) {
-                       idx++;
-                       continue;
-               }
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
+               if (idx < start || !devlink->ops->info_get)
+                       goto inc;
 
                mutex_lock(&devlink->lock);
                err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
@@ -5940,9 +6030,14 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg,
                mutex_unlock(&devlink->lock);
                if (err == -EOPNOTSUPP)
                        err = 0;
-               else if (err)
+               else if (err) {
+                       devlink_put(devlink);
                        break;
+               }
+inc:
                idx++;
+retry:
+               devlink_put(devlink);
        }
        mutex_unlock(&devlink_mutex);
 
@@ -6756,11 +6851,11 @@ EXPORT_SYMBOL_GPL(devlink_port_health_reporter_destroy);
 
 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 devlink *devlink = reporter->devlink;
        struct nlattr *reporter_attr;
        void *hdr;
 
@@ -6837,8 +6932,7 @@ static void devlink_recover_notify(struct devlink_health_reporter *reporter,
        if (!msg)
                return;
 
-       err = devlink_nl_health_reporter_fill(msg, reporter->devlink,
-                                             reporter, cmd, 0, 0, 0);
+       err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
        if (err) {
                nlmsg_free(msg);
                return;
@@ -7028,6 +7122,7 @@ devlink_health_reporter_get_from_cb(struct netlink_callback *cb)
                goto unlock;
 
        reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
+       devlink_put(devlink);
        mutex_unlock(&devlink_mutex);
        return reporter;
 unlock:
@@ -7071,7 +7166,7 @@ static int devlink_nl_cmd_health_reporter_get_doit(struct sk_buff *skb,
                goto out;
        }
 
-       err = devlink_nl_health_reporter_fill(msg, devlink, reporter,
+       err = devlink_nl_health_reporter_fill(msg, reporter,
                                              DEVLINK_CMD_HEALTH_REPORTER_GET,
                                              info->snd_portid, info->snd_seq,
                                              0);
@@ -7094,13 +7189,18 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
        struct devlink_port *port;
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        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)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry_rep;
+
                mutex_lock(&devlink->reporters_lock);
                list_for_each_entry(reporter, &devlink->reporter_list,
                                    list) {
@@ -7108,24 +7208,29 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
                                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);
+                       err = devlink_nl_health_reporter_fill(
+                               msg, reporter, DEVLINK_CMD_HEALTH_REPORTER_GET,
+                               NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+                               NLM_F_MULTI);
                        if (err) {
                                mutex_unlock(&devlink->reporters_lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                        idx++;
                }
                mutex_unlock(&devlink->reporters_lock);
+retry_rep:
+               devlink_put(devlink);
        }
 
-       list_for_each_entry(devlink, &devlink_list, list) {
-               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry_port;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(port, &devlink->port_list, list) {
                        mutex_lock(&port->reporters_lock);
@@ -7134,14 +7239,15 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
                                        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);
+                               err = devlink_nl_health_reporter_fill(
+                                       msg, 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);
                                        mutex_unlock(&devlink->lock);
+                                       devlink_put(devlink);
                                        goto out;
                                }
                                idx++;
@@ -7149,6 +7255,8 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
                        mutex_unlock(&port->reporters_lock);
                }
                mutex_unlock(&devlink->lock);
+retry_port:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -7677,13 +7785,18 @@ static int devlink_nl_cmd_trap_get_dumpit(struct sk_buff *msg,
        struct devlink_trap_item *trap_item;
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        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)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(trap_item, &devlink->trap_list, list) {
                        if (idx < start) {
@@ -7697,11 +7810,14 @@ static int devlink_nl_cmd_trap_get_dumpit(struct sk_buff *msg,
                                                   NLM_F_MULTI);
                        if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                        idx++;
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -7896,13 +8012,18 @@ static int devlink_nl_cmd_trap_group_get_dumpit(struct sk_buff *msg,
        u32 portid = NETLINK_CB(cb->skb).portid;
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        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)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(group_item, &devlink->trap_group_list,
                                    list) {
@@ -7917,11 +8038,14 @@ static int devlink_nl_cmd_trap_group_get_dumpit(struct sk_buff *msg,
                                                         NLM_F_MULTI);
                        if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                        idx++;
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -8202,13 +8326,18 @@ static int devlink_nl_cmd_trap_policer_get_dumpit(struct sk_buff *msg,
        u32 portid = NETLINK_CB(cb->skb).portid;
        struct devlink *devlink;
        int start = cb->args[0];
+       unsigned long index;
        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)))
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
                        continue;
+
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       goto retry;
+
                mutex_lock(&devlink->lock);
                list_for_each_entry(policer_item, &devlink->trap_policer_list,
                                    list) {
@@ -8223,11 +8352,14 @@ static int devlink_nl_cmd_trap_policer_get_dumpit(struct sk_buff *msg,
                                                           NLM_F_MULTI);
                        if (err) {
                                mutex_unlock(&devlink->lock);
+                               devlink_put(devlink);
                                goto out;
                        }
                        idx++;
                }
                mutex_unlock(&devlink->lock);
+retry:
+               devlink_put(devlink);
        }
 out:
        mutex_unlock(&devlink_mutex);
@@ -8768,30 +8900,44 @@ static bool devlink_reload_actions_valid(const struct devlink_ops *ops)
 }
 
 /**
- *     devlink_alloc - Allocate new devlink instance resources
+ *     devlink_alloc_ns - Allocate new devlink instance resources
+ *     in specific namespace
  *
  *     @ops: ops
  *     @priv_size: size of user private data
+ *     @net: net namespace
+ *     @dev: parent device
  *
  *     Allocate new devlink instance resources, including devlink index
  *     and name.
  */
-struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
+struct devlink *devlink_alloc_ns(const struct devlink_ops *ops,
+                                size_t priv_size, struct net *net,
+                                struct device *dev)
 {
        struct devlink *devlink;
+       static u32 last_id;
+       int ret;
 
-       if (WARN_ON(!ops))
-               return NULL;
-
+       WARN_ON(!ops || !dev);
        if (!devlink_reload_actions_valid(ops))
                return NULL;
 
        devlink = kzalloc(sizeof(*devlink) + priv_size, GFP_KERNEL);
        if (!devlink)
                return NULL;
+
+       ret = xa_alloc_cyclic(&devlinks, &devlink->index, devlink, xa_limit_31b,
+                             &last_id, GFP_KERNEL);
+       if (ret < 0) {
+               kfree(devlink);
+               return NULL;
+       }
+
+       devlink->dev = dev;
        devlink->ops = ops;
        xa_init_flags(&devlink->snapshot_ids, XA_FLAGS_ALLOC);
-       __devlink_net_set(devlink, &init_net);
+       write_pnet(&devlink->_net, net);
        INIT_LIST_HEAD(&devlink->port_list);
        INIT_LIST_HEAD(&devlink->rate_list);
        INIT_LIST_HEAD(&devlink->sb_list);
@@ -8805,22 +8951,22 @@ struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
        INIT_LIST_HEAD(&devlink->trap_policer_list);
        mutex_init(&devlink->lock);
        mutex_init(&devlink->reporters_lock);
+       refcount_set(&devlink->refcount, 1);
+       init_completion(&devlink->comp);
+
        return devlink;
 }
-EXPORT_SYMBOL_GPL(devlink_alloc);
+EXPORT_SYMBOL_GPL(devlink_alloc_ns);
 
 /**
  *     devlink_register - Register devlink instance
  *
  *     @devlink: devlink
- *     @dev: parent device
  */
-int devlink_register(struct devlink *devlink, struct device *dev)
+int devlink_register(struct devlink *devlink)
 {
-       devlink->dev = dev;
-       devlink->registered = true;
        mutex_lock(&devlink_mutex);
-       list_add_tail(&devlink->list, &devlink_list);
+       xa_set_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
        devlink_notify(devlink, DEVLINK_CMD_NEW);
        mutex_unlock(&devlink_mutex);
        return 0;
@@ -8834,11 +8980,14 @@ EXPORT_SYMBOL_GPL(devlink_register);
  */
 void devlink_unregister(struct devlink *devlink)
 {
+       devlink_put(devlink);
+       wait_for_completion(&devlink->comp);
+
        mutex_lock(&devlink_mutex);
        WARN_ON(devlink_reload_supported(devlink->ops) &&
                devlink->reload_enabled);
        devlink_notify(devlink, DEVLINK_CMD_DEL);
-       list_del(&devlink->list);
+       xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
        mutex_unlock(&devlink_mutex);
 }
 EXPORT_SYMBOL_GPL(devlink_unregister);
@@ -8900,6 +9049,7 @@ void devlink_free(struct devlink *devlink)
        WARN_ON(!list_empty(&devlink->port_list));
 
        xa_destroy(&devlink->snapshot_ids);
+       xa_erase(&devlinks, devlink->index);
 
        kfree(devlink);
 }
@@ -8960,9 +9110,10 @@ int devlink_port_register(struct devlink *devlink,
                mutex_unlock(&devlink->lock);
                return -EEXIST;
        }
+
+       WARN_ON(devlink_port->devlink);
        devlink_port->devlink = devlink;
        devlink_port->index = port_index;
-       devlink_port->registered = true;
        spin_lock_init(&devlink_port->type_lock);
        INIT_LIST_HEAD(&devlink_port->reporter_list);
        mutex_init(&devlink_port->reporters_lock);
@@ -9001,7 +9152,7 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port,
                                    enum devlink_port_type type,
                                    void *type_dev)
 {
-       if (WARN_ON(!devlink_port->registered))
+       if (WARN_ON(!devlink_port->devlink))
                return;
        devlink_port_type_warn_cancel(devlink_port);
        spin_lock_bh(&devlink_port->type_lock);
@@ -9121,7 +9272,7 @@ void devlink_port_attrs_set(struct devlink_port *devlink_port,
 {
        int ret;
 
-       if (WARN_ON(devlink_port->registered))
+       if (WARN_ON(devlink_port->devlink))
                return;
        devlink_port->attrs = *attrs;
        ret = __devlink_port_attrs_set(devlink_port, attrs->flavour);
@@ -9145,7 +9296,7 @@ void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port, u32 contro
        struct devlink_port_attrs *attrs = &devlink_port->attrs;
        int ret;
 
-       if (WARN_ON(devlink_port->registered))
+       if (WARN_ON(devlink_port->devlink))
                return;
        ret = __devlink_port_attrs_set(devlink_port,
                                       DEVLINK_PORT_FLAVOUR_PCI_PF);
@@ -9172,7 +9323,7 @@ void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port, u32 contro
        struct devlink_port_attrs *attrs = &devlink_port->attrs;
        int ret;
 
-       if (WARN_ON(devlink_port->registered))
+       if (WARN_ON(devlink_port->devlink))
                return;
        ret = __devlink_port_attrs_set(devlink_port,
                                       DEVLINK_PORT_FLAVOUR_PCI_VF);
@@ -9200,7 +9351,7 @@ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 contro
        struct devlink_port_attrs *attrs = &devlink_port->attrs;
        int ret;
 
-       if (WARN_ON(devlink_port->registered))
+       if (WARN_ON(devlink_port->devlink))
                return;
        ret = __devlink_port_attrs_set(devlink_port,
                                       DEVLINK_PORT_FLAVOUR_PCI_SF);
@@ -9788,6 +9939,22 @@ static int devlink_param_verify(const struct devlink_param *param)
                return devlink_param_driver_verify(param);
 }
 
+static int __devlink_param_register_one(struct devlink *devlink,
+                                       unsigned int port_index,
+                                       struct list_head *param_list,
+                                       const struct devlink_param *param,
+                                       enum devlink_command reg_cmd)
+{
+       int err;
+
+       err = devlink_param_verify(param);
+       if (err)
+               return err;
+
+       return devlink_param_register_one(devlink, port_index,
+                                         param_list, param, reg_cmd);
+}
+
 static int __devlink_params_register(struct devlink *devlink,
                                     unsigned int port_index,
                                     struct list_head *param_list,
@@ -9802,12 +9969,8 @@ static int __devlink_params_register(struct devlink *devlink,
 
        mutex_lock(&devlink->lock);
        for (i = 0; i < params_count; i++, param++) {
-               err = devlink_param_verify(param);
-               if (err)
-                       goto rollback;
-
-               err = devlink_param_register_one(devlink, port_index,
-                                                param_list, param, reg_cmd);
+               err = __devlink_param_register_one(devlink, port_index,
+                                                  param_list, param, reg_cmd);
                if (err)
                        goto rollback;
        }
@@ -9879,6 +10042,43 @@ void devlink_params_unregister(struct devlink *devlink,
 }
 EXPORT_SYMBOL_GPL(devlink_params_unregister);
 
+/**
+ * devlink_param_register - register one configuration parameter
+ *
+ * @devlink: devlink
+ * @param: one configuration parameter
+ *
+ * Register the configuration parameter supported by the driver.
+ * Return: returns 0 on successful registration or error code otherwise.
+ */
+int devlink_param_register(struct devlink *devlink,
+                          const struct devlink_param *param)
+{
+       int err;
+
+       mutex_lock(&devlink->lock);
+       err = __devlink_param_register_one(devlink, 0, &devlink->param_list,
+                                          param, DEVLINK_CMD_PARAM_NEW);
+       mutex_unlock(&devlink->lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(devlink_param_register);
+
+/**
+ * devlink_param_unregister - unregister one configuration parameter
+ * @devlink: devlink
+ * @param: configuration parameter to unregister
+ */
+void devlink_param_unregister(struct devlink *devlink,
+                             const struct devlink_param *param)
+{
+       mutex_lock(&devlink->lock);
+       devlink_param_unregister_one(devlink, 0, &devlink->param_list, param,
+                                    DEVLINK_CMD_PARAM_DEL);
+       mutex_unlock(&devlink->lock);
+}
+EXPORT_SYMBOL_GPL(devlink_param_unregister);
+
 /**
  *     devlink_params_publish - publish configuration parameters
  *
@@ -9921,6 +10121,54 @@ void devlink_params_unpublish(struct devlink *devlink)
 }
 EXPORT_SYMBOL_GPL(devlink_params_unpublish);
 
+/**
+ * devlink_param_publish - publish one configuration parameter
+ *
+ * @devlink: devlink
+ * @param: one configuration parameter
+ *
+ * Publish previously registered configuration parameter.
+ */
+void devlink_param_publish(struct devlink *devlink,
+                          const struct devlink_param *param)
+{
+       struct devlink_param_item *param_item;
+
+       list_for_each_entry(param_item, &devlink->param_list, list) {
+               if (param_item->param != param || param_item->published)
+                       continue;
+               param_item->published = true;
+               devlink_param_notify(devlink, 0, param_item,
+                                    DEVLINK_CMD_PARAM_NEW);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(devlink_param_publish);
+
+/**
+ * devlink_param_unpublish - unpublish one configuration parameter
+ *
+ * @devlink: devlink
+ * @param: one configuration parameter
+ *
+ * Unpublish previously registered configuration parameter.
+ */
+void devlink_param_unpublish(struct devlink *devlink,
+                            const struct devlink_param *param)
+{
+       struct devlink_param_item *param_item;
+
+       list_for_each_entry(param_item, &devlink->param_list, list) {
+               if (param_item->param != param || !param_item->published)
+                       continue;
+               param_item->published = false;
+               devlink_param_notify(devlink, 0, param_item,
+                                    DEVLINK_CMD_PARAM_DEL);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(devlink_param_unpublish);
+
 /**
  *     devlink_port_params_register - register port configuration parameters
  *
@@ -11276,23 +11524,29 @@ static void __net_exit devlink_pernet_pre_exit(struct net *net)
 {
        struct devlink *devlink;
        u32 actions_performed;
+       unsigned long index;
        int err;
 
        /* In case network namespace is getting destroyed, reload
         * all devlink instances from this namespace into init_net.
         */
        mutex_lock(&devlink_mutex);
-       list_for_each_entry(devlink, &devlink_list, list) {
-               if (net_eq(devlink_net(devlink), net)) {
-                       if (WARN_ON(!devlink_reload_supported(devlink->ops)))
-                               continue;
-                       err = devlink_reload(devlink, &init_net,
-                                            DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
-                                            DEVLINK_RELOAD_LIMIT_UNSPEC,
-                                            &actions_performed, NULL);
-                       if (err && err != -EOPNOTSUPP)
-                               pr_warn("Failed to reload devlink instance into init_net\n");
-               }
+       xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) {
+               if (!devlink_try_get(devlink))
+                       continue;
+
+               if (!net_eq(devlink_net(devlink), net))
+                       goto retry;
+
+               WARN_ON(!devlink_reload_supported(devlink->ops));
+               err = devlink_reload(devlink, &init_net,
+                                    DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
+                                    DEVLINK_RELOAD_LIMIT_UNSPEC,
+                                    &actions_performed, NULL);
+               if (err && err != -EOPNOTSUPP)
+                       pr_warn("Failed to reload devlink instance into init_net\n");
+retry:
+               devlink_put(devlink);
        }
        mutex_unlock(&devlink_mutex);
 }