diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-10-14 16:53:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-10-17 16:53:15 -0400 |
commit | 9bef83edfba72ba58b42c14fb046da2199574bc0 (patch) | |
tree | ce61ab76bce8de53b76a83ec3cca63a87b7286b7 /net/ipv4/fib_hash.c | |
parent | 7fc4463309faa087f5e41569a987d43b1d71b982 (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.c | 52 |
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 | |||
57 | struct fn_zone { | 59 | struct 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 | ||
75 | struct fn_hash { | 75 | struct 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 | ||
80 | static inline u32 fn_hash(__be32 key, struct fn_zone *fz) | 80 | static 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 */ |