diff options
| author | dpward <david.ward@ll.mit.edu> | 2011-09-05 12:47:24 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2011-09-16 17:47:28 -0400 |
| commit | aa1c366e4febc7f5c2b84958a2dd7cd70e28f9d0 (patch) | |
| tree | 336ecab3185913c78fb3ac81779a373e4d4ae5af | |
| parent | 728871bc05afc8ff310b17dba3e57a2472792b13 (diff) | |
net: Handle different key sizes between address families in flow cache
With the conversion of struct flowi to a union of AF-specific structs, some
operations on the flow cache need to account for the exact size of the key.
Signed-off-by: David Ward <david.ward@ll.mit.edu>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/net/flow.h | 19 | ||||
| -rw-r--r-- | net/core/flow.c | 31 |
2 files changed, 36 insertions, 14 deletions
diff --git a/include/net/flow.h b/include/net/flow.h index 2ec377d9ab9f..a09447749e2d 100644 --- a/include/net/flow.h +++ b/include/net/flow.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #ifndef _NET_FLOW_H | 7 | #ifndef _NET_FLOW_H |
| 8 | #define _NET_FLOW_H | 8 | #define _NET_FLOW_H |
| 9 | 9 | ||
| 10 | #include <linux/socket.h> | ||
| 10 | #include <linux/in6.h> | 11 | #include <linux/in6.h> |
| 11 | #include <linux/atomic.h> | 12 | #include <linux/atomic.h> |
| 12 | 13 | ||
| @@ -161,6 +162,24 @@ static inline struct flowi *flowidn_to_flowi(struct flowidn *fldn) | |||
| 161 | return container_of(fldn, struct flowi, u.dn); | 162 | return container_of(fldn, struct flowi, u.dn); |
| 162 | } | 163 | } |
| 163 | 164 | ||
| 165 | typedef unsigned long flow_compare_t; | ||
| 166 | |||
| 167 | static inline size_t flow_key_size(u16 family) | ||
| 168 | { | ||
| 169 | switch (family) { | ||
| 170 | case AF_INET: | ||
| 171 | BUILD_BUG_ON(sizeof(struct flowi4) % sizeof(flow_compare_t)); | ||
| 172 | return sizeof(struct flowi4) / sizeof(flow_compare_t); | ||
| 173 | case AF_INET6: | ||
| 174 | BUILD_BUG_ON(sizeof(struct flowi6) % sizeof(flow_compare_t)); | ||
| 175 | return sizeof(struct flowi6) / sizeof(flow_compare_t); | ||
| 176 | case AF_DECnet: | ||
| 177 | BUILD_BUG_ON(sizeof(struct flowidn) % sizeof(flow_compare_t)); | ||
| 178 | return sizeof(struct flowidn) / sizeof(flow_compare_t); | ||
| 179 | } | ||
| 180 | return 0; | ||
| 181 | } | ||
| 182 | |||
| 164 | #define FLOW_DIR_IN 0 | 183 | #define FLOW_DIR_IN 0 |
| 165 | #define FLOW_DIR_OUT 1 | 184 | #define FLOW_DIR_OUT 1 |
| 166 | #define FLOW_DIR_FWD 2 | 185 | #define FLOW_DIR_FWD 2 |
diff --git a/net/core/flow.c b/net/core/flow.c index 47b6d26c2afb..555a456efb07 100644 --- a/net/core/flow.c +++ b/net/core/flow.c | |||
| @@ -173,29 +173,26 @@ static void flow_new_hash_rnd(struct flow_cache *fc, | |||
| 173 | 173 | ||
| 174 | static u32 flow_hash_code(struct flow_cache *fc, | 174 | static u32 flow_hash_code(struct flow_cache *fc, |
| 175 | struct flow_cache_percpu *fcp, | 175 | struct flow_cache_percpu *fcp, |
| 176 | const struct flowi *key) | 176 | const struct flowi *key, |
| 177 | size_t keysize) | ||
| 177 | { | 178 | { |
| 178 | const u32 *k = (const u32 *) key; | 179 | const u32 *k = (const u32 *) key; |
| 180 | const u32 length = keysize * sizeof(flow_compare_t) / sizeof(u32); | ||
| 179 | 181 | ||
| 180 | return jhash2(k, (sizeof(*key) / sizeof(u32)), fcp->hash_rnd) | 182 | return jhash2(k, length, fcp->hash_rnd) |
| 181 | & (flow_cache_hash_size(fc) - 1); | 183 | & (flow_cache_hash_size(fc) - 1); |
| 182 | } | 184 | } |
| 183 | 185 | ||
| 184 | typedef unsigned long flow_compare_t; | ||
| 185 | |||
| 186 | /* I hear what you're saying, use memcmp. But memcmp cannot make | 186 | /* I hear what you're saying, use memcmp. But memcmp cannot make |
| 187 | * important assumptions that we can here, such as alignment and | 187 | * important assumptions that we can here, such as alignment. |
| 188 | * constant size. | ||
| 189 | */ | 188 | */ |
| 190 | static int flow_key_compare(const struct flowi *key1, const struct flowi *key2) | 189 | static int flow_key_compare(const struct flowi *key1, const struct flowi *key2, |
| 190 | size_t keysize) | ||
| 191 | { | 191 | { |
| 192 | const flow_compare_t *k1, *k1_lim, *k2; | 192 | const flow_compare_t *k1, *k1_lim, *k2; |
| 193 | const int n_elem = sizeof(struct flowi) / sizeof(flow_compare_t); | ||
| 194 | |||
| 195 | BUILD_BUG_ON(sizeof(struct flowi) % sizeof(flow_compare_t)); | ||
| 196 | 193 | ||
| 197 | k1 = (const flow_compare_t *) key1; | 194 | k1 = (const flow_compare_t *) key1; |
| 198 | k1_lim = k1 + n_elem; | 195 | k1_lim = k1 + keysize; |
| 199 | 196 | ||
| 200 | k2 = (const flow_compare_t *) key2; | 197 | k2 = (const flow_compare_t *) key2; |
| 201 | 198 | ||
| @@ -216,6 +213,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, | |||
| 216 | struct flow_cache_entry *fle, *tfle; | 213 | struct flow_cache_entry *fle, *tfle; |
| 217 | struct hlist_node *entry; | 214 | struct hlist_node *entry; |
| 218 | struct flow_cache_object *flo; | 215 | struct flow_cache_object *flo; |
| 216 | size_t keysize; | ||
| 219 | unsigned int hash; | 217 | unsigned int hash; |
| 220 | 218 | ||
| 221 | local_bh_disable(); | 219 | local_bh_disable(); |
| @@ -223,6 +221,11 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, | |||
| 223 | 221 | ||
| 224 | fle = NULL; | 222 | fle = NULL; |
| 225 | flo = NULL; | 223 | flo = NULL; |
| 224 | |||
| 225 | keysize = flow_key_size(family); | ||
| 226 | if (!keysize) | ||
| 227 | goto nocache; | ||
| 228 | |||
| 226 | /* Packet really early in init? Making flow_cache_init a | 229 | /* Packet really early in init? Making flow_cache_init a |
| 227 | * pre-smp initcall would solve this. --RR */ | 230 | * pre-smp initcall would solve this. --RR */ |
| 228 | if (!fcp->hash_table) | 231 | if (!fcp->hash_table) |
| @@ -231,12 +234,12 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, | |||
| 231 | if (fcp->hash_rnd_recalc) | 234 | if (fcp->hash_rnd_recalc) |
| 232 | flow_new_hash_rnd(fc, fcp); | 235 | flow_new_hash_rnd(fc, fcp); |
| 233 | 236 | ||
| 234 | hash = flow_hash_code(fc, fcp, key); | 237 | hash = flow_hash_code(fc, fcp, key, keysize); |
| 235 | hlist_for_each_entry(tfle, entry, &fcp->hash_table[hash], u.hlist) { | 238 | hlist_for_each_entry(tfle, entry, &fcp->hash_table[hash], u.hlist) { |
| 236 | if (tfle->net == net && | 239 | if (tfle->net == net && |
| 237 | tfle->family == family && | 240 | tfle->family == family && |
| 238 | tfle->dir == dir && | 241 | tfle->dir == dir && |
| 239 | flow_key_compare(key, &tfle->key) == 0) { | 242 | flow_key_compare(key, &tfle->key, keysize) == 0) { |
| 240 | fle = tfle; | 243 | fle = tfle; |
| 241 | break; | 244 | break; |
| 242 | } | 245 | } |
| @@ -251,7 +254,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, | |||
| 251 | fle->net = net; | 254 | fle->net = net; |
| 252 | fle->family = family; | 255 | fle->family = family; |
| 253 | fle->dir = dir; | 256 | fle->dir = dir; |
| 254 | memcpy(&fle->key, key, sizeof(*key)); | 257 | memcpy(&fle->key, key, keysize * sizeof(flow_compare_t)); |
| 255 | fle->object = NULL; | 258 | fle->object = NULL; |
| 256 | hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]); | 259 | hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]); |
| 257 | fcp->hash_count++; | 260 | fcp->hash_count++; |
