diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-11-09 12:11:28 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-10 20:54:41 -0800 |
commit | 84d2697d9649339215675551eae28ba04068dea1 (patch) | |
tree | d24fb9fb7b02c6fe28fe01c248de3c4caaadc2c0 /net/ipv6 | |
parent | 13cfa97bef0f1172879f98307ac716acf3e9cea9 (diff) | |
download | linux-3.10-84d2697d9649339215675551eae28ba04068dea1.tar.gz linux-3.10-84d2697d9649339215675551eae28ba04068dea1.tar.bz2 linux-3.10-84d2697d9649339215675551eae28ba04068dea1.zip |
ipv6: speedup inet6_dump_ifinfo()
When handling large number of netdevice, inet6_dump_ifinfo()
is very slow because it has O(N^2) complexity.
Instead of scanning one single list, we can use the 256 sub lists
of the dev_index hash table, and RCU lookups.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/addrconf.c | 45 |
1 files changed, 28 insertions, 17 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 024bba30de2..f9f7fd6ee1f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3823,28 +3823,39 @@ nla_put_failure: static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); - int idx, err; - int s_idx = cb->args[0]; + int h, s_h; + int idx = 0, err, s_idx; struct net_device *dev; struct inet6_dev *idev; + struct hlist_head *head; + struct hlist_node *node; - read_lock(&dev_base_lock); - idx = 0; - for_each_netdev(net, dev) { - if (idx < s_idx) - goto cont; - if ((idev = in6_dev_get(dev)) == NULL) - goto cont; - err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI); - in6_dev_put(idev); - if (err <= 0) - break; + s_h = cb->args[0]; + s_idx = cb->args[1]; + + rcu_read_lock(); + for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { + idx = 0; + head = &net->dev_index_head[h]; + hlist_for_each_entry_rcu(dev, node, head, index_hlist) { + if (idx < s_idx) + goto cont; + idev = __in6_dev_get(dev); + if (!idev) + goto cont; + if (inet6_fill_ifinfo(skb, idev, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + RTM_NEWLINK, NLM_F_MULTI) <= 0) + goto out; cont: - idx++; + idx++; + } } - read_unlock(&dev_base_lock); - cb->args[0] = idx; +out: + rcu_read_unlock(); + cb->args[1] = idx; + cb->args[0] = h; return skb->len; } |