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) \ #define for_each_netdev(net, d) \
list_for_each_entry(d, &(net)->dev_base_head, dev_list) 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) \ #define for_each_netdev_safe(net, d, n) \
list_for_each_entry_safe(d, n, &(net)->dev_base_head, dev_list) list_for_each_entry_safe(d, n, &(net)->dev_base_head, dev_list)
#define for_each_netdev_continue(net, d) \ #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 * The @dev_base_head list is protected by @dev_base_lock and the rtnl
* semaphore. * 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 * 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 * 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(); ASSERT_RTNL();
write_lock_bh(&dev_base_lock); 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->name_hlist, dev_name_hash(net, dev->name));
hlist_add_head_rcu(&dev->index_hlist, hlist_add_head_rcu(&dev->index_hlist,
dev_index_hash(net, dev->ifindex)); dev_index_hash(net, dev->ifindex));
@ -229,7 +229,7 @@ static void unlist_netdevice(struct net_device *dev)
/* Unlink dev from the device chain */ /* Unlink dev from the device chain */
write_lock_bh(&dev_base_lock); 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->name_hlist);
hlist_del_rcu(&dev->index_hlist); hlist_del_rcu(&dev->index_hlist);
write_unlock_bh(&dev_base_lock); 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; struct net_device *dev, *ret;
ret = NULL; ret = NULL;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(net, dev) { for_each_netdev_rcu(net, dev) {
if (((dev->flags ^ if_flags) & mask) == 0) { if (((dev->flags ^ if_flags) & mask) == 0) {
dev_hold(dev); dev_hold(dev);
ret = dev; ret = dev;
break; break;
} }
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
return ret; return ret;
} }
EXPORT_SYMBOL(dev_get_by_flags); EXPORT_SYMBOL(dev_get_by_flags);
@ -3077,18 +3077,18 @@ static int dev_ifconf(struct net *net, char __user *arg)
* in detail. * in detail.
*/ */
void *dev_seq_start(struct seq_file *seq, loff_t *pos) void *dev_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(dev_base_lock) __acquires(RCU)
{ {
struct net *net = seq_file_net(seq); struct net *net = seq_file_net(seq);
loff_t off; loff_t off;
struct net_device *dev; struct net_device *dev;
read_lock(&dev_base_lock); rcu_read_lock();
if (!*pos) if (!*pos)
return SEQ_START_TOKEN; return SEQ_START_TOKEN;
off = 1; off = 1;
for_each_netdev(net, dev) for_each_netdev_rcu(net, dev)
if (off++ == *pos) if (off++ == *pos)
return dev; 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) 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; ++*pos;
return v == SEQ_START_TOKEN ? return rcu_dereference(dev);
first_net_device(net) : next_net_device((struct net_device *)v);
} }
void dev_seq_stop(struct seq_file *seq, void *v) 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) 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 (!(saddr->sdn_flags & SDF_WILD)) {
if (le16_to_cpu(saddr->sdn_nodeaddrl)) { if (le16_to_cpu(saddr->sdn_nodeaddrl)) {
read_lock(&dev_base_lock); rcu_read_lock();
ldev = NULL; ldev = NULL;
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
if (!dev->dn_ptr) if (!dev->dn_ptr)
continue; continue;
if (dn_dev_islocal(dev, dn_saddr2dn(saddr))) { 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; break;
} }
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
if (ldev == NULL) if (ldev == NULL)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
} }

View file

