Skip to content

Commit

Permalink
net: Introduce for_each_netdev_rcu() iterator
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Dumazet authored and davem330 committed Nov 4, 2009
1 parent d007563 commit c6d14c8
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 77 deletions.
2 changes: 2 additions & 0 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
30 changes: 16 additions & 14 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand All @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions net/decnet/af_decnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -749,17 +749,17 @@ 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))) {
ldev = dev;
break;
}
}
read_unlock(&dev_base_lock);
rcu_read_unlock();
if (ldev == NULL)
return -EADDRNOTAVAIL;
}
Expand Down
6 changes: 3 additions & 3 deletions net/decnet/dn_fib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions net/decnet/dn_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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);
Expand Down
30 changes: 11 additions & 19 deletions net/ipv4/devinet.c
Original file line number Diff line number Diff line change
Expand Up @@ -876,32 +876,28 @@ __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;

for_primary_ifa(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;
Expand Down Expand Up @@ -962,17 +958,15 @@ __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)
break;
}
}
rcu_read_unlock();
read_unlock(&dev_base_lock);

return addr;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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,
Expand Down
20 changes: 7 additions & 13 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -481,19 +481,17 @@ 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);
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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1235,7 +1232,6 @@ int ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev,
read_unlock_bh(&idev->lock);
}
rcu_read_unlock();
read_unlock(&dev_base_lock);

if (!hiscore->ifa)
return -EADDRNOTAVAIL;
Expand Down Expand Up @@ -4052,19 +4048,17 @@ 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);
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)
Expand Down
6 changes: 3 additions & 3 deletions net/ipv6/anycast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
15 changes: 8 additions & 7 deletions net/netrom/nr_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}

Expand Down
Loading

0 comments on commit c6d14c8

Please sign in to comment.