aboutsummaryrefslogtreecommitdiffstats
path: root/net/openvswitch
diff options
context:
space:
mode:
authorAndy Zhou <azhou@nicira.com>2013-08-27 16:02:21 -0400
committerJesse Gross <jesse@nicira.com>2013-08-27 16:13:09 -0400
commit5828cd9a68873df1340b420371c02c47647878fb (patch)
treef32f417eecf4eab1f7a780b0191f4a1b0b0a9f1d /net/openvswitch
parent02237373b1c61a09a4db329545e39cffc48910d5 (diff)
openvswitch: optimize flow compare and mask functions
Make sure the sw_flow_key structure and valid mask boundaries are always machine word aligned. Optimize the flow compare and mask operations using machine word size operations. This patch improves throughput on average by 15% when CPU is the bottleneck of forwarding packets. This patch is inspired by ideas and code from a patch submitted by Peter Klausler titled "replace memcmp() with specialized comparator". However, The original patch only optimizes for architectures support unaligned machine word access. This patch optimizes for all architectures. Signed-off-by: Andy Zhou <azhou@nicira.com> Signed-off-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'net/openvswitch')
-rw-r--r--net/openvswitch/flow.c64
-rw-r--r--net/openvswitch/flow.h19
2 files changed, 44 insertions, 39 deletions
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 80bcb96467a5..ad1aeeb4f373 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -54,8 +54,8 @@ static void update_range__(struct sw_flow_match *match,
54 size_t offset, size_t size, bool is_mask) 54 size_t offset, size_t size, bool is_mask)
55{ 55{
56 struct sw_flow_key_range *range = NULL; 56 struct sw_flow_key_range *range = NULL;
57 size_t start = offset; 57 size_t start = rounddown(offset, sizeof(long));
58 size_t end = offset + size; 58 size_t end = roundup(offset + size, sizeof(long));
59 59
60 if (!is_mask) 60 if (!is_mask)
61 range = &match->range; 61 range = &match->range;
@@ -102,6 +102,11 @@ static void update_range__(struct sw_flow_match *match,
102 } \ 102 } \
103 } while (0) 103 } while (0)
104 104
105static u16 range_n_bytes(const struct sw_flow_key_range *range)
106{
107 return range->end - range->start;
108}
109
105void ovs_match_init(struct sw_flow_match *match, 110void ovs_match_init(struct sw_flow_match *match,
106 struct sw_flow_key *key, 111 struct sw_flow_key *key,
107 struct sw_flow_mask *mask) 112 struct sw_flow_mask *mask)
@@ -370,16 +375,17 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
370void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src, 375void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src,
371 const struct sw_flow_mask *mask) 376 const struct sw_flow_mask *mask)
372{ 377{
373 u8 *m = (u8 *)&mask->key + mask->range.start; 378 const long *m = (long *)((u8 *)&mask->key + mask->range.start);
374 u8 *s = (u8 *)src + mask->range.start; 379 const long *s = (long *)((u8 *)src + mask->range.start);
375 u8 *d = (u8 *)dst + mask->range.start; 380 long *d = (long *)((u8 *)dst + mask->range.start);
376 int i; 381 int i;
377 382
378 memset(dst, 0, sizeof(*dst)); 383 /* The memory outside of the 'mask->range' are not set since
379 for (i = 0; i < ovs_sw_flow_mask_size_roundup(mask); i++) { 384 * further operations on 'dst' only uses contents within
380 *d = *s & *m; 385 * 'mask->range'.
381 d++, s++, m++; 386 */
382 } 387 for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long))
388 *d++ = *s++ & *m++;
383} 389}
384 390
385#define TCP_FLAGS_OFFSET 13 391#define TCP_FLAGS_OFFSET 13
@@ -1000,8 +1006,13 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
1000static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, 1006static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start,
1001 int key_end) 1007 int key_end)
1002{ 1008{
1003 return jhash2((u32 *)((u8 *)key + key_start), 1009 u32 *hash_key = (u32 *)((u8 *)key + key_start);
1004 DIV_ROUND_UP(key_end - key_start, sizeof(u32)), 0); 1010 int hash_u32s = (key_end - key_start) >> 2;
1011
1012 /* Make sure number of hash bytes are multiple of u32. */
1013 BUILD_BUG_ON(sizeof(long) % sizeof(u32));
1014
1015 return jhash2(hash_key, hash_u32s, 0);
1005} 1016}
1006 1017
1007static int flow_key_start(const struct sw_flow_key *key) 1018static int flow_key_start(const struct sw_flow_key *key)
@@ -1009,17 +1020,25 @@ static int flow_key_start(const struct sw_flow_key *key)
1009 if (key->tun_key.ipv4_dst) 1020 if (key->tun_key.ipv4_dst)
1010 return 0; 1021 return 0;
1011 else 1022 else
1012 return offsetof(struct sw_flow_key, phy); 1023 return rounddown(offsetof(struct sw_flow_key, phy),
1024 sizeof(long));
1013} 1025}
1014 1026
1015static bool __cmp_key(const struct sw_flow_key *key1, 1027static bool __cmp_key(const struct sw_flow_key *key1,
1016 const struct sw_flow_key *key2, int key_start, int key_end) 1028 const struct sw_flow_key *key2, int key_start, int key_end)
1017{ 1029{
1018 return !memcmp((u8 *)key1 + key_start, 1030 const long *cp1 = (long *)((u8 *)key1 + key_start);
1019 (u8 *)key2 + key_start, (key_end - key_start)); 1031 const long *cp2 = (long *)((u8 *)key2 + key_start);
1032 long diffs = 0;
1033 int i;
1034
1035 for (i = key_start; i < key_end; i += sizeof(long))
1036 diffs |= *cp1++ ^ *cp2++;
1037
1038 return diffs == 0;
1020} 1039}
1021 1040
1022static bool __flow_cmp_key(const struct sw_flow *flow, 1041static bool __flow_cmp_masked_key(const struct sw_flow *flow,
1023 const struct sw_flow_key *key, int key_start, int key_end) 1042 const struct sw_flow_key *key, int key_start, int key_end)
1024{ 1043{
1025 return __cmp_key(&flow->key, key, key_start, key_end); 1044 return __cmp_key(&flow->key, key, key_start, key_end);
@@ -1056,7 +1075,7 @@ struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
1056} 1075}
1057 1076
1058static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table, 1077static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
1059 const struct sw_flow_key *flow_key, 1078 const struct sw_flow_key *unmasked,
1060 struct sw_flow_mask *mask) 1079 struct sw_flow_mask *mask)
1061{ 1080{
1062 struct sw_flow *flow; 1081 struct sw_flow *flow;
@@ -1066,12 +1085,13 @@ static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
1066 u32 hash; 1085 u32 hash;
1067 struct sw_flow_key masked_key; 1086 struct sw_flow_key masked_key;
1068 1087
1069 ovs_flow_key_mask(&masked_key, flow_key, mask); 1088 ovs_flow_key_mask(&masked_key, unmasked, mask);
1070 hash = ovs_flow_hash(&masked_key, key_start, key_end); 1089 hash = ovs_flow_hash(&masked_key, key_start, key_end);
1071 head = find_bucket(table, hash); 1090 head = find_bucket(table, hash);
1072 hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) { 1091 hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
1073 if (flow->mask == mask && 1092 if (flow->mask == mask &&
1074 __flow_cmp_key(flow, &masked_key, key_start, key_end)) 1093 __flow_cmp_masked_key(flow, &masked_key,
1094 key_start, key_end))
1075 return flow; 1095 return flow;
1076 } 1096 }
1077 return NULL; 1097 return NULL;
@@ -1961,6 +1981,8 @@ nla_put_failure:
1961 * Returns zero if successful or a negative error code. */ 1981 * Returns zero if successful or a negative error code. */
1962int ovs_flow_init(void) 1982int ovs_flow_init(void)
1963{ 1983{
1984 BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long));
1985
1964 flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0, 1986 flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0,
1965 0, NULL); 1987 0, NULL);
1966 if (flow_cache == NULL) 1988 if (flow_cache == NULL)
@@ -2016,7 +2038,7 @@ static bool ovs_sw_flow_mask_equal(const struct sw_flow_mask *a,
2016 2038
2017 return (a->range.end == b->range.end) 2039 return (a->range.end == b->range.end)
2018 && (a->range.start == b->range.start) 2040 && (a->range.start == b->range.start)
2019 && (memcmp(a_, b_, ovs_sw_flow_mask_actual_size(a)) == 0); 2041 && (memcmp(a_, b_, range_n_bytes(&a->range)) == 0);
2020} 2042}
2021 2043
2022struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl, 2044struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl,
@@ -2053,5 +2075,5 @@ static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask,
2053 u8 *m = (u8 *)&mask->key + range->start; 2075 u8 *m = (u8 *)&mask->key + range->start;
2054 2076
2055 mask->range = *range; 2077 mask->range = *range;
2056 memset(m, val, ovs_sw_flow_mask_size_roundup(mask)); 2078 memset(m, val, range_n_bytes(range));
2057} 2079}
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index e79305184b79..b65f885ac3dc 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -125,7 +125,7 @@ struct sw_flow_key {
125 } nd; 125 } nd;
126 } ipv6; 126 } ipv6;
127 }; 127 };
128}; 128} __aligned(__alignof__(long));
129 129
130struct sw_flow { 130struct sw_flow {
131 struct rcu_head rcu; 131 struct rcu_head rcu;
@@ -149,11 +149,6 @@ struct sw_flow_key_range {
149 size_t end; 149 size_t end;
150}; 150};
151 151
152static inline u16 ovs_sw_flow_key_range_actual_size(const struct sw_flow_key_range *range)
153{
154 return range->end - range->start;
155}
156
157struct sw_flow_match { 152struct sw_flow_match {
158 struct sw_flow_key *key; 153 struct sw_flow_key *key;
159 struct sw_flow_key_range range; 154 struct sw_flow_key_range range;
@@ -253,18 +248,6 @@ struct sw_flow_mask {
253 struct sw_flow_key key; 248 struct sw_flow_key key;
254}; 249};
255 250
256static inline u16
257ovs_sw_flow_mask_actual_size(const struct sw_flow_mask *mask)
258{
259 return ovs_sw_flow_key_range_actual_size(&mask->range);
260}
261
262static inline u16
263ovs_sw_flow_mask_size_roundup(const struct sw_flow_mask *mask)
264{
265 return roundup(ovs_sw_flow_mask_actual_size(mask), sizeof(u32));
266}
267
268struct sw_flow_mask *ovs_sw_flow_mask_alloc(void); 251struct sw_flow_mask *ovs_sw_flow_mask_alloc(void);
269void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *); 252void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *);
270void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *, bool deferred); 253void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *, bool deferred);