diff options
author | Mihai Maruseac <mihai.maruseac@gmail.com> | 2012-01-03 18:31:35 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-01-04 16:00:57 -0500 |
commit | 1d5783030a14d1b6ee763f63c8136e581f48b365 (patch) | |
tree | c17e33ce9a8dae357a653d2f56621369128a9d36 /net/ipv6/addrconf.c | |
parent | 48529680dc59061eaa13ea3b1047401612b79600 (diff) |
ipv6/addrconf: speedup /proc/net/if_inet6 filling
This ensures a linear behaviour when filling /proc/net/if_inet6 thus making
ifconfig run really fast on IPv6 only addresses. In fact, with this patch and
the IPv4 one sent a while ago, ifconfig will run in linear time regardless of
address type.
IPv4 related patch: f04565ddf52e401880f8ba51de0dff8ba51c99fd
dev: use name hash for dev_seq_ops
...
Some statistics (running ifconfig > /dev/null on a different setup):
iface count / IPv6 no-patch time / IPv6 patched time / IPv4 time
----------------------------------------------------------------
6250 | 0.23 s | 0.13 s | 0.11 s
12500 | 0.62 s | 0.28 s | 0.22 s
25000 | 2.91 s | 0.57 s | 0.46 s
50000 | 11.37 s | 1.21 s | 0.94 s
128000 | 86.78 s | 3.05 s | 2.54 s
Signed-off-by: Mihai Maruseac <mmaruseac@ixiacom.com>
Cc: Daniel Baluta <dbaluta@ixiacom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 43 |
1 files changed, 28 insertions, 15 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3513cceba50a..0ba0866230c9 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -3068,20 +3068,39 @@ static void addrconf_dad_run(struct inet6_dev *idev) | |||
3068 | struct if6_iter_state { | 3068 | struct if6_iter_state { |
3069 | struct seq_net_private p; | 3069 | struct seq_net_private p; |
3070 | int bucket; | 3070 | int bucket; |
3071 | int offset; | ||
3071 | }; | 3072 | }; |
3072 | 3073 | ||
3073 | static struct inet6_ifaddr *if6_get_first(struct seq_file *seq) | 3074 | static struct inet6_ifaddr *if6_get_first(struct seq_file *seq, loff_t pos) |
3074 | { | 3075 | { |
3075 | struct inet6_ifaddr *ifa = NULL; | 3076 | struct inet6_ifaddr *ifa = NULL; |
3076 | struct if6_iter_state *state = seq->private; | 3077 | struct if6_iter_state *state = seq->private; |
3077 | struct net *net = seq_file_net(seq); | 3078 | struct net *net = seq_file_net(seq); |
3079 | int p = 0; | ||
3078 | 3080 | ||
3079 | for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { | 3081 | /* initial bucket if pos is 0 */ |
3082 | if (pos == 0) { | ||
3083 | state->bucket = 0; | ||
3084 | state->offset = 0; | ||
3085 | } | ||
3086 | |||
3087 | for (; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { | ||
3080 | struct hlist_node *n; | 3088 | struct hlist_node *n; |
3081 | hlist_for_each_entry_rcu_bh(ifa, n, &inet6_addr_lst[state->bucket], | 3089 | hlist_for_each_entry_rcu_bh(ifa, n, &inet6_addr_lst[state->bucket], |
3082 | addr_lst) | 3090 | addr_lst) { |
3091 | /* sync with offset */ | ||
3092 | if (p < state->offset) { | ||
3093 | p++; | ||
3094 | continue; | ||
3095 | } | ||
3096 | state->offset++; | ||
3083 | if (net_eq(dev_net(ifa->idev->dev), net)) | 3097 | if (net_eq(dev_net(ifa->idev->dev), net)) |
3084 | return ifa; | 3098 | return ifa; |
3099 | } | ||
3100 | |||
3101 | /* prepare for next bucket */ | ||
3102 | state->offset = 0; | ||
3103 | p = 0; | ||
3085 | } | 3104 | } |
3086 | return NULL; | 3105 | return NULL; |
3087 | } | 3106 | } |
@@ -3093,13 +3112,17 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, | |||
3093 | struct net *net = seq_file_net(seq); | 3112 | struct net *net = seq_file_net(seq); |
3094 | struct hlist_node *n = &ifa->addr_lst; | 3113 | struct hlist_node *n = &ifa->addr_lst; |
3095 | 3114 | ||
3096 | hlist_for_each_entry_continue_rcu_bh(ifa, n, addr_lst) | 3115 | hlist_for_each_entry_continue_rcu_bh(ifa, n, addr_lst) { |
3116 | state->offset++; | ||
3097 | if (net_eq(dev_net(ifa->idev->dev), net)) | 3117 | if (net_eq(dev_net(ifa->idev->dev), net)) |
3098 | return ifa; | 3118 | return ifa; |
3119 | } | ||
3099 | 3120 | ||
3100 | while (++state->bucket < IN6_ADDR_HSIZE) { | 3121 | while (++state->bucket < IN6_ADDR_HSIZE) { |
3122 | state->offset = 0; | ||
3101 | hlist_for_each_entry_rcu_bh(ifa, n, | 3123 | hlist_for_each_entry_rcu_bh(ifa, n, |
3102 | &inet6_addr_lst[state->bucket], addr_lst) { | 3124 | &inet6_addr_lst[state->bucket], addr_lst) { |
3125 | state->offset++; | ||
3103 | if (net_eq(dev_net(ifa->idev->dev), net)) | 3126 | if (net_eq(dev_net(ifa->idev->dev), net)) |
3104 | return ifa; | 3127 | return ifa; |
3105 | } | 3128 | } |
@@ -3108,21 +3131,11 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, | |||
3108 | return NULL; | 3131 | return NULL; |
3109 | } | 3132 | } |
3110 | 3133 | ||
3111 | static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos) | ||
3112 | { | ||
3113 | struct inet6_ifaddr *ifa = if6_get_first(seq); | ||
3114 | |||
3115 | if (ifa) | ||
3116 | while (pos && (ifa = if6_get_next(seq, ifa)) != NULL) | ||
3117 | --pos; | ||
3118 | return pos ? NULL : ifa; | ||
3119 | } | ||
3120 | |||
3121 | static void *if6_seq_start(struct seq_file *seq, loff_t *pos) | 3134 | static void *if6_seq_start(struct seq_file *seq, loff_t *pos) |
3122 | __acquires(rcu_bh) | 3135 | __acquires(rcu_bh) |
3123 | { | 3136 | { |
3124 | rcu_read_lock_bh(); | 3137 | rcu_read_lock_bh(); |
3125 | return if6_get_idx(seq, *pos); | 3138 | return if6_get_first(seq, *pos); |
3126 | } | 3139 | } |
3127 | 3140 | ||
3128 | static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 3141 | static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos) |