aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/fib_hash.c
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-10-14 16:53:04 -0400
committerDavid S. Miller <davem@davemloft.net>2010-10-17 16:53:15 -0400
commit9bef83edfba72ba58b42c14fb046da2199574bc0 (patch)
treece61ab76bce8de53b76a83ec3cca63a87b7286b7 /net/ipv4/fib_hash.c
parent7fc4463309faa087f5e41569a987d43b1d71b982 (diff)
fib_hash: embed initial hash table in fn_zone
While looking for false sharing problems, I noticed sizeof(struct fn_zone) was small (28 bytes) and possibly sharing a cache line with an often written kernel structure. Most of the time, fn_zone uses its initial hash table of 16 slots. We can avoid the false sharing problem by embedding this initial hash table in fn_zone itself, so that sizeof(fn_zone) > L1_CACHE_BYTES We did a similar optimization in commit a6501e080c (Reduce memory needs and speedup lookups) Add a fz_revorder field to speedup fn_hash() a bit. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/fib_hash.c')
-rw-r--r--net/ipv4/fib_hash.c52
1 files changed, 23 insertions, 29 deletions
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c
index 83cca68e259c..10001aa40692 100644
--- a/net/ipv4/fib_hash.c
+++ b/net/ipv4/fib_hash.c
@@ -54,23 +54,23 @@ struct fib_node {
54 struct fib_alias fn_embedded_alias; 54 struct fib_alias fn_embedded_alias;
55}; 55};
56 56
57#define EMBEDDED_HASH_SIZE (L1_CACHE_BYTES / sizeof(struct hlist_head))
58
57struct fn_zone { 59struct fn_zone {
58 struct fn_zone *fz_next; /* Next not empty zone */ 60 struct fn_zone *fz_next; /* Next not empty zone */
59 struct hlist_head *fz_hash; /* Hash table pointer */ 61 struct hlist_head *fz_hash; /* Hash table pointer */
60 int fz_nent; /* Number of entries */
61
62 int fz_divisor; /* Hash divisor */
63 u32 fz_hashmask; /* (fz_divisor - 1) */ 62 u32 fz_hashmask; /* (fz_divisor - 1) */
64#define FZ_HASHMASK(fz) ((fz)->fz_hashmask)
65 63
66 int fz_order; /* Zone order */ 64 u8 fz_order; /* Zone order (0..32) */
67 __be32 fz_mask; 65 u8 fz_revorder; /* 32 - fz_order */
66 __be32 fz_mask; /* inet_make_mask(order) */
68#define FZ_MASK(fz) ((fz)->fz_mask) 67#define FZ_MASK(fz) ((fz)->fz_mask)
69};
70 68
71/* NOTE. On fast computers evaluation of fz_hashmask and fz_mask 69 struct hlist_head fz_embedded_hash[EMBEDDED_HASH_SIZE];
72 * can be cheaper than memory lookup, so that FZ_* macros are used. 70
73 */ 71 int fz_nent; /* Number of entries */
72 int fz_divisor; /* Hash size (mask+1) */
73};
74 74
75struct fn_hash { 75struct fn_hash {
76 struct fn_zone *fn_zones[33]; 76 struct fn_zone *fn_zones[33];
@@ -79,11 +79,11 @@ struct fn_hash {
79 79
80static inline u32 fn_hash(__be32 key, struct fn_zone *fz) 80static inline u32 fn_hash(__be32 key, struct fn_zone *fz)
81{ 81{
82 u32 h = ntohl(key)>>(32 - fz->fz_order); 82 u32 h = ntohl(key) >> fz->fz_revorder;
83 h ^= (h>>20); 83 h ^= (h>>20);
84 h ^= (h>>10); 84 h ^= (h>>10);
85 h ^= (h>>5); 85 h ^= (h>>5);
86 h &= FZ_HASHMASK(fz); 86 h &= fz->fz_hashmask;
87 return h; 87 return h;
88} 88}
89 89
@@ -147,14 +147,14 @@ static void fn_rehash_zone(struct fn_zone *fz)
147 int old_divisor, new_divisor; 147 int old_divisor, new_divisor;
148 u32 new_hashmask; 148 u32 new_hashmask;
149 149
150 old_divisor = fz->fz_divisor; 150 new_divisor = old_divisor = fz->fz_divisor;
151 151
152 switch (old_divisor) { 152 switch (old_divisor) {
153 case 16: 153 case EMBEDDED_HASH_SIZE:
154 new_divisor = 256; 154 new_divisor *= EMBEDDED_HASH_SIZE;
155 break; 155 break;
156 case 256: 156 case EMBEDDED_HASH_SIZE*EMBEDDED_HASH_SIZE:
157 new_divisor = 1024; 157 new_divisor *= (EMBEDDED_HASH_SIZE/2);
158 break; 158 break;
159 default: 159 default:
160 if ((old_divisor << 1) > FZ_MAX_DIVISOR) { 160 if ((old_divisor << 1) > FZ_MAX_DIVISOR) {
@@ -184,7 +184,8 @@ static void fn_rehash_zone(struct fn_zone *fz)
184 fib_hash_genid++; 184 fib_hash_genid++;
185 write_unlock_bh(&fib_hash_lock); 185 write_unlock_bh(&fib_hash_lock);
186 186
187 fz_hash_free(old_ht, old_divisor); 187 if (old_ht != fz->fz_embedded_hash)
188 fz_hash_free(old_ht, old_divisor);
188 } 189 }
189} 190}
190 191
@@ -210,18 +211,11 @@ fn_new_zone(struct fn_hash *table, int z)
210 if (!fz) 211 if (!fz)
211 return NULL; 212 return NULL;
212 213
213 if (z) { 214 fz->fz_divisor = z ? EMBEDDED_HASH_SIZE : 1;
214 fz->fz_divisor = 16; 215 fz->fz_hashmask = fz->fz_divisor - 1;
215 } else { 216 fz->fz_hash = fz->fz_embedded_hash;
216 fz->fz_divisor = 1;
217 }
218 fz->fz_hashmask = (fz->fz_divisor - 1);
219 fz->fz_hash = fz_hash_alloc(fz->fz_divisor);
220 if (!fz->fz_hash) {
221 kfree(fz);
222 return NULL;
223 }
224 fz->fz_order = z; 217 fz->fz_order = z;
218 fz->fz_revorder = 32 - z;
225 fz->fz_mask = inet_make_mask(z); 219 fz->fz_mask = inet_make_mask(z);
226 220
227 /* Find the first not empty zone with more specific mask */ 221 /* Find the first not empty zone with more specific mask */