diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-01-26 19:08:02 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-01-28 00:02:56 +0100 |
commit | ccf434380d1a67df2dcb9113206b77d0cb0a1cef (patch) | |
tree | 1e1a36691f42ed7448f27f71a9ca9445e88ab5f1 /net/core | |
parent | net: Store ipv4/ipv6 COW'd metrics in inetpeer cache. (diff) | |
download | linux-ccf434380d1a67df2dcb9113206b77d0cb0a1cef.tar.xz linux-ccf434380d1a67df2dcb9113206b77d0cb0a1cef.zip |
net: fix dev_seq_next()
Commit c6d14c84566d (net: Introduce for_each_netdev_rcu() iterator)
added a race in dev_seq_next().
The rcu_dereference() call should be done _before_ testing the end of
list, or we might return a wrong net_device if a concurrent thread
changes net_device list under us.
Note : discovered thanks to a sparse warning :
net/core/dev.c:3919:9: error: incompatible types in comparison expression
(different address spaces)
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/dev.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 1b4c07fe295f..ddd5df2b61d4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4051,12 +4051,15 @@ 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_device *dev = (v == SEQ_START_TOKEN) ? - first_net_device(seq_file_net(seq)) : - next_net_device((struct net_device *)v); + struct net_device *dev = v; + + if (v == SEQ_START_TOKEN) + dev = first_net_device_rcu(seq_file_net(seq)); + else + dev = next_net_device_rcu(dev); ++*pos; - return rcu_dereference(dev); + return dev; } void dev_seq_stop(struct seq_file *seq, void *v) |