diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-11-09 07:11:28 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-10 23:54:41 -0500 |
commit | 84d2697d9649339215675551eae28ba04068dea1 (patch) | |
tree | d24fb9fb7b02c6fe28fe01c248de3c4caaadc2c0 /net | |
parent | 13cfa97bef0f1172879f98307ac716acf3e9cea9 (diff) |
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')
-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 024bba30de21..f9f7fd6ee1f3 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -3823,28 +3823,39 @@ nla_put_failure: | |||
3823 | static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | 3823 | static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) |
3824 | { | 3824 | { |
3825 | struct net *net = sock_net(skb->sk); | 3825 | struct net *net = sock_net(skb->sk); |
3826 | int idx, err; | 3826 | int h, s_h; |
3827 | int s_idx = cb->args[0]; | 3827 | int idx = 0, err, s_idx; |
3828 | struct net_device *dev; | 3828 | struct net_device *dev; |
3829 | struct inet6_dev *idev; | 3829 | struct inet6_dev *idev; |
3830 | struct hlist_head *head; | ||
3831 | struct hlist_node *node; | ||
3830 | 3832 | ||
3831 | read_lock(&dev_base_lock); | 3833 | s_h = cb->args[0]; |
3832 | idx = 0; | 3834 | s_idx = cb->args[1]; |
3833 | for_each_netdev(net, dev) { | 3835 | |
3834 | if (idx < s_idx) | 3836 | rcu_read_lock(); |
3835 | goto cont; | 3837 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { |
3836 | if ((idev = in6_dev_get(dev)) == NULL) | 3838 | idx = 0; |
3837 | goto cont; | 3839 | head = &net->dev_index_head[h]; |
3838 | err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid, | 3840 | hlist_for_each_entry_rcu(dev, node, head, index_hlist) { |
3839 | cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI); | 3841 | if (idx < s_idx) |
3840 | in6_dev_put(idev); | 3842 | goto cont; |
3841 | if (err <= 0) | 3843 | idev = __in6_dev_get(dev); |
3842 | break; | 3844 | if (!idev) |
3845 | goto cont; | ||
3846 | if (inet6_fill_ifinfo(skb, idev, | ||
3847 | NETLINK_CB(cb->skb).pid, | ||
3848 | cb->nlh->nlmsg_seq, | ||
3849 | RTM_NEWLINK, NLM_F_MULTI) <= 0) | ||
3850 | goto out; | ||
3843 | cont: | 3851 | cont: |
3844 | idx++; | 3852 | idx++; |
3853 | } | ||
3845 | } | 3854 | } |
3846 | read_unlock(&dev_base_lock); | 3855 | out: |
3847 | cb->args[0] = idx; | 3856 | rcu_read_unlock(); |
3857 | cb->args[1] = idx; | ||
3858 | cb->args[0] = h; | ||
3848 | 3859 | ||
3849 | return skb->len; | 3860 | return skb->len; |
3850 | } | 3861 | } |