ipv6: use RCU to walk list of network devices

No longer need read_lock(&dev_base_lock), use RCU instead.
We also can avoid taking references on inet6_dev structs.

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-11 17:34:30 +00:00 committed by David S. Miller
parent bee7ca9ec0
commit ce81b76a39
3 changed files with 47 additions and 43 deletions

View file

@ -1114,6 +1114,16 @@ static inline struct net_device *next_net_device(struct net_device *dev)
return lh == &net->dev_base_head ? NULL : net_device_entry(lh); return lh == &net->dev_base_head ? NULL : net_device_entry(lh);
} }
static inline struct net_device *next_net_device_rcu(struct net_device *dev)
{
struct list_head *lh;
struct net *net;
net = dev_net(dev);
lh = rcu_dereference(dev->dev_list.next);
return lh == &net->dev_base_head ? NULL : net_device_entry(lh);
}
static inline struct net_device *first_net_device(struct net *net) static inline struct net_device *first_net_device(struct net *net)
{ {
return list_empty(&net->dev_base_head) ? NULL : return list_empty(&net->dev_base_head) ? NULL :

View file

@ -431,9 +431,9 @@ static inline struct ifacaddr6 *ac6_get_first(struct seq_file *seq)
struct net *net = seq_file_net(seq); struct net *net = seq_file_net(seq);
state->idev = NULL; state->idev = NULL;
for_each_netdev(net, state->dev) { for_each_netdev_rcu(net, state->dev) {
struct inet6_dev *idev; struct inet6_dev *idev;
idev = in6_dev_get(state->dev); idev = __in6_dev_get(state->dev);
if (!idev) if (!idev)
continue; continue;
read_lock_bh(&idev->lock); read_lock_bh(&idev->lock);
@ -443,7 +443,6 @@ static inline struct ifacaddr6 *ac6_get_first(struct seq_file *seq)
break; break;
} }
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev);
} }
return im; return im;
} }
@ -454,16 +453,15 @@ static struct ifacaddr6 *ac6_get_next(struct seq_file *seq, struct ifacaddr6 *im
im = im->aca_next; im = im->aca_next;
while (!im) { while (!im) {
if (likely(state->idev != NULL)) { if (likely(state->idev != NULL))
read_unlock_bh(&state->idev->lock); read_unlock_bh(&state->idev->lock);
in6_dev_put(state->idev);
} state->dev = next_net_device_rcu(state->dev);
state->dev = next_net_device(state->dev);
if (!state->dev) { if (!state->dev) {
state->idev = NULL; state->idev = NULL;
break; break;
} }
state->idev = in6_dev_get(state->dev); state->idev = __in6_dev_get(state->dev);
if (!state->idev) if (!state->idev)
continue; continue;
read_lock_bh(&state->idev->lock); read_lock_bh(&state->idev->lock);
@ -482,29 +480,30 @@ static struct ifacaddr6 *ac6_get_idx(struct seq_file *seq, loff_t pos)
} }
static void *ac6_seq_start(struct seq_file *seq, loff_t *pos) static void *ac6_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(dev_base_lock) __acquires(RCU)
{ {
read_lock(&dev_base_lock); rcu_read_lock();
return ac6_get_idx(seq, *pos); return ac6_get_idx(seq, *pos);
} }
static void *ac6_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void *ac6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{ {
struct ifacaddr6 *im; struct ifacaddr6 *im = ac6_get_next(seq, v);
im = ac6_get_next(seq, v);
++*pos; ++*pos;
return im; return im;
} }
static void ac6_seq_stop(struct seq_file *seq, void *v) static void ac6_seq_stop(struct seq_file *seq, void *v)
__releases(dev_base_lock) __releases(RCU)
{ {
struct ac6_iter_state *state = ac6_seq_private(seq); struct ac6_iter_state *state = ac6_seq_private(seq);
if (likely(state->idev != NULL)) { if (likely(state->idev != NULL)) {
read_unlock_bh(&state->idev->lock); read_unlock_bh(&state->idev->lock);
in6_dev_put(state->idev); state->idev = NULL;
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
} }
static int ac6_seq_show(struct seq_file *seq, void *v) static int ac6_seq_show(struct seq_file *seq, void *v)

View file

@ -2375,9 +2375,9 @@ static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
struct net *net = seq_file_net(seq); struct net *net = seq_file_net(seq);
state->idev = NULL; state->idev = NULL;
for_each_netdev(net, state->dev) { for_each_netdev_rcu(net, state->dev) {
struct inet6_dev *idev; struct inet6_dev *idev;
idev = in6_dev_get(state->dev); idev = __in6_dev_get(state->dev);
if (!idev) if (!idev)
continue; continue;
read_lock_bh(&idev->lock); read_lock_bh(&idev->lock);
@ -2387,7 +2387,6 @@ static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
break; break;
} }
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev);
} }
return im; return im;
} }
@ -2398,16 +2397,15 @@ static struct ifmcaddr6 *igmp6_mc_get_next(struct seq_file *seq, struct ifmcaddr
im = im->next; im = im->next;
while (!im) { while (!im) {
if (likely(state->idev != NULL)) { if (likely(state->idev != NULL))
read_unlock_bh(&state->idev->lock); read_unlock_bh(&state->idev->lock);
in6_dev_put(state->idev);
} state->dev = next_net_device_rcu(state->dev);
state->dev = next_net_device(state->dev);
if (!state->dev) { if (!state->dev) {
state->idev = NULL; state->idev = NULL;
break; break;
} }
state->idev = in6_dev_get(state->dev); state->idev = __in6_dev_get(state->dev);
if (!state->idev) if (!state->idev)
continue; continue;
read_lock_bh(&state->idev->lock); read_lock_bh(&state->idev->lock);
@ -2426,31 +2424,31 @@ static struct ifmcaddr6 *igmp6_mc_get_idx(struct seq_file *seq, loff_t pos)
} }
static void *igmp6_mc_seq_start(struct seq_file *seq, loff_t *pos) static void *igmp6_mc_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(dev_base_lock) __acquires(RCU)
{ {
read_lock(&dev_base_lock); rcu_read_lock();
return igmp6_mc_get_idx(seq, *pos); return igmp6_mc_get_idx(seq, *pos);
} }
static void *igmp6_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void *igmp6_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{ {
struct ifmcaddr6 *im; struct ifmcaddr6 *im = igmp6_mc_get_next(seq, v);
im = igmp6_mc_get_next(seq, v);
++*pos; ++*pos;
return im; return im;
} }
static void igmp6_mc_seq_stop(struct seq_file *seq, void *v) static void igmp6_mc_seq_stop(struct seq_file *seq, void *v)
__releases(dev_base_lock) __releases(RCU)
{ {
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
if (likely(state->idev != NULL)) { if (likely(state->idev != NULL)) {
read_unlock_bh(&state->idev->lock); read_unlock_bh(&state->idev->lock);
in6_dev_put(state->idev);
state->idev = NULL; state->idev = NULL;
} }
state->dev = NULL; state->dev = NULL;
read_unlock(&dev_base_lock); rcu_read_unlock();
} }
static int igmp6_mc_seq_show(struct seq_file *seq, void *v) static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
@ -2507,9 +2505,9 @@ static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
state->idev = NULL; state->idev = NULL;
state->im = NULL; state->im = NULL;
for_each_netdev(net, state->dev) { for_each_netdev_rcu(net, state->dev) {
struct inet6_dev *idev; struct inet6_dev *idev;
idev = in6_dev_get(state->dev); idev = __in6_dev_get(state->dev);
if (unlikely(idev == NULL)) if (unlikely(idev == NULL))
continue; continue;
read_lock_bh(&idev->lock); read_lock_bh(&idev->lock);
@ -2525,7 +2523,6 @@ static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
spin_unlock_bh(&im->mca_lock); spin_unlock_bh(&im->mca_lock);
} }
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
in6_dev_put(idev);
} }
return psf; return psf;
} }
@ -2539,16 +2536,15 @@ static struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_s
spin_unlock_bh(&state->im->mca_lock); spin_unlock_bh(&state->im->mca_lock);
state->im = state->im->next; state->im = state->im->next;
while (!state->im) { while (!state->im) {
if (likely(state->idev != NULL)) { if (likely(state->idev != NULL))
read_unlock_bh(&state->idev->lock); read_unlock_bh(&state->idev->lock);
in6_dev_put(state->idev);
} state->dev = next_net_device_rcu(state->dev);
state->dev = next_net_device(state->dev);
if (!state->dev) { if (!state->dev) {
state->idev = NULL; state->idev = NULL;
goto out; goto out;
} }
state->idev = in6_dev_get(state->dev); state->idev = __in6_dev_get(state->dev);
if (!state->idev) if (!state->idev)
continue; continue;
read_lock_bh(&state->idev->lock); read_lock_bh(&state->idev->lock);
@ -2573,9 +2569,9 @@ static struct ip6_sf_list *igmp6_mcf_get_idx(struct seq_file *seq, loff_t pos)
} }
static void *igmp6_mcf_seq_start(struct seq_file *seq, loff_t *pos) static void *igmp6_mcf_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(dev_base_lock) __acquires(RCU)
{ {
read_lock(&dev_base_lock); rcu_read_lock();
return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
} }
@ -2591,7 +2587,7 @@ static void *igmp6_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos)
} }
static void igmp6_mcf_seq_stop(struct seq_file *seq, void *v) static void igmp6_mcf_seq_stop(struct seq_file *seq, void *v)
__releases(dev_base_lock) __releases(RCU)
{ {
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
if (likely(state->im != NULL)) { if (likely(state->im != NULL)) {
@ -2600,11 +2596,10 @@ static void igmp6_mcf_seq_stop(struct seq_file *seq, void *v)
} }
if (likely(state->idev != NULL)) { if (likely(state->idev != NULL)) {
read_unlock_bh(&state->idev->lock); read_unlock_bh(&state->idev->lock);
in6_dev_put(state->idev);
state->idev = NULL; state->idev = NULL;
} }
state->dev = NULL; state->dev = NULL;
read_unlock(&dev_base_lock); rcu_read_unlock();
} }
static int igmp6_mcf_seq_show(struct seq_file *seq, void *v) static int igmp6_mcf_seq_show(struct seq_file *seq, void *v)