Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp/dccp: avoid one atomic operation for timewait hashdance

First, rename __inet_twsk_hashdance() to inet_twsk_hashdance()

Then, remove one inet_twsk_put() by setting tw_refcnt to 3 instead
of 4, but adding a fat warning that we do not have the right to access
tw anymore after inet_twsk_hashdance()

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
ec94c269 1e757011

+23 -22
+2 -2
include/net/inet_timewait_sock.h
··· 93 93 struct inet_timewait_death_row *dr, 94 94 const int state); 95 95 96 - void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, 97 - struct inet_hashinfo *hashinfo); 96 + void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, 97 + struct inet_hashinfo *hashinfo); 98 98 99 99 void __inet_twsk_schedule(struct inet_timewait_sock *tw, int timeo, 100 100 bool rearm);
+4 -3
net/dccp/minisocks.c
··· 63 63 */ 64 64 local_bh_disable(); 65 65 inet_twsk_schedule(tw, timeo); 66 - /* Linkage updates. */ 67 - __inet_twsk_hashdance(tw, sk, &dccp_hashinfo); 68 - inet_twsk_put(tw); 66 + /* Linkage updates. 67 + * Note that access to tw after this point is illegal. 68 + */ 69 + inet_twsk_hashdance(tw, sk, &dccp_hashinfo); 69 70 local_bh_enable(); 70 71 } else { 71 72 /* Sorry, if we're out of memory, just CLOSE this
+13 -14
net/ipv4/inet_timewait_sock.c
··· 97 97 * Essentially we whip up a timewait bucket, copy the relevant info into it 98 98 * from the SK, and mess with hash chains and list linkage. 99 99 */ 100 - void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, 100 + void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, 101 101 struct inet_hashinfo *hashinfo) 102 102 { 103 103 const struct inet_sock *inet = inet_sk(sk); ··· 119 119 120 120 spin_lock(lock); 121 121 122 - /* 123 - * Step 2: Hash TW into tcp ehash chain. 124 - * Notes : 125 - * - tw_refcnt is set to 4 because : 126 - * - We have one reference from bhash chain. 127 - * - We have one reference from ehash chain. 128 - * - We have one reference from timer. 129 - * - One reference for ourself (our caller will release it). 130 - * We can use atomic_set() because prior spin_lock()/spin_unlock() 131 - * committed into memory all tw fields. 132 - */ 133 - refcount_set(&tw->tw_refcnt, 4); 134 122 inet_twsk_add_node_rcu(tw, &ehead->chain); 135 123 136 124 /* Step 3: Remove SK from hash chain */ ··· 126 138 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 127 139 128 140 spin_unlock(lock); 141 + 142 + /* tw_refcnt is set to 3 because we have : 143 + * - one reference for bhash chain. 144 + * - one reference for ehash chain. 145 + * - one reference for timer. 146 + * We can use atomic_set() because prior spin_lock()/spin_unlock() 147 + * committed into memory all tw fields. 148 + * Also note that after this point, we lost our implicit reference 149 + * so we are not allowed to use tw anymore. 150 + */ 151 + refcount_set(&tw->tw_refcnt, 3); 129 152 } 130 - EXPORT_SYMBOL_GPL(__inet_twsk_hashdance); 153 + EXPORT_SYMBOL_GPL(inet_twsk_hashdance); 131 154 132 155 static void tw_timer_handler(struct timer_list *t) 133 156 {
+4 -3
net/ipv4/tcp_minisocks.c
··· 316 316 */ 317 317 local_bh_disable(); 318 318 inet_twsk_schedule(tw, timeo); 319 - /* Linkage updates. */ 320 - __inet_twsk_hashdance(tw, sk, &tcp_hashinfo); 321 - inet_twsk_put(tw); 319 + /* Linkage updates. 320 + * Note that access to tw after this point is illegal. 321 + */ 322 + inet_twsk_hashdance(tw, sk, &tcp_hashinfo); 322 323 local_bh_enable(); 323 324 } else { 324 325 /* Sorry, if we're out of memory, just CLOSE this