@ -607,8 +607,8 @@ static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa)
ASSERT_RTNL(); ASSERT_RTNL();
/* Scan device list */ /* Scan device list */
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
dn_db = dev->dn_ptr; dn_db = dev->dn_ptr;
if (dn_db == NULL) if (dn_db == NULL)
continue; 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) { if (found_it == 0) {
fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa); 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); dev_put(dev_out);
goto out; goto out;
} }
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
if (!dev->dn_ptr) if (!dev->dn_ptr)
continue; continue;
if (!dn_dev_islocal(dev, oldflp->fld_src)) 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; dev_out = dev;
break; break;
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
if (dev_out == NULL) if (dev_out == NULL)
goto out; goto out;
dev_hold(dev_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) if (!addr)
addr = ifa->ifa_local; addr = ifa->ifa_local;
} endfor_ifa(in_dev); } endfor_ifa(in_dev);
no_in_dev:
rcu_read_unlock();
no_in_dev:
if (addr) if (addr)
goto out; goto out_unlock;
/* Not loopback addresses on loopback should be preferred /* Not loopback addresses on loopback should be preferred
in this case. It is importnat that lo is the first interface in this case. It is importnat that lo is the first interface
in dev_base list. in dev_base list.
*/ */
read_lock(&dev_base_lock); for_each_netdev_rcu(net, dev) {
rcu_read_lock();
for_each_netdev(net, dev) {
if ((in_dev = __in_dev_get_rcu(dev)) == NULL) if ((in_dev = __in_dev_get_rcu(dev)) == NULL)
continue; continue;
@ -896,12 +893,11 @@ no_in_dev:
if (ifa->ifa_scope != RT_SCOPE_LINK && if (ifa->ifa_scope != RT_SCOPE_LINK &&
ifa->ifa_scope <= scope) { ifa->ifa_scope <= scope) {
addr = ifa->ifa_local; addr = ifa->ifa_local;
goto out_unlock_both; goto out_unlock;
} }
} endfor_ifa(in_dev); } endfor_ifa(in_dev);
} }
out_unlock_both: out_unlock:
read_unlock(&dev_base_lock);
rcu_read_unlock(); rcu_read_unlock();
out: out:
return addr; return addr;
@ -962,9 +958,8 @@ __be32 inet_confirm_addr(struct in_device *in_dev,
return confirm_addr_indev(in_dev, dst, local, scope); return confirm_addr_indev(in_dev, dst, local, scope);
net = dev_net(in_dev->dev); net = dev_net(in_dev->dev);
read_lock(&dev_base_lock);
rcu_read_lock(); rcu_read_lock();
for_each_netdev(net, dev) { for_each_netdev_rcu(net, dev) {
if ((in_dev = __in_dev_get_rcu(dev))) { if ((in_dev = __in_dev_get_rcu(dev))) {
addr = confirm_addr_indev(in_dev, dst, local, scope); addr = confirm_addr_indev(in_dev, dst, local, scope);
if (addr) if (addr)
@ -972,7 +967,6 @@ __be32 inet_confirm_addr(struct in_device *in_dev,
} }
} }
rcu_read_unlock(); rcu_read_unlock();
read_unlock(&dev_base_lock);
return addr; return addr;
} }
@ -1240,18 +1234,18 @@ static void devinet_copy_dflt_conf(struct net *net, int i)
{ {
struct net_device *dev; struct net_device *dev;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(net, dev) { for_each_netdev_rcu(net, dev) {
struct in_device *in_dev; struct in_device *in_dev;
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev); in_dev = __in_dev_get_rcu(dev);
if (in_dev && !test_bit(i, in_dev->cnf.state)) if (in_dev && !test_bit(i, in_dev->cnf.state))
in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 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) static void inet_forward_change(struct net *net)
{ {
struct net_device *dev; 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_ALL(net, ACCEPT_REDIRECTS) = !on;
IPV4_DEVCONF_DFLT(net, FORWARDING) = on; IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
read_lock(&dev_base_lock);
for_each_netdev(net, dev) { for_each_netdev(net, dev) {
struct in_device *in_dev; struct in_device *in_dev;
if (on) if (on)
@ -1271,7 +1264,6 @@ static void inet_forward_change(struct net *net)
IN_DEV_CONF_SET(in_dev, FORWARDING, on); IN_DEV_CONF_SET(in_dev, FORWARDING, on);
rcu_read_unlock(); rcu_read_unlock();
} }
read_unlock(&dev_base_lock);
} }
static int devinet_conf_proc(ctl_table *ctl, int write, 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 net_device *dev;
struct inet6_dev *idev; struct inet6_dev *idev;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(net, dev) { for_each_netdev_rcu(net, dev) {
rcu_read_lock();
idev = __in6_dev_get(dev); idev = __in6_dev_get(dev);
if (idev) { if (idev) {
int changed = (!idev->cnf.forwarding) ^ (!newf); int changed = (!idev->cnf.forwarding) ^ (!newf);
@ -491,9 +490,8 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
if (changed) if (changed)
dev_forward_change(idev); 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) 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->rule = -1;
hiscore->ifa = NULL; hiscore->ifa = NULL;
read_lock(&dev_base_lock);
rcu_read_lock(); rcu_read_lock();
for_each_netdev(net, dev) { for_each_netdev_rcu(net, dev) {
struct inet6_dev *idev; struct inet6_dev *idev;
/* Candidate Source Address (section 4) /* Candidate Source Address (section 4)
@ -1235,7 +1232,6 @@ try_nextdev:
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
} }
rcu_read_unlock(); rcu_read_unlock();
read_unlock(&dev_base_lock);
if (!hiscore->ifa) if (!hiscore->ifa)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
@ -4052,9 +4048,8 @@ static void addrconf_disable_change(struct net *net, __s32 newf)
struct net_device *dev; struct net_device *dev;
struct inet6_dev *idev; struct inet6_dev *idev;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(net, dev) { for_each_netdev_rcu(net, dev) {
rcu_read_lock();
idev = __in6_dev_get(dev); idev = __in6_dev_get(dev);
if (idev) { if (idev) {
int changed = (!idev->cnf.disable_ipv6) ^ (!newf); int changed = (!idev->cnf.disable_ipv6) ^ (!newf);
@ -4062,9 +4057,8 @@ static void addrconf_disable_change(struct net *net, __s32 newf)
if (changed) if (changed)
dev_disable_change(idev); 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) 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) if (dev)
return ipv6_chk_acast_dev(dev, addr); return ipv6_chk_acast_dev(dev, addr);
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(net, dev) for_each_netdev_rcu(net, dev)
if (ipv6_chk_acast_dev(dev, addr)) { if (ipv6_chk_acast_dev(dev, addr)) {
found = 1; found = 1;
break; break;
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
return found; return found;
} }

View file

@ -597,15 +597,15 @@ struct net_device *nr_dev_first(void)
{ {
struct net_device *dev, *first = NULL; struct net_device *dev, *first = NULL;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM) if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
if (first == NULL || strncmp(dev->name, first->name, 3) < 0) if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
first = dev; first = dev;
} }
if (first) if (first)
dev_hold(first); dev_hold(first);
read_unlock(&dev_base_lock); rcu_read_unlock();
return first; return first;
} }
@ -617,16 +617,17 @@ struct net_device *nr_dev_get(ax25_address *addr)
{ {
struct net_device *dev; struct net_device *dev;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) { if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM &&
ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) {
dev_hold(dev); dev_hold(dev);
goto out; goto out;
} }
} }
dev = NULL; dev = NULL;
out: out:
read_unlock(&dev_base_lock); rcu_read_unlock();
return dev; return dev;
} }

View file

@ -600,13 +600,13 @@ struct net_device *rose_dev_first(void)
{ {
struct net_device *dev, *first = NULL; struct net_device *dev, *first = NULL;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE) if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
if (first == NULL || strncmp(dev->name, first->name, 3) < 0) if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
first = dev; first = dev;
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
return first; return first;
} }
@ -618,8 +618,8 @@ struct net_device *rose_dev_get(rose_address *addr)
{ {
struct net_device *dev; struct net_device *dev;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0) { if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0) {
dev_hold(dev); dev_hold(dev);
goto out; goto out;
@ -627,7 +627,7 @@ struct net_device *rose_dev_get(rose_address *addr)
} }
dev = NULL; dev = NULL;
out: out:
read_unlock(&dev_base_lock); rcu_read_unlock();
return dev; return dev;
} }
@ -635,14 +635,14 @@ static int rose_dev_exists(rose_address *addr)
{ {
struct net_device *dev; struct net_device *dev;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0) if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
goto out; goto out;
} }
dev = NULL; dev = NULL;
out: out:
read_unlock(&dev_base_lock); rcu_read_unlock();
return dev != NULL; return dev != NULL;
} }

View file

@ -205,14 +205,14 @@ static void sctp_get_local_addr_list(void)
struct list_head *pos; struct list_head *pos;
struct sctp_af *af; struct sctp_af *af;
read_lock(&dev_base_lock); rcu_read_lock();
for_each_netdev(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
__list_for_each(pos, &sctp_address_families) { __list_for_each(pos, &sctp_address_families) {
af = list_entry(pos, struct sctp_af, list); af = list_entry(pos, struct sctp_af, list);
af->copy_addrlist(&sctp_local_addr_list, dev); af->copy_addrlist(&sctp_local_addr_list, dev);
} }
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
} }
/* Free the existing local addresses. */ /* Free the existing local addresses. */