Adds RCU management to the list of netdevices.
Convert some for_each_netdev() users to RCU version, if
it can avoid read_lock-ing dev_base_lock
Ie:
	read_lock(&dev_base_loack);
	for_each_netdev(net, dev)
		some_action();
	read_unlock(&dev_base_lock);
becomes :
	rcu_read_lock();
	for_each_netdev_rcu(net, dev)
		some_action();
	rcu_read_unlock();
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
 
 #define for_each_netdev(net, d)                \
                list_for_each_entry(d, &(net)->dev_base_head, dev_list)
+#define for_each_netdev_rcu(net, d)            \
+               list_for_each_entry_rcu(d, &(net)->dev_base_head, dev_list)
 #define for_each_netdev_safe(net, d, n)        \
                list_for_each_entry_safe(d, n, &(net)->dev_base_head, dev_list)
 #define for_each_netdev_continue(net, d)               \
 
  * The @dev_base_head list is protected by @dev_base_lock and the rtnl
  * semaphore.
  *
- * Pure readers hold dev_base_lock for reading.
+ * Pure readers hold dev_base_lock for reading, or rcu_read_lock()
  *
  * Writers must hold the rtnl semaphore while they loop through the
  * dev_base_head list, and hold dev_base_lock for writing when they do the
        ASSERT_RTNL();
 
        write_lock_bh(&dev_base_lock);
-       list_add_tail(&dev->dev_list, &net->dev_base_head);
+       list_add_tail_rcu(&dev->dev_list, &net->dev_base_head);
        hlist_add_head_rcu(&dev->name_hlist, dev_name_hash(net, dev->name));
        hlist_add_head_rcu(&dev->index_hlist,
                           dev_index_hash(net, dev->ifindex));
 
        /* Unlink dev from the device chain */
        write_lock_bh(&dev_base_lock);
-       list_del(&dev->dev_list);
+       list_del_rcu(&dev->dev_list);
        hlist_del_rcu(&dev->name_hlist);
        hlist_del_rcu(&dev->index_hlist);
        write_unlock_bh(&dev_base_lock);
        struct net_device *dev, *ret;
 
        ret = NULL;
-       read_lock(&dev_base_lock);
-       for_each_netdev(net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
                if (((dev->flags ^ if_flags) & mask) == 0) {
                        dev_hold(dev);
                        ret = dev;
                        break;
                }
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
        return ret;
 }
 EXPORT_SYMBOL(dev_get_by_flags);
  *     in detail.
  */
 void *dev_seq_start(struct seq_file *seq, loff_t *pos)
-       __acquires(dev_base_lock)
+       __acquires(RCU)
 {
        struct net *net = seq_file_net(seq);
        loff_t off;
        struct net_device *dev;
 
-       read_lock(&dev_base_lock);
+       rcu_read_lock();
        if (!*pos)
                return SEQ_START_TOKEN;
 
        off = 1;
-       for_each_netdev(net, dev)
+       for_each_netdev_rcu(net, dev)
                if (off++ == *pos)
                        return dev;
 
 
 void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
-       struct net *net = seq_file_net(seq);
+       struct net_device *dev = (v == SEQ_START_TOKEN) ?
+                                 first_net_device(seq_file_net(seq)) :
+                                 next_net_device((struct net_device *)v);
+
        ++*pos;
-       return v == SEQ_START_TOKEN ?
-               first_net_device(net) : next_net_device((struct net_device *)v);
+       return rcu_dereference(dev);
 }
 
 void dev_seq_stop(struct seq_file *seq, void *v)
-       __releases(dev_base_lock)
+       __releases(RCU)
 {
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 }
 
 static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
 
 
        if (!(saddr->sdn_flags & SDF_WILD)) {
                if (le16_to_cpu(saddr->sdn_nodeaddrl)) {
-                       read_lock(&dev_base_lock);
+                       rcu_read_lock();
                        ldev = NULL;
-                       for_each_netdev(&init_net, dev) {
+                       for_each_netdev_rcu(&init_net, dev) {
                                if (!dev->dn_ptr)
                                        continue;
                                if (dn_dev_islocal(dev, dn_saddr2dn(saddr))) {
                                        break;
                                }
                        }
-                       read_unlock(&dev_base_lock);
+                       rcu_read_unlock();
                        if (ldev == NULL)
                                return -EADDRNOTAVAIL;
                }
 
        ASSERT_RTNL();
 
        /* Scan device list */
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                dn_db = dev->dn_ptr;
                if (dn_db == NULL)
                        continue;
                        }
                }
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 
        if (found_it == 0) {
                fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa);
 
                        dev_put(dev_out);
                        goto out;
                }
-               read_lock(&dev_base_lock);
-               for_each_netdev(&init_net, dev) {
+               rcu_read_lock();
+               for_each_netdev_rcu(&init_net, dev) {
                        if (!dev->dn_ptr)
                                continue;
                        if (!dn_dev_islocal(dev, oldflp->fld_src))
                        dev_out = dev;
                        break;
                }
-               read_unlock(&dev_base_lock);
+               rcu_read_unlock();
                if (dev_out == NULL)
                        goto out;
                dev_hold(dev_out);
 
                if (!addr)
                        addr = ifa->ifa_local;
        } endfor_ifa(in_dev);
-no_in_dev:
-       rcu_read_unlock();
 
+no_in_dev:
        if (addr)
-               goto out;
+               goto out_unlock;
 
        /* Not loopback addresses on loopback should be preferred
           in this case. It is importnat that lo is the first interface
           in dev_base list.
         */
-       read_lock(&dev_base_lock);
-       rcu_read_lock();
-       for_each_netdev(net, dev) {
+       for_each_netdev_rcu(net, dev) {
                if ((in_dev = __in_dev_get_rcu(dev)) == NULL)
                        continue;
 
                        if (ifa->ifa_scope != RT_SCOPE_LINK &&
                            ifa->ifa_scope <= scope) {
                                addr = ifa->ifa_local;
-                               goto out_unlock_both;
+                               goto out_unlock;
                        }
                } endfor_ifa(in_dev);
        }
-out_unlock_both:
-       read_unlock(&dev_base_lock);
+out_unlock:
        rcu_read_unlock();
 out:
        return addr;
                return confirm_addr_indev(in_dev, dst, local, scope);
 
        net = dev_net(in_dev->dev);
-       read_lock(&dev_base_lock);
        rcu_read_lock();
-       for_each_netdev(net, dev) {
+       for_each_netdev_rcu(net, dev) {
                if ((in_dev = __in_dev_get_rcu(dev))) {
                        addr = confirm_addr_indev(in_dev, dst, local, scope);
                        if (addr)
                }
        }
        rcu_read_unlock();
-       read_unlock(&dev_base_lock);
 
        return addr;
 }
 {
        struct net_device *dev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
                struct in_device *in_dev;
-               rcu_read_lock();
+
                in_dev = __in_dev_get_rcu(dev);
                if (in_dev && !test_bit(i, in_dev->cnf.state))
                        in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
-               rcu_read_unlock();
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 }
 
+/* called with RTNL locked */
 static void inet_forward_change(struct net *net)
 {
        struct net_device *dev;
        IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
        IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
 
-       read_lock(&dev_base_lock);
        for_each_netdev(net, dev) {
                struct in_device *in_dev;
                if (on)
                        IN_DEV_CONF_SET(in_dev, FORWARDING, on);
                rcu_read_unlock();
        }
-       read_unlock(&dev_base_lock);
 }
 
 static int devinet_conf_proc(ctl_table *ctl, int write,
 
        struct net_device *dev;
        struct inet6_dev *idev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(net, dev) {
-               rcu_read_lock();
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
                idev = __in6_dev_get(dev);
                if (idev) {
                        int changed = (!idev->cnf.forwarding) ^ (!newf);
                        if (changed)
                                dev_forward_change(idev);
                }
-               rcu_read_unlock();
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 }
 
 static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
        hiscore->rule = -1;
        hiscore->ifa = NULL;
 
-       read_lock(&dev_base_lock);
        rcu_read_lock();
 
-       for_each_netdev(net, dev) {
+       for_each_netdev_rcu(net, dev) {
                struct inet6_dev *idev;
 
                /* Candidate Source Address (section 4)
                read_unlock_bh(&idev->lock);
        }
        rcu_read_unlock();
-       read_unlock(&dev_base_lock);
 
        if (!hiscore->ifa)
                return -EADDRNOTAVAIL;
        struct net_device *dev;
        struct inet6_dev *idev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(net, dev) {
-               rcu_read_lock();
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
                idev = __in6_dev_get(dev);
                if (idev) {
                        int changed = (!idev->cnf.disable_ipv6) ^ (!newf);
                        if (changed)
                                dev_disable_change(idev);
                }
-               rcu_read_unlock();
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 }
 
 static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old)
 
 
        if (dev)
                return ipv6_chk_acast_dev(dev, addr);
-       read_lock(&dev_base_lock);
-       for_each_netdev(net, dev)
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev)
                if (ipv6_chk_acast_dev(dev, addr)) {
                        found = 1;
                        break;
                }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
        return found;
 }
 
 
 {
        struct net_device *dev, *first = NULL;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
                        if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
                                first = dev;
        }
        if (first)
                dev_hold(first);
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 
        return first;
 }
 {
        struct net_device *dev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
-               if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
+               if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM &&
+                   ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) {
                        dev_hold(dev);
                        goto out;
                }
        }
        dev = NULL;
 out:
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
        return dev;
 }
 
 
 {
        struct net_device *dev, *first = NULL;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
                        if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
                                first = dev;
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 
        return first;
 }
 {
        struct net_device *dev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0) {
                        dev_hold(dev);
                        goto out;
        }
        dev = NULL;
 out:
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
        return dev;
 }
 
 {
        struct net_device *dev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
                        goto out;
        }
        dev = NULL;
 out:
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
        return dev != NULL;
 }
 
 
        struct list_head *pos;
        struct sctp_af *af;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       rcu_read_lock();
+       for_each_netdev_rcu(&init_net, dev) {
                __list_for_each(pos, &sctp_address_families) {
                        af = list_entry(pos, struct sctp_af, list);
                        af->copy_addrlist(&sctp_local_addr_list, dev);
                }
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 }
 
 /* Free the existing local addresses.  */