diff options
author | Vladimir Davydov <VDavydov@parallels.com> | 2013-03-13 19:40:14 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-03-19 12:08:31 -0400 |
commit | dece40e848f6e022f960dc9de54be518928460c3 (patch) | |
tree | 4350c34ed329d2a4335f71c9bc4d3856c2530a38 /net | |
parent | 493763684fefca54502e2d95b057075ac8e279ea (diff) |
netfilter: nf_conntrack: speed up module removal path if netns in use
The patch introduces nf_conntrack_cleanup_net_list(), which cleanups
nf_conntrack for a list of netns and calls synchronize_net() only once
for them all. This should reduce netns destruction time.
I've measured cleanup time for 1k dummy net ns. Here are the results:
<without the patch>
# modprobe nf_conntrack
# time modprobe -r nf_conntrack
real 0m10.337s
user 0m0.000s
sys 0m0.376s
<with the patch>
# modprobe nf_conntrack
# time modprobe -r nf_conntrack
real 0m5.661s
user 0m0.000s
sys 0m0.216s
Signed-off-by: Vladimir Davydov <vdavydov@parallels.com>
Cc: Patrick McHardy <kaber@trash.net>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: Gao feng <gaofeng@cn.fujitsu.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/nf_conntrack_core.c | 46 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_standalone.c | 16 |
2 files changed, 42 insertions, 20 deletions
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 1068deb97c8b..007e8c43d19a 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
@@ -1365,30 +1365,48 @@ void nf_conntrack_cleanup_end(void) | |||
1365 | */ | 1365 | */ |
1366 | void nf_conntrack_cleanup_net(struct net *net) | 1366 | void nf_conntrack_cleanup_net(struct net *net) |
1367 | { | 1367 | { |
1368 | LIST_HEAD(single); | ||
1369 | |||
1370 | list_add(&net->exit_list, &single); | ||
1371 | nf_conntrack_cleanup_net_list(&single); | ||
1372 | } | ||
1373 | |||
1374 | void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list) | ||
1375 | { | ||
1376 | int busy; | ||
1377 | struct net *net; | ||
1378 | |||
1368 | /* | 1379 | /* |
1369 | * This makes sure all current packets have passed through | 1380 | * This makes sure all current packets have passed through |
1370 | * netfilter framework. Roll on, two-stage module | 1381 | * netfilter framework. Roll on, two-stage module |
1371 | * delete... | 1382 | * delete... |
1372 | */ | 1383 | */ |
1373 | synchronize_net(); | 1384 | synchronize_net(); |
1374 | i_see_dead_people: | 1385 | i_see_dead_people: |
1375 | nf_ct_iterate_cleanup(net, kill_all, NULL); | 1386 | busy = 0; |
1376 | nf_ct_release_dying_list(net); | 1387 | list_for_each_entry(net, net_exit_list, exit_list) { |
1377 | if (atomic_read(&net->ct.count) != 0) { | 1388 | nf_ct_iterate_cleanup(net, kill_all, NULL); |
1389 | nf_ct_release_dying_list(net); | ||
1390 | if (atomic_read(&net->ct.count) != 0) | ||
1391 | busy = 1; | ||
1392 | } | ||
1393 | if (busy) { | ||
1378 | schedule(); | 1394 | schedule(); |
1379 | goto i_see_dead_people; | 1395 | goto i_see_dead_people; |
1380 | } | 1396 | } |
1381 | 1397 | ||
1382 | nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); | 1398 | list_for_each_entry(net, net_exit_list, exit_list) { |
1383 | nf_conntrack_proto_pernet_fini(net); | 1399 | nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); |
1384 | nf_conntrack_helper_pernet_fini(net); | 1400 | nf_conntrack_proto_pernet_fini(net); |
1385 | nf_conntrack_ecache_pernet_fini(net); | 1401 | nf_conntrack_helper_pernet_fini(net); |
1386 | nf_conntrack_tstamp_pernet_fini(net); | 1402 | nf_conntrack_ecache_pernet_fini(net); |
1387 | nf_conntrack_acct_pernet_fini(net); | 1403 | nf_conntrack_tstamp_pernet_fini(net); |
1388 | nf_conntrack_expect_pernet_fini(net); | 1404 | nf_conntrack_acct_pernet_fini(net); |
1389 | kmem_cache_destroy(net->ct.nf_conntrack_cachep); | 1405 | nf_conntrack_expect_pernet_fini(net); |
1390 | kfree(net->ct.slabname); | 1406 | kmem_cache_destroy(net->ct.nf_conntrack_cachep); |
1391 | free_percpu(net->ct.stat); | 1407 | kfree(net->ct.slabname); |
1408 | free_percpu(net->ct.stat); | ||
1409 | } | ||
1392 | } | 1410 | } |
1393 | 1411 | ||
1394 | void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) | 1412 | void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) |
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 6bcce401fd1c..6c69fbdb8361 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c | |||
@@ -545,16 +545,20 @@ out_init: | |||
545 | return ret; | 545 | return ret; |
546 | } | 546 | } |
547 | 547 | ||
548 | static void nf_conntrack_pernet_exit(struct net *net) | 548 | static void nf_conntrack_pernet_exit(struct list_head *net_exit_list) |
549 | { | 549 | { |
550 | nf_conntrack_standalone_fini_sysctl(net); | 550 | struct net *net; |
551 | nf_conntrack_standalone_fini_proc(net); | 551 | |
552 | nf_conntrack_cleanup_net(net); | 552 | list_for_each_entry(net, net_exit_list, exit_list) { |
553 | nf_conntrack_standalone_fini_sysctl(net); | ||
554 | nf_conntrack_standalone_fini_proc(net); | ||
555 | } | ||
556 | nf_conntrack_cleanup_net_list(net_exit_list); | ||
553 | } | 557 | } |
554 | 558 | ||
555 | static struct pernet_operations nf_conntrack_net_ops = { | 559 | static struct pernet_operations nf_conntrack_net_ops = { |
556 | .init = nf_conntrack_pernet_init, | 560 | .init = nf_conntrack_pernet_init, |
557 | .exit = nf_conntrack_pernet_exit, | 561 | .exit_batch = nf_conntrack_pernet_exit, |
558 | }; | 562 | }; |
559 | 563 | ||
560 | static int __init nf_conntrack_standalone_init(void) | 564 | static int __init nf_conntrack_standalone_init(void) |