tcp/dccp: avoid one atomic operation for timewait hashdance
authorEric Dumazet <edumazet@google.com>
Tue, 12 Dec 2017 05:25:12 +0000 (21:25 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 13 Dec 2017 19:33:10 +0000 (14:33 -0500)
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>
include/net/inet_timewait_sock.h
net/dccp/minisocks.c
net/ipv4/inet_timewait_sock.c
net/ipv4/tcp_minisocks.c

index 1356fa6a7566bf8b53632215ef8de4b153848f9b..899495589a7ea2bf693cdda42f83cec160e861b5 100644 (file)
@@ -93,8 +93,8 @@ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk,
                                           struct inet_timewait_death_row *dr,
                                           const int state);
 
-void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
-                          struct inet_hashinfo *hashinfo);
+void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
+                        struct inet_hashinfo *hashinfo);
 
 void __inet_twsk_schedule(struct inet_timewait_sock *tw, int timeo,
                          bool rearm);
index 178bb9833311f83205317b07fe64cb2e45a9f734..37ccbe62eb1af3f9dffbf63323c008cc96cd8ea1 100644 (file)
@@ -63,9 +63,10 @@ void dccp_time_wait(struct sock *sk, int state, int timeo)
                 */
                local_bh_disable();
                inet_twsk_schedule(tw, timeo);
-               /* Linkage updates. */
-               __inet_twsk_hashdance(tw, sk, &dccp_hashinfo);
-               inet_twsk_put(tw);
+               /* Linkage updates.
+                * Note that access to tw after this point is illegal.
+                */
+               inet_twsk_hashdance(tw, sk, &dccp_hashinfo);
                local_bh_enable();
        } else {
                /* Sorry, if we're out of memory, just CLOSE this
index b563e0c46bac2362acccf38495546a8b6b726384..277ff69a312dca1d0bc04be4b0b36db133aaf63b 100644 (file)
@@ -97,7 +97,7 @@ static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
  * Essentially we whip up a timewait bucket, copy the relevant info into it
  * from the SK, and mess with hash chains and list linkage.
  */
-void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
+void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
                           struct inet_hashinfo *hashinfo)
 {
        const struct inet_sock *inet = inet_sk(sk);
@@ -119,18 +119,6 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
 
        spin_lock(lock);
 
-       /*
-        * Step 2: Hash TW into tcp ehash chain.
-        * Notes :
-        * - tw_refcnt is set to 4 because :
-        * - We have one reference from bhash chain.
-        * - We have one reference from ehash chain.
-        * - We have one reference from timer.
-        * - One reference for ourself (our caller will release it).
-        * We can use atomic_set() because prior spin_lock()/spin_unlock()
-        * committed into memory all tw fields.
-        */
-       refcount_set(&tw->tw_refcnt, 4);
        inet_twsk_add_node_rcu(tw, &ehead->chain);
 
        /* Step 3: Remove SK from hash chain */
@@ -138,8 +126,19 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
                sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
 
        spin_unlock(lock);
+
+       /* tw_refcnt is set to 3 because we have :
+        * - one reference for bhash chain.
+        * - one reference for ehash chain.
+        * - one reference for timer.
+        * We can use atomic_set() because prior spin_lock()/spin_unlock()
+        * committed into memory all tw fields.
+        * Also note that after this point, we lost our implicit reference
+        * so we are not allowed to use tw anymore.
+        */
+       refcount_set(&tw->tw_refcnt, 3);
 }
-EXPORT_SYMBOL_GPL(__inet_twsk_hashdance);
+EXPORT_SYMBOL_GPL(inet_twsk_hashdance);
 
 static void tw_timer_handler(struct timer_list *t)
 {
index b079b619b60ca577d5ef20a5065fce87acecd96c..a8384b0c11f8fa589e2ed5311899b62c80a269f8 100644 (file)
@@ -316,9 +316,10 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
                 */
                local_bh_disable();
                inet_twsk_schedule(tw, timeo);
-               /* Linkage updates. */
-               __inet_twsk_hashdance(tw, sk, &tcp_hashinfo);
-               inet_twsk_put(tw);
+               /* Linkage updates.
+                * Note that access to tw after this point is illegal.
+                */
+               inet_twsk_hashdance(tw, sk, &tcp_hashinfo);
                local_bh_enable();
        } else {
                /* Sorry, if we're out of memory, just CLOSE this