Commit aa1c366e authored by dpward's avatar dpward Committed by David S. Miller

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: default avatarDavid Ward <david.ward@ll.mit.edu>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 728871bc
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#ifndef _NET_FLOW_H #ifndef _NET_FLOW_H
#define _NET_FLOW_H #define _NET_FLOW_H
#include <linux/socket.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <linux/atomic.h> #include <linux/atomic.h>
...@@ -161,6 +162,24 @@ static inline struct flowi *flowidn_to_flowi(struct flowidn *fldn) ...@@ -161,6 +162,24 @@ static inline struct flowi *flowidn_to_flowi(struct flowidn *fldn)
return container_of(fldn, struct flowi, u.dn); return container_of(fldn, struct flowi, u.dn);
} }
typedef unsigned long flow_compare_t;
static inline size_t flow_key_size(u16 family)
{
switch (family) {
case AF_INET:
BUILD_BUG_ON(sizeof(struct flowi4) % sizeof(flow_compare_t));
return sizeof(struct flowi4) / sizeof(flow_compare_t);
case AF_INET6:
BUILD_BUG_ON(sizeof(struct flowi6) % sizeof(flow_compare_t));
return sizeof(struct flowi6) / sizeof(flow_compare_t);
case AF_DECnet:
BUILD_BUG_ON(sizeof(struct flowidn) % sizeof(flow_compare_t));
return sizeof(struct flowidn) / sizeof(flow_compare_t);
}
return 0;
}
#define FLOW_DIR_IN 0 #define FLOW_DIR_IN 0
#define FLOW_DIR_OUT 1 #define FLOW_DIR_OUT 1
#define FLOW_DIR_FWD 2 #define FLOW_DIR_FWD 2
......
...@@ -173,29 +173,26 @@ static void flow_new_hash_rnd(struct flow_cache *fc, ...@@ -173,29 +173,26 @@ static void flow_new_hash_rnd(struct flow_cache *fc,
static u32 flow_hash_code(struct flow_cache *fc, static u32 flow_hash_code(struct flow_cache *fc,
struct flow_cache_percpu *fcp, struct flow_cache_percpu *fcp,
const struct flowi *key) const struct flowi *key,
size_t keysize)
{ {
const u32 *k = (const u32 *) key; const u32 *k = (const u32 *) key;
const u32 length = keysize * sizeof(flow_compare_t) / sizeof(u32);
return jhash2(k, (sizeof(*key) / sizeof(u32)), fcp->hash_rnd) return jhash2(k, length, fcp->hash_rnd)
& (flow_cache_hash_size(fc) - 1); & (flow_cache_hash_size(fc) - 1);
} }
typedef unsigned long flow_compare_t;
/* I hear what you're saying, use memcmp. But memcmp cannot make /* I hear what you're saying, use memcmp. But memcmp cannot make
* important assumptions that we can here, such as alignment and * important assumptions that we can here, such as alignment.
* constant size.
*/ */
static int flow_key_compare(const struct flowi *key1, const struct flowi *key2) static int flow_key_compare(const struct flowi *key1, const struct flowi *key2,
size_t keysize)
{ {
const flow_compare_t *k1, *k1_lim, *k2; const flow_compare_t *k1, *k1_lim, *k2;
const int n_elem = sizeof(struct flowi) / sizeof(flow_compare_t);
BUILD_BUG_ON(sizeof(struct flowi) % sizeof(flow_compare_t));
k1 = (const flow_compare_t *) key1; k1 = (const flow_compare_t *) key1;
k1_lim = k1 + n_elem; k1_lim = k1 + keysize;
k2 = (const flow_compare_t *) key2; k2 = (const flow_compare_t *) key2;
...@@ -216,6 +213,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, ...@@ -216,6 +213,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir,
struct flow_cache_entry *fle, *tfle; struct flow_cache_entry *fle, *tfle;
struct hlist_node *entry; struct hlist_node *entry;
struct flow_cache_object *flo; struct flow_cache_object *flo;
size_t keysize;
unsigned int hash; unsigned int hash;
local_bh_disable(); local_bh_disable();
...@@ -223,6 +221,11 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, ...@@ -223,6 +221,11 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir,
fle = NULL; fle = NULL;
flo = NULL; flo = NULL;
keysize = flow_key_size(family);
if (!keysize)
goto nocache;
/* Packet really early in init? Making flow_cache_init a /* Packet really early in init? Making flow_cache_init a
* pre-smp initcall would solve this. --RR */ * pre-smp initcall would solve this. --RR */
if (!fcp->hash_table) if (!fcp->hash_table)
...@@ -231,12 +234,12 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, ...@@ -231,12 +234,12 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir,
if (fcp->hash_rnd_recalc) if (fcp->hash_rnd_recalc)
flow_new_hash_rnd(fc, fcp); flow_new_hash_rnd(fc, fcp);
hash = flow_hash_code(fc, fcp, key); hash = flow_hash_code(fc, fcp, key, keysize);
hlist_for_each_entry(tfle, entry, &fcp->hash_table[hash], u.hlist) { hlist_for_each_entry(tfle, entry, &fcp->hash_table[hash], u.hlist) {
if (tfle->net == net && if (tfle->net == net &&
tfle->family == family && tfle->family == family &&
tfle->dir == dir && tfle->dir == dir &&
flow_key_compare(key, &tfle->key) == 0) { flow_key_compare(key, &tfle->key, keysize) == 0) {
fle = tfle; fle = tfle;
break; break;
} }
...@@ -251,7 +254,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, ...@@ -251,7 +254,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir,
fle->net = net; fle->net = net;
fle->family = family; fle->family = family;
fle->dir = dir; fle->dir = dir;
memcpy(&fle->key, key, sizeof(*key)); memcpy(&fle->key, key, keysize * sizeof(flow_compare_t));
fle->object = NULL; fle->object = NULL;
hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]); hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]);
fcp->hash_count++; fcp->hash_count++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment