net: Introduce for_each_netdev_rcu() iterator

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>
This commit is contained in:
Eric Dumazet 2009-11-04 05:43:23 -08:00 committed by David S. Miller
parent d0075634cf
commit c6d14c8456
11 changed files with 68 additions and 77 deletions

View file

@ -1081,6 +1081,8 @@ extern rwlock_t dev_base_lock; /* Device list lock */
#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) \

View file

@ -175,7 +175,7 @@ static struct list_head ptype_all __read_mostly; /* Taps */
* 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
@ -212,7 +212,7 @@ static int list_netdevice(struct net_device *dev)
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));
@ -229,7 +229,7 @@ static void unlist_netdevice(struct net_device *dev)
/* 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);
@ -799,15 +799,15 @@ struct net_device *dev_get_by_flags(struct net *net, unsigned short if_flags,
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);
@ -3077,18 +3077,18 @@ static int dev_ifconf(struct net *net, char __user *arg)
* 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;
@ -3097,16 +3097,18 @@ void *dev_seq_start(struct seq_file *seq, loff_t *pos)
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)

View file

@ -749,9 +749,9 @@ static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
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))) {
@ -759,7 +759,7 @@ static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
break;
}
}
read_unlock(&dev_base_lock);
rcu_read_unlock();
if (ldev == NULL)
return -EADDRNOTAVAIL;
}

View file

@ -607,8 +607,8 @@ static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa)
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;
@ -619,7 +619,7 @@ static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa)
}
}
}
read_unlock(&dev_base_lock);
rcu_read_unlock();
if (found_it == 0) {
fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa);

View file

@ -908,8 +908,8 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *old
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))
@ -922,7 +922,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *old
dev_out = dev;
break;
}
read_unlock(&dev_base_lock);
rcu_read_unlock();
if (dev_out == NULL)
goto out;
dev_hold(dev_out);

View file

@ -876,19 +876,16 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
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;
@ -896,12 +893,11 @@ no_in_dev:
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;
@ -962,9 +958,8 @@ __be32 inet_confirm_addr(struct in_device *in_dev,
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)
@ -972,7 +967,6 @@ __be32 inet_confirm_addr(struct in_device *in_dev,
}
}
rcu_read_unlock();
read_unlock(&dev_base_lock);
return addr;
}
@ -1240,18 +1234,18 @@ static void devinet_copy_dflt_conf(struct net *net, int i)
{
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;
@ -1260,7 +1254,6 @@ static void inet_forward_change(struct net *net)
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)
@ -1271,7 +1264,6 @@ static void inet_forward_change(struct net *net)
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,

View file

@ -481,9 +481,8 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
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);
@ -491,9 +490,8 @@ static void addrconf_forward_change(struct net *net, __s32 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)
@ -1137,10 +1135,9 @@ int ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev,
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)
@ -1235,7 +1232,6 @@ try_nextdev:
read_unlock_bh(&idev->lock);
}
rcu_read_unlock();
read_unlock(&dev_base_lock);
if (!hiscore->ifa)
return -EADDRNOTAVAIL;
@ -4052,9 +4048,8 @@ static void addrconf_disable_change(struct net *net, __s32 newf)
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);
@ -4062,9 +4057,8 @@ static void addrconf_disable_change(struct net *net, __s32 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)

View file

@ -404,13 +404,13 @@ int ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
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;
}

View file

@ -597,15 +597,15 @@ struct net_device *nr_dev_first(void)
{
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;
}
@ -617,16 +617,17 @@ struct net_device *nr_dev_get(ax25_address *addr)
{
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;
}

View file

@ -600,13 +600,13 @@ struct net_device *rose_dev_first(void)
{
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;
}
@ -618,8 +618,8 @@ struct net_device *rose_dev_get(rose_address *addr)
{
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;
@ -627,7 +627,7 @@ struct net_device *rose_dev_get(rose_address *addr)
}
dev = NULL;
out:
read_unlock(&dev_base_lock);
rcu_read_unlock();
return dev;
}
@ -635,14 +635,14 @@ static int rose_dev_exists(rose_address *addr)
{
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;
}

View file

@ -205,14 +205,14 @@ static void sctp_get_local_addr_list(void)
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. */