diff --git a/include/net/tcp.h b/include/net/tcp.h index a7be6928b05e9eb14da53187a3fc87400c92897c..69630979810eed1d916be8971b7f27d01f7d10cb 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -84,16 +84,27 @@ struct tcp_ehash_bucket { struct tcp_bind_bucket { unsigned short port; signed short fastreuse; - struct tcp_bind_bucket *next; - struct tcp_bind_bucket **pprev; + struct hlist_node node; struct hlist_head owners; }; +#define tb_for_each(tb, node, head) hlist_for_each_entry(tb, node, head, node) + struct tcp_bind_hashbucket { spinlock_t lock; - struct tcp_bind_bucket *chain; + struct hlist_head chain; }; +static inline struct tcp_bind_bucket *__tb_head(struct tcp_bind_hashbucket *head) +{ + return hlist_entry(head->chain.first, struct tcp_bind_bucket, node); +} + +static inline struct tcp_bind_bucket *tb_head(struct tcp_bind_hashbucket *head) +{ + return hlist_empty(&head->chain) ? NULL : __tb_head(head); +} + extern struct tcp_hashinfo { /* This is for sockets with full identity only. Sockets here will * always be without wildcards and will have the following invariant: diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c1fbbff7546a7e6e812b4ca61839d5807d4b7564..619c4ce05f138b9c3e225619fc5c8292cffe979d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2641,7 +2641,7 @@ void __init tcp_init(void) panic("Failed to allocate TCP bind hash table\n"); for (i = 0; i < tcp_bhash_size; i++) { tcp_bhash[i].lock = SPIN_LOCK_UNLOCKED; - tcp_bhash[i].chain = NULL; + INIT_HLIST_HEAD(&tcp_bhash[i].chain); } /* Try to be a bit smarter and adjust defaults depending diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 76ec332e3278207c3dccc576371d0dfeb4e49f40..ab2a3977c8477d64c049674a12ad3e3e19b8704f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -134,10 +134,7 @@ struct tcp_bind_bucket *tcp_bucket_create(struct tcp_bind_hashbucket *head, tb->port = snum; tb->fastreuse = 0; INIT_HLIST_HEAD(&tb->owners); - if ((tb->next = head->chain) != NULL) - tb->next->pprev = &tb->next; - head->chain = tb; - tb->pprev = &head->chain; + hlist_add_head(&tb->node, &head->chain); } return tb; } @@ -146,9 +143,7 @@ struct tcp_bind_bucket *tcp_bucket_create(struct tcp_bind_hashbucket *head, void tcp_bucket_destroy(struct tcp_bind_bucket *tb) { if (hlist_empty(&tb->owners)) { - if (tb->next) - tb->next->pprev = tb->pprev; - *(tb->pprev) = tb->next; + __hlist_del(&tb->node); kmem_cache_free(tcp_bucket_cachep, tb); } } @@ -211,6 +206,7 @@ static inline int tcp_bind_conflict(struct sock *sk, struct tcp_bind_bucket *tb) static int tcp_v4_get_port(struct sock *sk, unsigned short snum) { struct tcp_bind_hashbucket *head; + struct hlist_node *node; struct tcp_bind_bucket *tb; int ret; @@ -229,7 +225,7 @@ static int tcp_v4_get_port(struct sock *sk, unsigned short snum) rover = low; head = &tcp_bhash[tcp_bhashfn(rover)]; spin_lock(&head->lock); - for (tb = head->chain; tb; tb = tb->next) + tb_for_each(tb, node, &head->chain) if (tb->port == rover) goto next; break; @@ -248,15 +244,17 @@ static int tcp_v4_get_port(struct sock *sk, unsigned short snum) * non-NULL and we hold it's mutex. */ snum = rover; - tb = NULL; } else { head = &tcp_bhash[tcp_bhashfn(snum)]; spin_lock(&head->lock); - for (tb = head->chain; tb; tb = tb->next) + tb_for_each(tb, node, &head->chain) if (tb->port == snum) - break; + goto tb_found; } - if (tb && !hlist_empty(&tb->owners)) { + tb = NULL; + goto tb_not_found; +tb_found: + if (!hlist_empty(&tb->owners)) { if (sk->sk_reuse > 1) goto success; if (tb->fastreuse > 0 && @@ -268,6 +266,7 @@ static int tcp_v4_get_port(struct sock *sk, unsigned short snum) goto fail_unlock; } } +tb_not_found: ret = 1; if (!tb && (tb = tcp_bucket_create(head, snum)) == NULL) goto fail_unlock; @@ -646,6 +645,7 @@ static int tcp_v4_hash_connect(struct sock *sk) int low = sysctl_local_port_range[0]; int high = sysctl_local_port_range[1]; int remaining = (high - low) + 1; + struct hlist_node *node; struct tcp_tw_bucket *tw = NULL; local_bh_disable(); @@ -677,7 +677,7 @@ static int tcp_v4_hash_connect(struct sock *sk) * because the established check is already * unique enough. */ - for (tb = head->chain; tb; tb = tb->next) { + tb_for_each(tb, node, &head->chain) { if (tb->port == rover) { BUG_TRAP(!hlist_empty(&tb->owners)); if (tb->fastreuse >= 0) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c8261dd92731252f30bfed37154ec30faf140289..71ca875f7e71f8c928b865245684de5e2b5baa40 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -157,6 +157,7 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) { struct tcp_bind_hashbucket *head; struct tcp_bind_bucket *tb; + struct hlist_node *node; int ret; local_bh_disable(); @@ -173,7 +174,7 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) rover = low; head = &tcp_bhash[tcp_bhashfn(rover)]; spin_lock(&head->lock); - for (tb = head->chain; tb; tb = tb->next) + tb_for_each(tb, node, &head->chain) if (tb->port == rover) goto next; break; @@ -190,14 +191,16 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) /* OK, here is the one we will use. */ snum = rover; - tb = NULL; } else { head = &tcp_bhash[tcp_bhashfn(snum)]; spin_lock(&head->lock); - for (tb = head->chain; tb != NULL; tb = tb->next) + tb_for_each(tb, node, &head->chain) if (tb->port == snum) - break; + goto tb_found; } + tb = NULL; + goto tb_not_found; +tb_found: if (tb && !hlist_empty(&tb->owners)) { if (tb->fastreuse > 0 && sk->sk_reuse && sk->sk_state != TCP_LISTEN) { @@ -208,9 +211,9 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) goto fail_unlock; } } +tb_not_found: ret = 1; - if (tb == NULL && - (tb = tcp_bucket_create(head, snum)) == NULL) + if (!tb && (tb = tcp_bucket_create(head, snum)) == NULL) goto fail_unlock; if (hlist_empty(&tb->owners)) { if (sk->sk_reuse && sk->sk_state != TCP_LISTEN) @@ -550,7 +553,7 @@ static int tcp_v6_hash_connect(struct sock *sk) } head = &tcp_bhash[tcp_bhashfn(inet_sk(sk)->num)]; - tb = head->chain; + tb = tb_head(head); spin_lock_bh(&head->lock);