aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv4/inet_timewait_sock.c39
1 files changed, 24 insertions, 15 deletions
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index 1f5d508bb18b..d38ca7c77b93 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -427,31 +427,40 @@ void inet_twsk_purge(struct net *net, struct inet_hashinfo *hashinfo,
427 struct inet_timewait_sock *tw; 427 struct inet_timewait_sock *tw;
428 struct sock *sk; 428 struct sock *sk;
429 struct hlist_nulls_node *node; 429 struct hlist_nulls_node *node;
430 int h; 430 unsigned int slot;
431 431
432 local_bh_disable(); 432 for (slot = 0; slot <= hashinfo->ehash_mask; slot++) {
433 for (h = 0; h <= hashinfo->ehash_mask; h++) { 433 struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
434 struct inet_ehash_bucket *head = 434restart_rcu:
435 inet_ehash_bucket(hashinfo, h); 435 rcu_read_lock();
436 spinlock_t *lock = inet_ehash_lockp(hashinfo, h);
437restart: 436restart:
438 spin_lock(lock); 437 sk_nulls_for_each_rcu(sk, node, &head->twchain) {
439 sk_nulls_for_each(sk, node, &head->twchain) {
440
441 tw = inet_twsk(sk); 438 tw = inet_twsk(sk);
442 if (!net_eq(twsk_net(tw), net) || 439 if (!net_eq(twsk_net(tw), net) ||
443 tw->tw_family != family) 440 tw->tw_family != family)
444 continue; 441 continue;
445 442
446 atomic_inc(&tw->tw_refcnt); 443 if (unlikely(!atomic_inc_not_zero(&tw->tw_refcnt)))
447 spin_unlock(lock); 444 continue;
445
446 if (unlikely(!net_eq(twsk_net(tw), net) ||
447 tw->tw_family != family)) {
448 inet_twsk_put(tw);
449 goto restart;
450 }
451
452 rcu_read_unlock();
448 inet_twsk_deschedule(tw, twdr); 453 inet_twsk_deschedule(tw, twdr);
449 inet_twsk_put(tw); 454 inet_twsk_put(tw);
450 455 goto restart_rcu;
451 goto restart;
452 } 456 }
453 spin_unlock(lock); 457 /* If the nulls value we got at the end of this lookup is
458 * not the expected one, we must restart lookup.
459 * We probably met an item that was moved to another chain.
460 */
461 if (get_nulls_value(node) != slot)
462 goto restart;
463 rcu_read_unlock();
454 } 464 }
455 local_bh_enable();
456} 465}
457EXPORT_SYMBOL_GPL(inet_twsk_purge); 466EXPORT_SYMBOL_GPL(inet_twsk_purge);