diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2009-12-02 21:29:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-03 15:23:47 -0500 |
commit | b099ce2602d806deb41caaa578731848995cdb2a (patch) | |
tree | 7541d0d59c73e6b6274d420b8a3ec35d85ca5f2d | |
parent | 575f4cd5a5b639457747434dbe18d175fa767db4 (diff) |
net: Batch inet_twsk_purge
This function walks the whole hashtable so there is no point in
passing it a network namespace. Instead I purge all timewait
sockets from dead network namespaces that I find. If the namespace
is one of the once I am trying to purge I am guaranteed no new timewait
sockets can be formed so this will get them all. If the namespace
is one I am not acting for it might form a few more but I will
call inet_twsk_purge again and shortly to get rid of them. In
any even if the network namespace is dead timewait sockets are
useless.
Move the calls of inet_twsk_purge into batch_exit routines so
that if I am killing a bunch of namespaces at once I will just
call inet_twsk_purge once and save a lot of redundant unnecessary
work.
My simple 4k network namespace exit test the cleanup time dropped from
roughly 8.2s to 1.6s. While the time spent running inet_twsk_purge fell
to about 2ms. 1ms for ipv4 and 1ms for ipv6.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/inet_timewait_sock.h | 6 | ||||
-rw-r--r-- | net/ipv4/inet_timewait_sock.c | 10 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 11 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 11 |
4 files changed, 24 insertions, 14 deletions
diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index 773b10fa38e4..4fd007f34dd5 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h | |||
@@ -212,14 +212,14 @@ extern void inet_twsk_schedule(struct inet_timewait_sock *tw, | |||
212 | extern void inet_twsk_deschedule(struct inet_timewait_sock *tw, | 212 | extern void inet_twsk_deschedule(struct inet_timewait_sock *tw, |
213 | struct inet_timewait_death_row *twdr); | 213 | struct inet_timewait_death_row *twdr); |
214 | 214 | ||
215 | extern void inet_twsk_purge(struct net *net, struct inet_hashinfo *hashinfo, | 215 | extern void inet_twsk_purge(struct inet_hashinfo *hashinfo, |
216 | struct inet_timewait_death_row *twdr, int family); | 216 | struct inet_timewait_death_row *twdr, int family); |
217 | 217 | ||
218 | static inline | 218 | static inline |
219 | struct net *twsk_net(const struct inet_timewait_sock *twsk) | 219 | struct net *twsk_net(const struct inet_timewait_sock *twsk) |
220 | { | 220 | { |
221 | #ifdef CONFIG_NET_NS | 221 | #ifdef CONFIG_NET_NS |
222 | return twsk->tw_net; | 222 | return rcu_dereference(twsk->tw_net); |
223 | #else | 223 | #else |
224 | return &init_net; | 224 | return &init_net; |
225 | #endif | 225 | #endif |
@@ -229,7 +229,7 @@ static inline | |||
229 | void twsk_net_set(struct inet_timewait_sock *twsk, struct net *net) | 229 | void twsk_net_set(struct inet_timewait_sock *twsk, struct net *net) |
230 | { | 230 | { |
231 | #ifdef CONFIG_NET_NS | 231 | #ifdef CONFIG_NET_NS |
232 | twsk->tw_net = net; | 232 | rcu_assign_pointer(twsk->tw_net, net); |
233 | #endif | 233 | #endif |
234 | } | 234 | } |
235 | #endif /* _INET_TIMEWAIT_SOCK_ */ | 235 | #endif /* _INET_TIMEWAIT_SOCK_ */ |
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index d38ca7c77b93..31f931ef3daf 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c | |||
@@ -421,7 +421,7 @@ out: | |||
421 | 421 | ||
422 | EXPORT_SYMBOL_GPL(inet_twdr_twcal_tick); | 422 | EXPORT_SYMBOL_GPL(inet_twdr_twcal_tick); |
423 | 423 | ||
424 | void inet_twsk_purge(struct net *net, struct inet_hashinfo *hashinfo, | 424 | void inet_twsk_purge(struct inet_hashinfo *hashinfo, |
425 | struct inet_timewait_death_row *twdr, int family) | 425 | struct inet_timewait_death_row *twdr, int family) |
426 | { | 426 | { |
427 | struct inet_timewait_sock *tw; | 427 | struct inet_timewait_sock *tw; |
@@ -436,15 +436,15 @@ restart_rcu: | |||
436 | restart: | 436 | restart: |
437 | sk_nulls_for_each_rcu(sk, node, &head->twchain) { | 437 | sk_nulls_for_each_rcu(sk, node, &head->twchain) { |
438 | tw = inet_twsk(sk); | 438 | tw = inet_twsk(sk); |
439 | if (!net_eq(twsk_net(tw), net) || | 439 | if ((tw->tw_family != family) || |
440 | tw->tw_family != family) | 440 | atomic_read(&twsk_net(tw)->count)) |
441 | continue; | 441 | continue; |
442 | 442 | ||
443 | if (unlikely(!atomic_inc_not_zero(&tw->tw_refcnt))) | 443 | if (unlikely(!atomic_inc_not_zero(&tw->tw_refcnt))) |
444 | continue; | 444 | continue; |
445 | 445 | ||
446 | if (unlikely(!net_eq(twsk_net(tw), net) || | 446 | if (unlikely((tw->tw_family != family) || |
447 | tw->tw_family != family)) { | 447 | atomic_read(&twsk_net(tw)->count))) { |
448 | inet_twsk_put(tw); | 448 | inet_twsk_put(tw); |
449 | goto restart; | 449 | goto restart; |
450 | } | 450 | } |
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 59c911f3889d..fee9aabd5aa1 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -2529,12 +2529,17 @@ static int __net_init tcp_sk_init(struct net *net) | |||
2529 | static void __net_exit tcp_sk_exit(struct net *net) | 2529 | static void __net_exit tcp_sk_exit(struct net *net) |
2530 | { | 2530 | { |
2531 | inet_ctl_sock_destroy(net->ipv4.tcp_sock); | 2531 | inet_ctl_sock_destroy(net->ipv4.tcp_sock); |
2532 | inet_twsk_purge(net, &tcp_hashinfo, &tcp_death_row, AF_INET); | 2532 | } |
2533 | |||
2534 | static void __net_exit tcp_sk_exit_batch(struct list_head *net_exit_list) | ||
2535 | { | ||
2536 | inet_twsk_purge(&tcp_hashinfo, &tcp_death_row, AF_INET); | ||
2533 | } | 2537 | } |
2534 | 2538 | ||
2535 | static struct pernet_operations __net_initdata tcp_sk_ops = { | 2539 | static struct pernet_operations __net_initdata tcp_sk_ops = { |
2536 | .init = tcp_sk_init, | 2540 | .init = tcp_sk_init, |
2537 | .exit = tcp_sk_exit, | 2541 | .exit = tcp_sk_exit, |
2542 | .exit_batch = tcp_sk_exit_batch, | ||
2538 | }; | 2543 | }; |
2539 | 2544 | ||
2540 | void __init tcp_v4_init(void) | 2545 | void __init tcp_v4_init(void) |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index fc0a4e5895ee..aadd7cef73b3 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -2184,12 +2184,17 @@ static int tcpv6_net_init(struct net *net) | |||
2184 | static void tcpv6_net_exit(struct net *net) | 2184 | static void tcpv6_net_exit(struct net *net) |
2185 | { | 2185 | { |
2186 | inet_ctl_sock_destroy(net->ipv6.tcp_sk); | 2186 | inet_ctl_sock_destroy(net->ipv6.tcp_sk); |
2187 | inet_twsk_purge(net, &tcp_hashinfo, &tcp_death_row, AF_INET6); | 2187 | } |
2188 | |||
2189 | static void tcpv6_net_exit_batch(struct list_head *net_exit_list) | ||
2190 | { | ||
2191 | inet_twsk_purge(&tcp_hashinfo, &tcp_death_row, AF_INET6); | ||
2188 | } | 2192 | } |
2189 | 2193 | ||
2190 | static struct pernet_operations tcpv6_net_ops = { | 2194 | static struct pernet_operations tcpv6_net_ops = { |
2191 | .init = tcpv6_net_init, | 2195 | .init = tcpv6_net_init, |
2192 | .exit = tcpv6_net_exit, | 2196 | .exit = tcpv6_net_exit, |
2197 | .exit_batch = tcpv6_net_exit_batch, | ||
2193 | }; | 2198 | }; |
2194 | 2199 | ||
2195 | int __init tcpv6_init(void) | 2200 | int __init tcpv6_init(void) |