1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e48c414eSArnaldo Carvalho de Melo /* 3e48c414eSArnaldo Carvalho de Melo * INET An implementation of the TCP/IP protocol suite for the LINUX 4e48c414eSArnaldo Carvalho de Melo * operating system. INET is implemented using the BSD Socket 5e48c414eSArnaldo Carvalho de Melo * interface as the means of communication with the user level. 6e48c414eSArnaldo Carvalho de Melo * 7e48c414eSArnaldo Carvalho de Melo * Generic TIME_WAIT sockets functions 8e48c414eSArnaldo Carvalho de Melo * 9e48c414eSArnaldo Carvalho de Melo * From code orinally in TCP 10e48c414eSArnaldo Carvalho de Melo */ 11e48c414eSArnaldo Carvalho de Melo 12172589ccSIlpo Järvinen #include <linux/kernel.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 143a9a231dSPaul Gortmaker #include <linux/module.h> 15e48c414eSArnaldo Carvalho de Melo #include <net/inet_hashtables.h> 16e48c414eSArnaldo Carvalho de Melo #include <net/inet_timewait_sock.h> 17696ab2d3SArnaldo Carvalho de Melo #include <net/ip.h> 18e48c414eSArnaldo Carvalho de Melo 1913475a30SEric Dumazet 202a8875e7SEric Dumazet /** 212a8875e7SEric Dumazet * inet_twsk_bind_unhash - unhash a timewait socket from bind hash 222a8875e7SEric Dumazet * @tw: timewait socket 232a8875e7SEric Dumazet * @hashinfo: hashinfo pointer 242a8875e7SEric Dumazet * 252a8875e7SEric Dumazet * unhash a timewait socket from bind hash, if hashed. 262a8875e7SEric Dumazet * bind hash lock must be held by caller. 272a8875e7SEric Dumazet * Returns 1 if caller should call inet_twsk_put() after lock release. 283cdaedaeSEric Dumazet */ 29fc01538fSEric Dumazet void inet_twsk_bind_unhash(struct inet_timewait_sock *tw, 303cdaedaeSEric Dumazet struct inet_hashinfo *hashinfo) 313cdaedaeSEric Dumazet { 32936a192fSKuniyuki Iwashima struct inet_bind2_bucket *tb2 = tw->tw_tb2; 333cdaedaeSEric Dumazet struct inet_bind_bucket *tb = tw->tw_tb; 343cdaedaeSEric Dumazet 353cdaedaeSEric Dumazet if (!tb) 36fc01538fSEric Dumazet return; 373cdaedaeSEric Dumazet 383cdaedaeSEric Dumazet __hlist_del(&tw->tw_bind_node); 393cdaedaeSEric Dumazet tw->tw_tb = NULL; 40936a192fSKuniyuki Iwashima 41936a192fSKuniyuki Iwashima __hlist_del(&tw->tw_bind2_node); 42936a192fSKuniyuki Iwashima tw->tw_tb2 = NULL; 43936a192fSKuniyuki Iwashima inet_bind2_bucket_destroy(hashinfo->bind2_bucket_cachep, tb2); 44*8002d44fSKuniyuki Iwashima inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); 45936a192fSKuniyuki Iwashima 46fc01538fSEric Dumazet __sock_put((struct sock *)tw); 473cdaedaeSEric Dumazet } 483cdaedaeSEric Dumazet 49e48c414eSArnaldo Carvalho de Melo /* Must be called with locally disabled BHs. */ 50789f558cSEric Dumazet static void inet_twsk_kill(struct inet_timewait_sock *tw) 51e48c414eSArnaldo Carvalho de Melo { 52789f558cSEric Dumazet struct inet_hashinfo *hashinfo = tw->tw_dr->hashinfo; 539db66bdcSEric Dumazet spinlock_t *lock = inet_ehash_lockp(hashinfo, tw->tw_hash); 54936a192fSKuniyuki Iwashima struct inet_bind_hashbucket *bhead, *bhead2; 55e48c414eSArnaldo Carvalho de Melo 569db66bdcSEric Dumazet spin_lock(lock); 57fc01538fSEric Dumazet sk_nulls_del_node_init_rcu((struct sock *)tw); 589db66bdcSEric Dumazet spin_unlock(lock); 59e48c414eSArnaldo Carvalho de Melo 60e48c414eSArnaldo Carvalho de Melo /* Disassociate with bind bucket. */ 6104c494e6SEric Dumazet bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), tw->tw_num, 6204c494e6SEric Dumazet hashinfo->bhash_size)]; 63936a192fSKuniyuki Iwashima bhead2 = inet_bhashfn_portaddr(hashinfo, (struct sock *)tw, 64936a192fSKuniyuki Iwashima twsk_net(tw), tw->tw_num); 653cdaedaeSEric Dumazet 66e48c414eSArnaldo Carvalho de Melo spin_lock(&bhead->lock); 67936a192fSKuniyuki Iwashima spin_lock(&bhead2->lock); 68fc01538fSEric Dumazet inet_twsk_bind_unhash(tw, hashinfo); 69936a192fSKuniyuki Iwashima spin_unlock(&bhead2->lock); 70e48c414eSArnaldo Carvalho de Melo spin_unlock(&bhead->lock); 713cdaedaeSEric Dumazet 72e9bd0ccaSKuniyuki Iwashima refcount_dec(&tw->tw_dr->tw_refcount); 73789f558cSEric Dumazet inet_twsk_put(tw); 74e48c414eSArnaldo Carvalho de Melo } 75e48c414eSArnaldo Carvalho de Melo 7605dbc7b5SEric Dumazet void inet_twsk_free(struct inet_timewait_sock *tw) 777054fb93SPavel Emelyanov { 787054fb93SPavel Emelyanov struct module *owner = tw->tw_prot->owner; 797054fb93SPavel Emelyanov twsk_destructor((struct sock *)tw); 807054fb93SPavel Emelyanov kmem_cache_free(tw->tw_prot->twsk_prot->twsk_slab, tw); 817054fb93SPavel Emelyanov module_put(owner); 827054fb93SPavel Emelyanov } 834dbc8ef7SArnaldo Carvalho de Melo 844dbc8ef7SArnaldo Carvalho de Melo void inet_twsk_put(struct inet_timewait_sock *tw) 854dbc8ef7SArnaldo Carvalho de Melo { 8641c6d650SReshetova, Elena if (refcount_dec_and_test(&tw->tw_refcnt)) 874dbc8ef7SArnaldo Carvalho de Melo inet_twsk_free(tw); 887054fb93SPavel Emelyanov } 897054fb93SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_twsk_put); 907054fb93SPavel Emelyanov 9181b3ade5SKuniyuki Iwashima static void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw, 9205dbc7b5SEric Dumazet struct hlist_nulls_head *list) 9305dbc7b5SEric Dumazet { 9481b3ade5SKuniyuki Iwashima hlist_nulls_add_head_rcu(&tw->tw_node, list); 9505dbc7b5SEric Dumazet } 9605dbc7b5SEric Dumazet 9705dbc7b5SEric Dumazet static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw, 9805dbc7b5SEric Dumazet struct hlist_head *list) 9905dbc7b5SEric Dumazet { 10005dbc7b5SEric Dumazet hlist_add_head(&tw->tw_bind_node, list); 10105dbc7b5SEric Dumazet } 10205dbc7b5SEric Dumazet 103936a192fSKuniyuki Iwashima static void inet_twsk_add_bind2_node(struct inet_timewait_sock *tw, 104936a192fSKuniyuki Iwashima struct hlist_head *list) 105936a192fSKuniyuki Iwashima { 106936a192fSKuniyuki Iwashima hlist_add_head(&tw->tw_bind2_node, list); 107936a192fSKuniyuki Iwashima } 108936a192fSKuniyuki Iwashima 109e48c414eSArnaldo Carvalho de Melo /* 110e599ea14SEric Dumazet * Enter the time wait state. This is called with locally disabled BH. 111e48c414eSArnaldo Carvalho de Melo * Essentially we whip up a timewait bucket, copy the relevant info into it 112e48c414eSArnaldo Carvalho de Melo * from the SK, and mess with hash chains and list linkage. 113e48c414eSArnaldo Carvalho de Melo */ 114ec94c269SEric Dumazet void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, 115e48c414eSArnaldo Carvalho de Melo struct inet_hashinfo *hashinfo) 116e48c414eSArnaldo Carvalho de Melo { 117e48c414eSArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 118463c84b9SArnaldo Carvalho de Melo const struct inet_connection_sock *icsk = inet_csk(sk); 11981c3d547SEric Dumazet struct inet_ehash_bucket *ehead = inet_ehash_bucket(hashinfo, sk->sk_hash); 1209db66bdcSEric Dumazet spinlock_t *lock = inet_ehash_lockp(hashinfo, sk->sk_hash); 121936a192fSKuniyuki Iwashima struct inet_bind_hashbucket *bhead, *bhead2; 122936a192fSKuniyuki Iwashima 123e48c414eSArnaldo Carvalho de Melo /* Step 1: Put TW into bind hash. Original socket stays there too. 124e48c414eSArnaldo Carvalho de Melo Note, that any socket with inet->num != 0 MUST be bound in 125e48c414eSArnaldo Carvalho de Melo binding cache, even if it is closed. 126e48c414eSArnaldo Carvalho de Melo */ 12704c494e6SEric Dumazet bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), inet->inet_num, 12804c494e6SEric Dumazet hashinfo->bhash_size)]; 129936a192fSKuniyuki Iwashima bhead2 = inet_bhashfn_portaddr(hashinfo, sk, twsk_net(tw), inet->inet_num); 130936a192fSKuniyuki Iwashima 131e599ea14SEric Dumazet spin_lock(&bhead->lock); 132936a192fSKuniyuki Iwashima spin_lock(&bhead2->lock); 133936a192fSKuniyuki Iwashima 134463c84b9SArnaldo Carvalho de Melo tw->tw_tb = icsk->icsk_bind_hash; 135547b792cSIlpo Järvinen WARN_ON(!icsk->icsk_bind_hash); 136e48c414eSArnaldo Carvalho de Melo inet_twsk_add_bind_node(tw, &tw->tw_tb->owners); 137936a192fSKuniyuki Iwashima 138936a192fSKuniyuki Iwashima tw->tw_tb2 = icsk->icsk_bind2_hash; 139936a192fSKuniyuki Iwashima WARN_ON(!icsk->icsk_bind2_hash); 140936a192fSKuniyuki Iwashima inet_twsk_add_bind2_node(tw, &tw->tw_tb2->deathrow); 141936a192fSKuniyuki Iwashima 142936a192fSKuniyuki Iwashima spin_unlock(&bhead2->lock); 143e48c414eSArnaldo Carvalho de Melo spin_unlock(&bhead->lock); 144e48c414eSArnaldo Carvalho de Melo 1459db66bdcSEric Dumazet spin_lock(lock); 146e48c414eSArnaldo Carvalho de Melo 14781b3ade5SKuniyuki Iwashima inet_twsk_add_node_rcu(tw, &ehead->chain); 1483ab5aee7SEric Dumazet 14905dbc7b5SEric Dumazet /* Step 3: Remove SK from hash chain */ 1503ab5aee7SEric Dumazet if (__sk_nulls_del_node_init_rcu(sk)) 1513ab5aee7SEric Dumazet sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 152e48c414eSArnaldo Carvalho de Melo 153e599ea14SEric Dumazet spin_unlock(lock); 154ec94c269SEric Dumazet 155ec94c269SEric Dumazet /* tw_refcnt is set to 3 because we have : 156ec94c269SEric Dumazet * - one reference for bhash chain. 157ec94c269SEric Dumazet * - one reference for ehash chain. 158ec94c269SEric Dumazet * - one reference for timer. 159ec94c269SEric Dumazet * We can use atomic_set() because prior spin_lock()/spin_unlock() 160ec94c269SEric Dumazet * committed into memory all tw fields. 161ec94c269SEric Dumazet * Also note that after this point, we lost our implicit reference 162ec94c269SEric Dumazet * so we are not allowed to use tw anymore. 163ec94c269SEric Dumazet */ 164ec94c269SEric Dumazet refcount_set(&tw->tw_refcnt, 3); 165e48c414eSArnaldo Carvalho de Melo } 166ec94c269SEric Dumazet EXPORT_SYMBOL_GPL(inet_twsk_hashdance); 167696ab2d3SArnaldo Carvalho de Melo 1681ab791dcSKees Cook static void tw_timer_handler(struct timer_list *t) 169c676270bSArnaldo Carvalho de Melo { 1701ab791dcSKees Cook struct inet_timewait_sock *tw = from_timer(tw, t, tw_timer); 171789f558cSEric Dumazet 172789f558cSEric Dumazet inet_twsk_kill(tw); 173789f558cSEric Dumazet } 174789f558cSEric Dumazet 175789f558cSEric Dumazet struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, 176789f558cSEric Dumazet struct inet_timewait_death_row *dr, 177789f558cSEric Dumazet const int state) 178789f558cSEric Dumazet { 179789f558cSEric Dumazet struct inet_timewait_sock *tw; 180789f558cSEric Dumazet 1816f605b57SKuniyuki Iwashima if (refcount_read(&dr->tw_refcount) - 1 >= 1826f605b57SKuniyuki Iwashima READ_ONCE(dr->sysctl_max_tw_buckets)) 183789f558cSEric Dumazet return NULL; 184789f558cSEric Dumazet 185789f558cSEric Dumazet tw = kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab, 18654e6ecb2SChristoph Lameter GFP_ATOMIC); 18700db4124SIan Morris if (tw) { 188c676270bSArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 189c676270bSArnaldo Carvalho de Melo 190789f558cSEric Dumazet tw->tw_dr = dr; 191c676270bSArnaldo Carvalho de Melo /* Give us an identity. */ 192c720c7e8SEric Dumazet tw->tw_daddr = inet->inet_daddr; 193c720c7e8SEric Dumazet tw->tw_rcv_saddr = inet->inet_rcv_saddr; 194c676270bSArnaldo Carvalho de Melo tw->tw_bound_dev_if = sk->sk_bound_dev_if; 19566b13d99SEric Dumazet tw->tw_tos = inet->tos; 196c720c7e8SEric Dumazet tw->tw_num = inet->inet_num; 197c676270bSArnaldo Carvalho de Melo tw->tw_state = TCP_TIME_WAIT; 198c676270bSArnaldo Carvalho de Melo tw->tw_substate = state; 199c720c7e8SEric Dumazet tw->tw_sport = inet->inet_sport; 200c720c7e8SEric Dumazet tw->tw_dport = inet->inet_dport; 201c676270bSArnaldo Carvalho de Melo tw->tw_family = sk->sk_family; 202c676270bSArnaldo Carvalho de Melo tw->tw_reuse = sk->sk_reuse; 2033099a529SEric Dumazet tw->tw_reuseport = sk->sk_reuseport; 20481c3d547SEric Dumazet tw->tw_hash = sk->sk_hash; 205c676270bSArnaldo Carvalho de Melo tw->tw_ipv6only = 0; 2064bd0623fSEric Dumazet tw->tw_transparent = inet_test_bit(TRANSPARENT, sk); 207c676270bSArnaldo Carvalho de Melo tw->tw_prot = sk->sk_prot_creator; 20833cf7c90SEric Dumazet atomic64_set(&tw->tw_cookie, atomic64_read(&sk->sk_cookie)); 209efd7ef1cSEric W. Biederman twsk_net_set(tw, sock_net(sk)); 2101ab791dcSKees Cook timer_setup(&tw->tw_timer, tw_timer_handler, TIMER_PINNED); 21147e1c323SEric Dumazet /* 21247e1c323SEric Dumazet * Because we use RCU lookups, we should not set tw_refcnt 21347e1c323SEric Dumazet * to a non null value before everything is setup for this 21447e1c323SEric Dumazet * timewait socket. 21547e1c323SEric Dumazet */ 21641c6d650SReshetova, Elena refcount_set(&tw->tw_refcnt, 0); 217789f558cSEric Dumazet 218eeb2b856SArnaldo Carvalho de Melo __module_get(tw->tw_prot->owner); 219c676270bSArnaldo Carvalho de Melo } 220c676270bSArnaldo Carvalho de Melo 221c676270bSArnaldo Carvalho de Melo return tw; 222c676270bSArnaldo Carvalho de Melo } 223696ab2d3SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(inet_twsk_alloc); 224696ab2d3SArnaldo Carvalho de Melo 225696ab2d3SArnaldo Carvalho de Melo /* These are always called from BH context. See callers in 226696ab2d3SArnaldo Carvalho de Melo * tcp_input.c to verify this. 227696ab2d3SArnaldo Carvalho de Melo */ 228696ab2d3SArnaldo Carvalho de Melo 229dbe7faa4SEric Dumazet /* This is for handling early-kills of TIME_WAIT sockets. 230dbe7faa4SEric Dumazet * Warning : consume reference. 231dbe7faa4SEric Dumazet * Caller should not access tw anymore. 232dbe7faa4SEric Dumazet */ 233dbe7faa4SEric Dumazet void inet_twsk_deschedule_put(struct inet_timewait_sock *tw) 234696ab2d3SArnaldo Carvalho de Melo { 235789f558cSEric Dumazet if (del_timer_sync(&tw->tw_timer)) 236789f558cSEric Dumazet inet_twsk_kill(tw); 237dbe7faa4SEric Dumazet inet_twsk_put(tw); 238696ab2d3SArnaldo Carvalho de Melo } 239dbe7faa4SEric Dumazet EXPORT_SYMBOL(inet_twsk_deschedule_put); 240696ab2d3SArnaldo Carvalho de Melo 241ed2e9239SEric Dumazet void __inet_twsk_schedule(struct inet_timewait_sock *tw, int timeo, bool rearm) 242696ab2d3SArnaldo Carvalho de Melo { 243696ab2d3SArnaldo Carvalho de Melo /* timeout := RTO * 3.5 244696ab2d3SArnaldo Carvalho de Melo * 245696ab2d3SArnaldo Carvalho de Melo * 3.5 = 1+2+0.5 to wait for two retransmits. 246696ab2d3SArnaldo Carvalho de Melo * 247696ab2d3SArnaldo Carvalho de Melo * RATIONALE: if FIN arrived and we entered TIME-WAIT state, 248696ab2d3SArnaldo Carvalho de Melo * our ACK acking that FIN can be lost. If N subsequent retransmitted 249696ab2d3SArnaldo Carvalho de Melo * FINs (or previous seqments) are lost (probability of such event 250696ab2d3SArnaldo Carvalho de Melo * is p^(N+1), where p is probability to lose single packet and 251696ab2d3SArnaldo Carvalho de Melo * time to detect the loss is about RTO*(2^N - 1) with exponential 252696ab2d3SArnaldo Carvalho de Melo * backoff). Normal timewait length is calculated so, that we 253696ab2d3SArnaldo Carvalho de Melo * waited at least for one retransmitted FIN (maximal RTO is 120sec). 254696ab2d3SArnaldo Carvalho de Melo * [ BTW Linux. following BSD, violates this requirement waiting 255696ab2d3SArnaldo Carvalho de Melo * only for 60sec, we should wait at least for 240 secs. 256696ab2d3SArnaldo Carvalho de Melo * Well, 240 consumes too much of resources 8) 257696ab2d3SArnaldo Carvalho de Melo * ] 258696ab2d3SArnaldo Carvalho de Melo * This interval is not reduced to catch old duplicate and 259696ab2d3SArnaldo Carvalho de Melo * responces to our wandering segments living for two MSLs. 260696ab2d3SArnaldo Carvalho de Melo * However, if we use PAWS to detect 261696ab2d3SArnaldo Carvalho de Melo * old duplicates, we can reduce the interval to bounds required 262696ab2d3SArnaldo Carvalho de Melo * by RTO, rather than MSL. So, if peer understands PAWS, we 263696ab2d3SArnaldo Carvalho de Melo * kill tw bucket after 3.5*RTO (it is important that this number 264696ab2d3SArnaldo Carvalho de Melo * is greater than TS tick!) and detect old duplicates with help 265696ab2d3SArnaldo Carvalho de Melo * of PAWS. 266696ab2d3SArnaldo Carvalho de Melo */ 267696ab2d3SArnaldo Carvalho de Melo 268ed2e9239SEric Dumazet if (!rearm) { 26927dd35e0SEric Dumazet bool kill = timeo <= 4*HZ; 27027dd35e0SEric Dumazet 27127dd35e0SEric Dumazet __NET_INC_STATS(twsk_net(tw), kill ? LINUX_MIB_TIMEWAITKILLED : 27227dd35e0SEric Dumazet LINUX_MIB_TIMEWAITED); 273f3438bc7SThomas Gleixner BUG_ON(mod_timer(&tw->tw_timer, jiffies + timeo)); 274fbb82952SEric Dumazet refcount_inc(&tw->tw_dr->tw_refcount); 275ed2e9239SEric Dumazet } else { 276ed2e9239SEric Dumazet mod_timer_pending(&tw->tw_timer, jiffies + timeo); 277696ab2d3SArnaldo Carvalho de Melo } 278696ab2d3SArnaldo Carvalho de Melo } 279ed2e9239SEric Dumazet EXPORT_SYMBOL_GPL(__inet_twsk_schedule); 28004c494e6SEric Dumazet 28104c494e6SEric Dumazet void inet_twsk_purge(struct inet_hashinfo *hashinfo, int family) 28204c494e6SEric Dumazet { 28304c494e6SEric Dumazet struct inet_timewait_sock *tw; 28404c494e6SEric Dumazet struct sock *sk; 28504c494e6SEric Dumazet struct hlist_nulls_node *node; 28604c494e6SEric Dumazet unsigned int slot; 28704c494e6SEric Dumazet 28804c494e6SEric Dumazet for (slot = 0; slot <= hashinfo->ehash_mask; slot++) { 28904c494e6SEric Dumazet struct inet_ehash_bucket *head = &hashinfo->ehash[slot]; 29004c494e6SEric Dumazet restart_rcu: 29104c494e6SEric Dumazet cond_resched(); 29204c494e6SEric Dumazet rcu_read_lock(); 29304c494e6SEric Dumazet restart: 29404c494e6SEric Dumazet sk_nulls_for_each_rcu(sk, node, &head->chain) { 295740ea3c4SKuniyuki Iwashima if (sk->sk_state != TCP_TIME_WAIT) { 296740ea3c4SKuniyuki Iwashima /* A kernel listener socket might not hold refcnt for net, 297740ea3c4SKuniyuki Iwashima * so reqsk_timer_handler() could be fired after net is 298740ea3c4SKuniyuki Iwashima * freed. Userspace listener and reqsk never exist here. 299740ea3c4SKuniyuki Iwashima */ 300740ea3c4SKuniyuki Iwashima if (unlikely(sk->sk_state == TCP_NEW_SYN_RECV && 301740ea3c4SKuniyuki Iwashima hashinfo->pernet)) { 302740ea3c4SKuniyuki Iwashima struct request_sock *req = inet_reqsk(sk); 303740ea3c4SKuniyuki Iwashima 304740ea3c4SKuniyuki Iwashima inet_csk_reqsk_queue_drop_and_put(req->rsk_listener, req); 305740ea3c4SKuniyuki Iwashima } 306740ea3c4SKuniyuki Iwashima 30704c494e6SEric Dumazet continue; 308740ea3c4SKuniyuki Iwashima } 309740ea3c4SKuniyuki Iwashima 31004c494e6SEric Dumazet tw = inet_twsk(sk); 31104c494e6SEric Dumazet if ((tw->tw_family != family) || 31204c494e6SEric Dumazet refcount_read(&twsk_net(tw)->ns.count)) 31304c494e6SEric Dumazet continue; 31404c494e6SEric Dumazet 31504c494e6SEric Dumazet if (unlikely(!refcount_inc_not_zero(&tw->tw_refcnt))) 31604c494e6SEric Dumazet continue; 31704c494e6SEric Dumazet 31804c494e6SEric Dumazet if (unlikely((tw->tw_family != family) || 31904c494e6SEric Dumazet refcount_read(&twsk_net(tw)->ns.count))) { 32004c494e6SEric Dumazet inet_twsk_put(tw); 32104c494e6SEric Dumazet goto restart; 32204c494e6SEric Dumazet } 32304c494e6SEric Dumazet 32404c494e6SEric Dumazet rcu_read_unlock(); 32504c494e6SEric Dumazet local_bh_disable(); 32604c494e6SEric Dumazet inet_twsk_deschedule_put(tw); 32704c494e6SEric Dumazet local_bh_enable(); 32804c494e6SEric Dumazet goto restart_rcu; 32904c494e6SEric Dumazet } 33004c494e6SEric Dumazet /* If the nulls value we got at the end of this lookup is 33104c494e6SEric Dumazet * not the expected one, we must restart lookup. 33204c494e6SEric Dumazet * We probably met an item that was moved to another chain. 33304c494e6SEric Dumazet */ 33404c494e6SEric Dumazet if (get_nulls_value(node) != slot) 33504c494e6SEric Dumazet goto restart; 33604c494e6SEric Dumazet rcu_read_unlock(); 33704c494e6SEric Dumazet } 33804c494e6SEric Dumazet } 33904c494e6SEric Dumazet EXPORT_SYMBOL_GPL(inet_twsk_purge); 340