diff options
Diffstat (limited to 'net/ipv4/inet_timewait_sock.c')
-rw-r--r-- | net/ipv4/inet_timewait_sock.c | 38 |
1 files changed, 28 insertions, 10 deletions
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 31f931ef3daf..11a107a5af4f 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c | |||
@@ -14,22 +14,33 @@ | |||
14 | #include <net/inet_timewait_sock.h> | 14 | #include <net/inet_timewait_sock.h> |
15 | #include <net/ip.h> | 15 | #include <net/ip.h> |
16 | 16 | ||
17 | |||
18 | /* | ||
19 | * unhash a timewait socket from established hash | ||
20 | * lock must be hold by caller | ||
21 | */ | ||
22 | int inet_twsk_unhash(struct inet_timewait_sock *tw) | ||
23 | { | ||
24 | if (hlist_nulls_unhashed(&tw->tw_node)) | ||
25 | return 0; | ||
26 | |||
27 | hlist_nulls_del_rcu(&tw->tw_node); | ||
28 | sk_nulls_node_init(&tw->tw_node); | ||
29 | return 1; | ||
30 | } | ||
31 | |||
17 | /* Must be called with locally disabled BHs. */ | 32 | /* Must be called with locally disabled BHs. */ |
18 | static void __inet_twsk_kill(struct inet_timewait_sock *tw, | 33 | static void __inet_twsk_kill(struct inet_timewait_sock *tw, |
19 | struct inet_hashinfo *hashinfo) | 34 | struct inet_hashinfo *hashinfo) |
20 | { | 35 | { |
21 | struct inet_bind_hashbucket *bhead; | 36 | struct inet_bind_hashbucket *bhead; |
22 | struct inet_bind_bucket *tb; | 37 | struct inet_bind_bucket *tb; |
38 | int refcnt; | ||
23 | /* Unlink from established hashes. */ | 39 | /* Unlink from established hashes. */ |
24 | spinlock_t *lock = inet_ehash_lockp(hashinfo, tw->tw_hash); | 40 | spinlock_t *lock = inet_ehash_lockp(hashinfo, tw->tw_hash); |
25 | 41 | ||
26 | spin_lock(lock); | 42 | spin_lock(lock); |
27 | if (hlist_nulls_unhashed(&tw->tw_node)) { | 43 | refcnt = inet_twsk_unhash(tw); |
28 | spin_unlock(lock); | ||
29 | return; | ||
30 | } | ||
31 | hlist_nulls_del_rcu(&tw->tw_node); | ||
32 | sk_nulls_node_init(&tw->tw_node); | ||
33 | spin_unlock(lock); | 44 | spin_unlock(lock); |
34 | 45 | ||
35 | /* Disassociate with bind bucket. */ | 46 | /* Disassociate with bind bucket. */ |
@@ -37,9 +48,12 @@ static void __inet_twsk_kill(struct inet_timewait_sock *tw, | |||
37 | hashinfo->bhash_size)]; | 48 | hashinfo->bhash_size)]; |
38 | spin_lock(&bhead->lock); | 49 | spin_lock(&bhead->lock); |
39 | tb = tw->tw_tb; | 50 | tb = tw->tw_tb; |
40 | __hlist_del(&tw->tw_bind_node); | 51 | if (tb) { |
41 | tw->tw_tb = NULL; | 52 | __hlist_del(&tw->tw_bind_node); |
42 | inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); | 53 | tw->tw_tb = NULL; |
54 | inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); | ||
55 | refcnt++; | ||
56 | } | ||
43 | spin_unlock(&bhead->lock); | 57 | spin_unlock(&bhead->lock); |
44 | #ifdef SOCK_REFCNT_DEBUG | 58 | #ifdef SOCK_REFCNT_DEBUG |
45 | if (atomic_read(&tw->tw_refcnt) != 1) { | 59 | if (atomic_read(&tw->tw_refcnt) != 1) { |
@@ -47,7 +61,10 @@ static void __inet_twsk_kill(struct inet_timewait_sock *tw, | |||
47 | tw->tw_prot->name, tw, atomic_read(&tw->tw_refcnt)); | 61 | tw->tw_prot->name, tw, atomic_read(&tw->tw_refcnt)); |
48 | } | 62 | } |
49 | #endif | 63 | #endif |
50 | inet_twsk_put(tw); | 64 | while (refcnt) { |
65 | inet_twsk_put(tw); | ||
66 | refcnt--; | ||
67 | } | ||
51 | } | 68 | } |
52 | 69 | ||
53 | static noinline void inet_twsk_free(struct inet_timewait_sock *tw) | 70 | static noinline void inet_twsk_free(struct inet_timewait_sock *tw) |
@@ -92,6 +109,7 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, | |||
92 | tw->tw_tb = icsk->icsk_bind_hash; | 109 | tw->tw_tb = icsk->icsk_bind_hash; |
93 | WARN_ON(!icsk->icsk_bind_hash); | 110 | WARN_ON(!icsk->icsk_bind_hash); |
94 | inet_twsk_add_bind_node(tw, &tw->tw_tb->owners); | 111 | inet_twsk_add_bind_node(tw, &tw->tw_tb->owners); |
112 | atomic_inc(&tw->tw_refcnt); | ||
95 | spin_unlock(&bhead->lock); | 113 | spin_unlock(&bhead->lock); |
96 | 114 | ||
97 | spin_lock(lock); | 115 | spin_lock(lock); |