Merge tag 'hwmon-for-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
[linux-block.git] / net / core / rtnetlink.c
index bd50e9fe3234b6c252a199f05adcae2a09b1bbdd..a3d7847ce69d36401051526acf30211d96e2b24e 100644 (file)
@@ -483,24 +483,15 @@ EXPORT_SYMBOL_GPL(__rtnl_link_unregister);
  */
 static void rtnl_lock_unregistering_all(void)
 {
-       struct net *net;
-       bool unregistering;
        DEFINE_WAIT_FUNC(wait, woken_wake_function);
 
        add_wait_queue(&netdev_unregistering_wq, &wait);
        for (;;) {
-               unregistering = false;
                rtnl_lock();
                /* We held write locked pernet_ops_rwsem, and parallel
                 * setup_net() and cleanup_net() are not possible.
                 */
-               for_each_net(net) {
-                       if (atomic_read(&net->dev_unreg_count) > 0) {
-                               unregistering = true;
-                               break;
-                       }
-               }
-               if (!unregistering)
+               if (!atomic_read(&dev_unreg_count))
                        break;
                __rtnl_unlock();
 
@@ -851,9 +842,22 @@ int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
 }
 EXPORT_SYMBOL_GPL(rtnl_put_cacheinfo);
 
+void netdev_set_operstate(struct net_device *dev, int newstate)
+{
+       unsigned int old = READ_ONCE(dev->operstate);
+
+       do {
+               if (old == newstate)
+                       return;
+       } while (!try_cmpxchg(&dev->operstate, &old, newstate));
+
+       netdev_state_change(dev);
+}
+EXPORT_SYMBOL(netdev_set_operstate);
+
 static void set_operstate(struct net_device *dev, unsigned char transition)
 {
-       unsigned char operstate = dev->operstate;
+       unsigned char operstate = READ_ONCE(dev->operstate);
 
        switch (transition) {
        case IF_OPER_UP:
@@ -875,12 +879,7 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
                break;
        }
 
-       if (dev->operstate != operstate) {
-               write_lock(&dev_base_lock);
-               dev->operstate = operstate;
-               write_unlock(&dev_base_lock);
-               netdev_state_change(dev);
-       }
+       netdev_set_operstate(dev, operstate);
 }
 
 static unsigned int rtnl_dev_get_flags(const struct net_device *dev)
@@ -1456,17 +1455,18 @@ static noinline_for_stack int rtnl_fill_vf(struct sk_buff *skb,
        return 0;
 }
 
-static int rtnl_fill_link_ifmap(struct sk_buff *skb, struct net_device *dev)
+static int rtnl_fill_link_ifmap(struct sk_buff *skb,
+                               const struct net_device *dev)
 {
        struct rtnl_link_ifmap map;
 
        memset(&map, 0, sizeof(map));
-       map.mem_start   = dev->mem_start;
-       map.mem_end     = dev->mem_end;
-       map.base_addr   = dev->base_addr;
-       map.irq         = dev->irq;
-       map.dma         = dev->dma;
-       map.port        = dev->if_port;
+       map.mem_start = READ_ONCE(dev->mem_start);
+       map.mem_end   = READ_ONCE(dev->mem_end);
+       map.base_addr = READ_ONCE(dev->base_addr);
+       map.irq       = READ_ONCE(dev->irq);
+       map.dma       = READ_ONCE(dev->dma);
+       map.port      = READ_ONCE(dev->if_port);
 
        if (nla_put_64bit(skb, IFLA_MAP, sizeof(map), &map, IFLA_PAD))
                return -EMSGSIZE;
@@ -1612,10 +1612,10 @@ static int put_master_ifindex(struct sk_buff *skb, struct net_device *dev)
 static int nla_put_iflink(struct sk_buff *skb, const struct net_device *dev,
                          bool force)
 {
-       int ifindex = dev_get_iflink(dev);
+       int iflink = dev_get_iflink(dev);
 
-       if (force || dev->ifindex != ifindex)
-               return nla_put_u32(skb, IFLA_LINK, ifindex);
+       if (force || READ_ONCE(dev->ifindex) != iflink)
+               return nla_put_u32(skb, IFLA_LINK, iflink);
 
        return 0;
 }
@@ -1699,7 +1699,7 @@ static int rtnl_fill_alt_ifnames(struct sk_buff *skb,
        struct netdev_name_node *name_node;
        int count = 0;
 
-       list_for_each_entry(name_node, &dev->name_node->list, list) {
+       list_for_each_entry_rcu(name_node, &dev->name_node->list, list) {
                if (nla_put_string(skb, IFLA_ALT_IFNAME, name_node->name))
                        return -EMSGSIZE;
                count++;
@@ -1707,6 +1707,7 @@ static int rtnl_fill_alt_ifnames(struct sk_buff *skb,
        return count;
 }
 
+/* RCU protected. */
 static int rtnl_fill_prop_list(struct sk_buff *skb,
                               const struct net_device *dev)
 {
@@ -1876,9 +1877,6 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
                        goto nla_put_failure;
        }
 
-       if (rtnl_fill_link_ifmap(skb, dev))
-               goto nla_put_failure;
-
        if (dev->addr_len) {
                if (nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr) ||
                    nla_put(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast))
@@ -1928,10 +1926,11 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
        rcu_read_lock();
        if (rtnl_fill_link_af(skb, dev, ext_filter_mask))
                goto nla_put_failure_rcu;
-       rcu_read_unlock();
-
+       if (rtnl_fill_link_ifmap(skb, dev))
+               goto nla_put_failure_rcu;
        if (rtnl_fill_prop_list(skb, dev))
-               goto nla_put_failure;
+               goto nla_put_failure_rcu;
+       rcu_read_unlock();
 
        if (dev->dev.parent &&
            nla_put_string(skb, IFLA_PARENT_DEV_NAME,
@@ -2200,25 +2199,22 @@ static int rtnl_valid_dump_ifinfo_req(const struct nlmsghdr *nlh,
 
 static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
 {
+       const struct rtnl_link_ops *kind_ops = NULL;
        struct netlink_ext_ack *extack = cb->extack;
        const struct nlmsghdr *nlh = cb->nlh;
        struct net *net = sock_net(skb->sk);
-       struct net *tgt_net = net;
-       int h, s_h;
-       int idx = 0, s_idx;
-       struct net_device *dev;
-       struct hlist_head *head;
+       unsigned int flags = NLM_F_MULTI;
        struct nlattr *tb[IFLA_MAX+1];
+       struct {
+               unsigned long ifindex;
+       } *ctx = (void *)cb->ctx;
+       struct net *tgt_net = net;
        u32 ext_filter_mask = 0;
-       const struct rtnl_link_ops *kind_ops = NULL;
-       unsigned int flags = NLM_F_MULTI;
+       struct net_device *dev;
        int master_idx = 0;
        int netnsid = -1;
        int err, i;
 
-       s_h = cb->args[0];
-       s_idx = cb->args[1];
-
        err = rtnl_valid_dump_ifinfo_req(nlh, cb->strict_check, tb, extack);
        if (err < 0) {
                if (cb->strict_check)
@@ -2262,36 +2258,18 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
                flags |= NLM_F_DUMP_FILTERED;
 
 walk_entries:
-       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
-               idx = 0;
-               head = &tgt_net->dev_index_head[h];
-               hlist_for_each_entry(dev, head, index_hlist) {
-                       if (link_dump_filtered(dev, master_idx, kind_ops))
-                               goto cont;
-                       if (idx < s_idx)
-                               goto cont;
-                       err = rtnl_fill_ifinfo(skb, dev, net,
-                                              RTM_NEWLINK,
-                                              NETLINK_CB(cb->skb).portid,
-                                              nlh->nlmsg_seq, 0, flags,
-                                              ext_filter_mask, 0, NULL, 0,
-                                              netnsid, GFP_KERNEL);
-
-                       if (err < 0) {
-                               if (likely(skb->len))
-                                       goto out;
-
-                               goto out_err;
-                       }
-cont:
-                       idx++;
-               }
+       err = 0;
+       for_each_netdev_dump(tgt_net, dev, ctx->ifindex) {
+               if (link_dump_filtered(dev, master_idx, kind_ops))
+                       continue;
+               err = rtnl_fill_ifinfo(skb, dev, net, RTM_NEWLINK,
+                                      NETLINK_CB(cb->skb).portid,
+                                      nlh->nlmsg_seq, 0, flags,
+                                      ext_filter_mask, 0, NULL, 0,
+                                      netnsid, GFP_KERNEL);
+               if (err < 0)
+                       break;
        }
-out:
-       err = skb->len;
-out_err:
-       cb->args[1] = idx;
-       cb->args[0] = h;
        cb->seq = tgt_net->dev_base_seq;
        nl_dump_check_consistent(cb, nlmsg_hdr(skb));
        if (netnsid >= 0)
@@ -2983,11 +2961,9 @@ static int do_setlink(const struct sk_buff *skb,
        if (tb[IFLA_LINKMODE]) {
                unsigned char value = nla_get_u8(tb[IFLA_LINKMODE]);
 
-               write_lock(&dev_base_lock);
                if (dev->link_mode ^ value)
                        status |= DO_SETLINK_NOTIFY;
-               dev->link_mode = value;
-               write_unlock(&dev_base_lock);
+               WRITE_ONCE(dev->link_mode, value);
        }
 
        if (tb[IFLA_VFINFO_LIST]) {
@@ -6552,6 +6528,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
                }
                owner = link->owner;
                dumpit = link->dumpit;
+               flags = link->flags;
 
                if (type == RTM_GETLINK - RTM_BASE)
                        min_dump_alloc = rtnl_calcit(skb, nlh);
@@ -6569,6 +6546,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
                                .dump           = dumpit,
                                .min_dump_alloc = min_dump_alloc,
                                .module         = owner,
+                               .flags          = flags,
                        };
                        err = netlink_dump_start(rtnl, skb, nlh, &c);
                        /* netlink_dump_start() will keep a reference on