tcp: Save unnecessary inet_twsk_purge() calls.
While destroying netns, we call inet_twsk_purge() in tcp_sk_exit_batch()
and tcpv6_net_exit_batch() for AF_INET and AF_INET6. These commands
trigger the kernel to walk through the potentially big ehash twice even
though the netns has no TIME_WAIT sockets.
# ip netns add test
# ip netns del test
or
# unshare -n /bin/true >/dev/null
When tw_refcount is 1, we need not call inet_twsk_purge() at least
for the net. We can save such unneeded iterations if all netns in
net_exit_list have no TIME_WAIT sockets. This change eliminates
the tax by the additional unshare() described in the next patch to
guarantee the per-netns ehash size.
Tested:
# mount -t debugfs none /sys/kernel/debug/
# echo cleanup_net > /sys/kernel/debug/tracing/set_ftrace_filter
# echo inet_twsk_purge >> /sys/kernel/debug/tracing/set_ftrace_filter
# echo function > /sys/kernel/debug/tracing/current_tracer
# cat ./add_del_unshare.sh
for i in `seq 1 40`
do
(for j in `seq 1 100` ; do unshare -n /bin/true >/dev/null ; done) &
done
wait;
# ./add_del_unshare.sh
Before the patch:
# cat /sys/kernel/debug/tracing/trace_pipe
kworker/u128:0-8 [031] ...1. 174.162765: cleanup_net <-process_one_work
kworker/u128:0-8 [031] ...1. 174.240796: inet_twsk_purge <-cleanup_net
kworker/u128:0-8 [032] ...1. 174.244759: inet_twsk_purge <-tcp_sk_exit_batch
kworker/u128:0-8 [034] ...1. 174.290861: cleanup_net <-process_one_work
kworker/u128:0-8 [039] ...1. 175.245027: inet_twsk_purge <-cleanup_net
kworker/u128:0-8 [046] ...1. 175.290541: inet_twsk_purge <-tcp_sk_exit_batch
kworker/u128:0-8 [037] ...1. 175.321046: cleanup_net <-process_one_work
kworker/u128:0-8 [024] ...1. 175.941633: inet_twsk_purge <-cleanup_net
kworker/u128:0-8 [025] ...1. 176.242539: inet_twsk_purge <-tcp_sk_exit_batch
After:
# cat /sys/kernel/debug/tracing/trace_pipe
kworker/u128:0-8 [038] ...1. 428.116174: cleanup_net <-process_one_work
kworker/u128:0-8 [038] ...1. 428.262532: cleanup_net <-process_one_work
kworker/u128:0-8 [030] ...1. 429.292645: cleanup_net <-process_one_work
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
4461568aa4
commit
edc12f032a
@@ -346,6 +346,7 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb);
|
|||||||
void tcp_rcv_space_adjust(struct sock *sk);
|
void tcp_rcv_space_adjust(struct sock *sk);
|
||||||
int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp);
|
int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp);
|
||||||
void tcp_twsk_destructor(struct sock *sk);
|
void tcp_twsk_destructor(struct sock *sk);
|
||||||
|
void tcp_twsk_purge(struct list_head *net_exit_list, int family);
|
||||||
ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos,
|
ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos,
|
||||||
struct pipe_inode_info *pipe, size_t len,
|
struct pipe_inode_info *pipe, size_t len,
|
||||||
unsigned int flags);
|
unsigned int flags);
|
||||||
|
|||||||
@@ -3206,7 +3206,7 @@ static void __net_exit tcp_sk_exit_batch(struct list_head *net_exit_list)
|
|||||||
{
|
{
|
||||||
struct net *net;
|
struct net *net;
|
||||||
|
|
||||||
inet_twsk_purge(&tcp_hashinfo, AF_INET);
|
tcp_twsk_purge(net_exit_list, AF_INET);
|
||||||
|
|
||||||
list_for_each_entry(net, net_exit_list, exit_list) {
|
list_for_each_entry(net, net_exit_list, exit_list) {
|
||||||
WARN_ON_ONCE(!refcount_dec_and_test(&net->ipv4.tcp_death_row.tw_refcount));
|
WARN_ON_ONCE(!refcount_dec_and_test(&net->ipv4.tcp_death_row.tw_refcount));
|
||||||
|
|||||||
@@ -347,6 +347,21 @@ void tcp_twsk_destructor(struct sock *sk)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
|
EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
|
||||||
|
|
||||||
|
void tcp_twsk_purge(struct list_head *net_exit_list, int family)
|
||||||
|
{
|
||||||
|
struct net *net;
|
||||||
|
|
||||||
|
list_for_each_entry(net, net_exit_list, exit_list) {
|
||||||
|
/* The last refcount is decremented in tcp_sk_exit_batch() */
|
||||||
|
if (refcount_read(&net->ipv4.tcp_death_row.tw_refcount) == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
inet_twsk_purge(&tcp_hashinfo, family);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tcp_twsk_purge);
|
||||||
|
|
||||||
/* Warning : This function is called without sk_listener being locked.
|
/* Warning : This function is called without sk_listener being locked.
|
||||||
* Be sure to read socket fields once, as their value could change under us.
|
* Be sure to read socket fields once, as their value could change under us.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2229,7 +2229,7 @@ static void __net_exit tcpv6_net_exit(struct net *net)
|
|||||||
|
|
||||||
static void __net_exit tcpv6_net_exit_batch(struct list_head *net_exit_list)
|
static void __net_exit tcpv6_net_exit_batch(struct list_head *net_exit_list)
|
||||||
{
|
{
|
||||||
inet_twsk_purge(&tcp_hashinfo, AF_INET6);
|
tcp_twsk_purge(net_exit_list, AF_INET6);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pernet_operations tcpv6_net_ops = {
|
static struct pernet_operations tcpv6_net_ops = {
|
||||||
|
|||||||
Reference in New Issue
Block a user