aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <dada1@cosmosbay.com>2007-02-08 17:16:46 -0500
committerDavid S. Miller <davem@davemloft.net>2007-02-08 17:16:46 -0500
commitdbca9b2750e3b1ee6f56a616160ccfc12e8b161f (patch)
treebf84c0acb5495fc95bc616d32b8af946f2e3bca9
parenteac3731bd04c7131478722a3c148b78774553116 (diff)
[NET]: change layout of ehash table
ehash table layout is currently this one : First half of this table is used by sockets not in TIME_WAIT state Second half of it is used by sockets in TIME_WAIT state. This is non optimal because of for a given hash or socket, the two chain heads are located in separate cache lines. Moreover the locks of the second half are never used. If instead of this halving, we use two list heads in inet_ehash_bucket instead of only one, we probably can avoid one cache miss, and reduce ram usage, particularly if sizeof(rwlock_t) is big (various CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_LOCK_ALLOC settings). So we still halves the table but we keep together related chains to speedup lookups and socket state change. In this patch I did not try to align struct inet_ehash_bucket, but a future patch could try to make this structure have a convenient size (a power of two or a multiple of L1_CACHE_SIZE). I guess rwlock will just vanish as soon as RCU is plugged into ehash :) , so maybe we dont need to scratch our heads to align the bucket... Note : In case struct inet_ehash_bucket is not a power of two, we could probably change alloc_large_system_hash() (in case it use __get_free_pages()) to free the unused space. It currently allocates a big zone, but the last quarter of it could be freed. Again, this should be a temporary 'problem'. Patch tested on ipv4 tcp only, but should be OK for IPV6 and DCCP. Signed-off-by: Eric Dumazet <dada1@cosmosbay.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/inet_hashtables.h10
-rw-r--r--net/dccp/proto.c4
-rw-r--r--net/ipv4/inet_diag.c2
-rw-r--r--net/ipv4/inet_hashtables.c2
-rw-r--r--net/ipv4/inet_timewait_sock.c4
-rw-r--r--net/ipv4/tcp.c7
-rw-r--r--net/ipv4/tcp_ipv4.c4
-rw-r--r--net/ipv6/inet6_hashtables.c4
8 files changed, 19 insertions, 18 deletions
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 34cc76e3ddb4..d27ee8c0da3f 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -34,12 +34,13 @@
34#include <asm/byteorder.h> 34#include <asm/byteorder.h>
35 35
36/* This is for all connections with a full identity, no wildcards. 36/* This is for all connections with a full identity, no wildcards.
37 * New scheme, half the table is for TIME_WAIT, the other half is 37 * One chain is dedicated to TIME_WAIT sockets.
38 * for the rest. I'll experiment with dynamic table growth later. 38 * I'll experiment with dynamic table growth later.
39 */ 39 */
40struct inet_ehash_bucket { 40struct inet_ehash_bucket {
41 rwlock_t lock; 41 rwlock_t lock;
42 struct hlist_head chain; 42 struct hlist_head chain;
43 struct hlist_head twchain;
43}; 44};
44 45
45/* There are a few simple rules, which allow for local port reuse by 46/* There are a few simple rules, which allow for local port reuse by
@@ -97,8 +98,7 @@ struct inet_hashinfo {
97 * 98 *
98 * TCP_ESTABLISHED <= sk->sk_state < TCP_CLOSE 99 * TCP_ESTABLISHED <= sk->sk_state < TCP_CLOSE
99 * 100 *
100 * First half of the table is for sockets not in TIME_WAIT, second half 101 * TIME_WAIT sockets use a separate chain (twchain).
101 * is for TIME_WAIT sockets only.
102 */ 102 */
103 struct inet_ehash_bucket *ehash; 103 struct inet_ehash_bucket *ehash;
104 104
@@ -369,7 +369,7 @@ static inline struct sock *
369 } 369 }
370 370
371 /* Must check for a TIME_WAIT'er before going to listener hash. */ 371 /* Must check for a TIME_WAIT'er before going to listener hash. */
372 sk_for_each(sk, node, &(head + hashinfo->ehash_size)->chain) { 372 sk_for_each(sk, node, &head->twchain) {
373 if (INET_TW_MATCH(sk, hash, acookie, saddr, daddr, ports, dif)) 373 if (INET_TW_MATCH(sk, hash, acookie, saddr, daddr, ports, dif))
374 goto hit; 374 goto hit;
375 } 375 }
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 63b3fa20e14b..48438565d70f 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -1024,7 +1024,6 @@ static int __init dccp_init(void)
1024 do { 1024 do {
1025 dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE / 1025 dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
1026 sizeof(struct inet_ehash_bucket); 1026 sizeof(struct inet_ehash_bucket);
1027 dccp_hashinfo.ehash_size >>= 1;
1028 while (dccp_hashinfo.ehash_size & 1027 while (dccp_hashinfo.ehash_size &
1029 (dccp_hashinfo.ehash_size - 1)) 1028 (dccp_hashinfo.ehash_size - 1))
1030 dccp_hashinfo.ehash_size--; 1029 dccp_hashinfo.ehash_size--;
@@ -1037,9 +1036,10 @@ static int __init dccp_init(void)
1037 goto out_free_bind_bucket_cachep; 1036 goto out_free_bind_bucket_cachep;
1038 } 1037 }
1039 1038
1040 for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) { 1039 for (i = 0; i < dccp_hashinfo.ehash_size; i++) {
1041 rwlock_init(&dccp_hashinfo.ehash[i].lock); 1040 rwlock_init(&dccp_hashinfo.ehash[i].lock);
1042 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain); 1041 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain);
1042 INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].twchain);
1043 } 1043 }
1044 1044
1045 bhash_order = ehash_order; 1045 bhash_order = ehash_order;
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 9cd53addb784..8aa7d51e6881 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -778,7 +778,7 @@ next_normal:
778 struct inet_timewait_sock *tw; 778 struct inet_timewait_sock *tw;
779 779
780 inet_twsk_for_each(tw, node, 780 inet_twsk_for_each(tw, node,
781 &hashinfo->ehash[i + hashinfo->ehash_size].chain) { 781 &head->twchain) {
782 782
783 if (num < s_num) 783 if (num < s_num)
784 goto next_dying; 784 goto next_dying;
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 8c79c8a4ea5c..150ace18dc75 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -212,7 +212,7 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
212 write_lock(&head->lock); 212 write_lock(&head->lock);
213 213
214 /* Check TIME-WAIT sockets first. */ 214 /* Check TIME-WAIT sockets first. */
215 sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) { 215 sk_for_each(sk2, node, &head->twchain) {
216 tw = inet_twsk(sk2); 216 tw = inet_twsk(sk2);
217 217
218 if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) { 218 if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index 9f414e35c488..a73cf93cee36 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -78,8 +78,8 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
78 if (__sk_del_node_init(sk)) 78 if (__sk_del_node_init(sk))
79 sock_prot_dec_use(sk->sk_prot); 79 sock_prot_dec_use(sk->sk_prot);
80 80
81 /* Step 3: Hash TW into TIMEWAIT half of established hash table. */ 81 /* Step 3: Hash TW into TIMEWAIT chain. */
82 inet_twsk_add_node(tw, &(ehead + hashinfo->ehash_size)->chain); 82 inet_twsk_add_node(tw, &ehead->twchain);
83 atomic_inc(&tw->tw_refcnt); 83 atomic_inc(&tw->tw_refcnt);
84 84
85 write_unlock(&ehead->lock); 85 write_unlock(&ehead->lock);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index b67e0dd743be..5bd43d7294fd 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2415,10 +2415,11 @@ void __init tcp_init(void)
2415 &tcp_hashinfo.ehash_size, 2415 &tcp_hashinfo.ehash_size,
2416 NULL, 2416 NULL,
2417 0); 2417 0);
2418 tcp_hashinfo.ehash_size = (1 << tcp_hashinfo.ehash_size) >> 1; 2418 tcp_hashinfo.ehash_size = 1 << tcp_hashinfo.ehash_size;
2419 for (i = 0; i < (tcp_hashinfo.ehash_size << 1); i++) { 2419 for (i = 0; i < tcp_hashinfo.ehash_size; i++) {
2420 rwlock_init(&tcp_hashinfo.ehash[i].lock); 2420 rwlock_init(&tcp_hashinfo.ehash[i].lock);
2421 INIT_HLIST_HEAD(&tcp_hashinfo.ehash[i].chain); 2421 INIT_HLIST_HEAD(&tcp_hashinfo.ehash[i].chain);
2422 INIT_HLIST_HEAD(&tcp_hashinfo.ehash[i].twchain);
2422 } 2423 }
2423 2424
2424 tcp_hashinfo.bhash = 2425 tcp_hashinfo.bhash =
@@ -2475,7 +2476,7 @@ void __init tcp_init(void)
2475 2476
2476 printk(KERN_INFO "TCP: Hash tables configured " 2477 printk(KERN_INFO "TCP: Hash tables configured "
2477 "(established %d bind %d)\n", 2478 "(established %d bind %d)\n",
2478 tcp_hashinfo.ehash_size << 1, tcp_hashinfo.bhash_size); 2479 tcp_hashinfo.ehash_size, tcp_hashinfo.bhash_size);
2479 2480
2480 tcp_register_congestion_control(&tcp_reno); 2481 tcp_register_congestion_control(&tcp_reno);
2481} 2482}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 383e4b52dbde..f51d6404c61c 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2051,7 +2051,7 @@ static void *established_get_first(struct seq_file *seq)
2051 } 2051 }
2052 st->state = TCP_SEQ_STATE_TIME_WAIT; 2052 st->state = TCP_SEQ_STATE_TIME_WAIT;
2053 inet_twsk_for_each(tw, node, 2053 inet_twsk_for_each(tw, node,
2054 &tcp_hashinfo.ehash[st->bucket + tcp_hashinfo.ehash_size].chain) { 2054 &tcp_hashinfo.ehash[st->bucket].twchain) {
2055 if (tw->tw_family != st->family) { 2055 if (tw->tw_family != st->family) {
2056 continue; 2056 continue;
2057 } 2057 }
@@ -2107,7 +2107,7 @@ get_tw:
2107 } 2107 }
2108 2108
2109 st->state = TCP_SEQ_STATE_TIME_WAIT; 2109 st->state = TCP_SEQ_STATE_TIME_WAIT;
2110 tw = tw_head(&tcp_hashinfo.ehash[st->bucket + tcp_hashinfo.ehash_size].chain); 2110 tw = tw_head(&tcp_hashinfo.ehash[st->bucket].twchain);
2111 goto get_tw; 2111 goto get_tw;
2112found: 2112found:
2113 cur = sk; 2113 cur = sk;
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index b7e5bae0e347..e61116949bee 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -79,7 +79,7 @@ struct sock *__inet6_lookup_established(struct inet_hashinfo *hashinfo,
79 goto hit; /* You sunk my battleship! */ 79 goto hit; /* You sunk my battleship! */
80 } 80 }
81 /* Must check for a TIME_WAIT'er before going to listener hash. */ 81 /* Must check for a TIME_WAIT'er before going to listener hash. */
82 sk_for_each(sk, node, &(head + hashinfo->ehash_size)->chain) { 82 sk_for_each(sk, node, &head->twchain) {
83 const struct inet_timewait_sock *tw = inet_twsk(sk); 83 const struct inet_timewait_sock *tw = inet_twsk(sk);
84 84
85 if(*((__portpair *)&(tw->tw_dport)) == ports && 85 if(*((__portpair *)&(tw->tw_dport)) == ports &&
@@ -183,7 +183,7 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
183 write_lock(&head->lock); 183 write_lock(&head->lock);
184 184
185 /* Check TIME-WAIT sockets first. */ 185 /* Check TIME-WAIT sockets first. */
186 sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) { 186 sk_for_each(sk2, node, &head->twchain) {
187 const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2); 187 const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
188 188
189 tw = inet_twsk(sk2); 189 tw = inet_twsk(sk2);