diff options
Diffstat (limited to 'net/openvswitch/flow_table.c')
-rw-r--r-- | net/openvswitch/flow_table.c | 150 |
1 files changed, 85 insertions, 65 deletions
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index e42542706087..3c268b3d71c3 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <linux/if_vlan.h> | 25 | #include <linux/if_vlan.h> |
26 | #include <net/llc_pdu.h> | 26 | #include <net/llc_pdu.h> |
27 | #include <linux/kernel.h> | 27 | #include <linux/kernel.h> |
28 | #include <linux/jhash.h> | 28 | #include <linux/hash.h> |
29 | #include <linux/jiffies.h> | 29 | #include <linux/jiffies.h> |
30 | #include <linux/llc.h> | 30 | #include <linux/llc.h> |
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
@@ -44,8 +44,6 @@ | |||
44 | #include <net/ipv6.h> | 44 | #include <net/ipv6.h> |
45 | #include <net/ndisc.h> | 45 | #include <net/ndisc.h> |
46 | 46 | ||
47 | #include "datapath.h" | ||
48 | |||
49 | #define TBL_MIN_BUCKETS 1024 | 47 | #define TBL_MIN_BUCKETS 1024 |
50 | #define REHASH_INTERVAL (10 * 60 * HZ) | 48 | #define REHASH_INTERVAL (10 * 60 * HZ) |
51 | 49 | ||
@@ -72,19 +70,42 @@ void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, | |||
72 | *d++ = *s++ & *m++; | 70 | *d++ = *s++ & *m++; |
73 | } | 71 | } |
74 | 72 | ||
75 | struct sw_flow *ovs_flow_alloc(void) | 73 | struct sw_flow *ovs_flow_alloc(bool percpu_stats) |
76 | { | 74 | { |
77 | struct sw_flow *flow; | 75 | struct sw_flow *flow; |
76 | int cpu; | ||
78 | 77 | ||
79 | flow = kmem_cache_alloc(flow_cache, GFP_KERNEL); | 78 | flow = kmem_cache_alloc(flow_cache, GFP_KERNEL); |
80 | if (!flow) | 79 | if (!flow) |
81 | return ERR_PTR(-ENOMEM); | 80 | return ERR_PTR(-ENOMEM); |
82 | 81 | ||
83 | spin_lock_init(&flow->lock); | ||
84 | flow->sf_acts = NULL; | 82 | flow->sf_acts = NULL; |
85 | flow->mask = NULL; | 83 | flow->mask = NULL; |
86 | 84 | ||
85 | flow->stats.is_percpu = percpu_stats; | ||
86 | |||
87 | if (!percpu_stats) { | ||
88 | flow->stats.stat = kzalloc(sizeof(*flow->stats.stat), GFP_KERNEL); | ||
89 | if (!flow->stats.stat) | ||
90 | goto err; | ||
91 | |||
92 | spin_lock_init(&flow->stats.stat->lock); | ||
93 | } else { | ||
94 | flow->stats.cpu_stats = alloc_percpu(struct flow_stats); | ||
95 | if (!flow->stats.cpu_stats) | ||
96 | goto err; | ||
97 | |||
98 | for_each_possible_cpu(cpu) { | ||
99 | struct flow_stats *cpu_stats; | ||
100 | |||
101 | cpu_stats = per_cpu_ptr(flow->stats.cpu_stats, cpu); | ||
102 | spin_lock_init(&cpu_stats->lock); | ||
103 | } | ||
104 | } | ||
87 | return flow; | 105 | return flow; |
106 | err: | ||
107 | kmem_cache_free(flow_cache, flow); | ||
108 | return ERR_PTR(-ENOMEM); | ||
88 | } | 109 | } |
89 | 110 | ||
90 | int ovs_flow_tbl_count(struct flow_table *table) | 111 | int ovs_flow_tbl_count(struct flow_table *table) |
@@ -118,6 +139,10 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets) | |||
118 | static void flow_free(struct sw_flow *flow) | 139 | static void flow_free(struct sw_flow *flow) |
119 | { | 140 | { |
120 | kfree((struct sf_flow_acts __force *)flow->sf_acts); | 141 | kfree((struct sf_flow_acts __force *)flow->sf_acts); |
142 | if (flow->stats.is_percpu) | ||
143 | free_percpu(flow->stats.cpu_stats); | ||
144 | else | ||
145 | kfree(flow->stats.stat); | ||
121 | kmem_cache_free(flow_cache, flow); | 146 | kmem_cache_free(flow_cache, flow); |
122 | } | 147 | } |
123 | 148 | ||
@@ -128,36 +153,29 @@ static void rcu_free_flow_callback(struct rcu_head *rcu) | |||
128 | flow_free(flow); | 153 | flow_free(flow); |
129 | } | 154 | } |
130 | 155 | ||
131 | static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu) | ||
132 | { | ||
133 | struct sw_flow_mask *mask = container_of(rcu, struct sw_flow_mask, rcu); | ||
134 | |||
135 | kfree(mask); | ||
136 | } | ||
137 | |||
138 | static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred) | ||
139 | { | ||
140 | if (!mask) | ||
141 | return; | ||
142 | |||
143 | BUG_ON(!mask->ref_count); | ||
144 | mask->ref_count--; | ||
145 | |||
146 | if (!mask->ref_count) { | ||
147 | list_del_rcu(&mask->list); | ||
148 | if (deferred) | ||
149 | call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb); | ||
150 | else | ||
151 | kfree(mask); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | void ovs_flow_free(struct sw_flow *flow, bool deferred) | 156 | void ovs_flow_free(struct sw_flow *flow, bool deferred) |
156 | { | 157 | { |
157 | if (!flow) | 158 | if (!flow) |
158 | return; | 159 | return; |
159 | 160 | ||
160 | flow_mask_del_ref(flow->mask, deferred); | 161 | if (flow->mask) { |
162 | struct sw_flow_mask *mask = flow->mask; | ||
163 | |||
164 | /* ovs-lock is required to protect mask-refcount and | ||
165 | * mask list. | ||
166 | */ | ||
167 | ASSERT_OVSL(); | ||
168 | BUG_ON(!mask->ref_count); | ||
169 | mask->ref_count--; | ||
170 | |||
171 | if (!mask->ref_count) { | ||
172 | list_del_rcu(&mask->list); | ||
173 | if (deferred) | ||
174 | kfree_rcu(mask, rcu); | ||
175 | else | ||
176 | kfree(mask); | ||
177 | } | ||
178 | } | ||
161 | 179 | ||
162 | if (deferred) | 180 | if (deferred) |
163 | call_rcu(&flow->rcu, rcu_free_flow_callback); | 181 | call_rcu(&flow->rcu, rcu_free_flow_callback); |
@@ -170,26 +188,9 @@ static void free_buckets(struct flex_array *buckets) | |||
170 | flex_array_free(buckets); | 188 | flex_array_free(buckets); |
171 | } | 189 | } |
172 | 190 | ||
191 | |||
173 | static void __table_instance_destroy(struct table_instance *ti) | 192 | static void __table_instance_destroy(struct table_instance *ti) |
174 | { | 193 | { |
175 | int i; | ||
176 | |||
177 | if (ti->keep_flows) | ||
178 | goto skip_flows; | ||
179 | |||
180 | for (i = 0; i < ti->n_buckets; i++) { | ||
181 | struct sw_flow *flow; | ||
182 | struct hlist_head *head = flex_array_get(ti->buckets, i); | ||
183 | struct hlist_node *n; | ||
184 | int ver = ti->node_ver; | ||
185 | |||
186 | hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { | ||
187 | hlist_del(&flow->hash_node[ver]); | ||
188 | ovs_flow_free(flow, false); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | skip_flows: | ||
193 | free_buckets(ti->buckets); | 194 | free_buckets(ti->buckets); |
194 | kfree(ti); | 195 | kfree(ti); |
195 | } | 196 | } |
@@ -240,20 +241,38 @@ static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) | |||
240 | 241 | ||
241 | static void table_instance_destroy(struct table_instance *ti, bool deferred) | 242 | static void table_instance_destroy(struct table_instance *ti, bool deferred) |
242 | { | 243 | { |
244 | int i; | ||
245 | |||
243 | if (!ti) | 246 | if (!ti) |
244 | return; | 247 | return; |
245 | 248 | ||
249 | if (ti->keep_flows) | ||
250 | goto skip_flows; | ||
251 | |||
252 | for (i = 0; i < ti->n_buckets; i++) { | ||
253 | struct sw_flow *flow; | ||
254 | struct hlist_head *head = flex_array_get(ti->buckets, i); | ||
255 | struct hlist_node *n; | ||
256 | int ver = ti->node_ver; | ||
257 | |||
258 | hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { | ||
259 | hlist_del_rcu(&flow->hash_node[ver]); | ||
260 | ovs_flow_free(flow, deferred); | ||
261 | } | ||
262 | } | ||
263 | |||
264 | skip_flows: | ||
246 | if (deferred) | 265 | if (deferred) |
247 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); | 266 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); |
248 | else | 267 | else |
249 | __table_instance_destroy(ti); | 268 | __table_instance_destroy(ti); |
250 | } | 269 | } |
251 | 270 | ||
252 | void ovs_flow_tbl_destroy(struct flow_table *table) | 271 | void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred) |
253 | { | 272 | { |
254 | struct table_instance *ti = ovsl_dereference(table->ti); | 273 | struct table_instance *ti = ovsl_dereference(table->ti); |
255 | 274 | ||
256 | table_instance_destroy(ti, false); | 275 | table_instance_destroy(ti, deferred); |
257 | } | 276 | } |
258 | 277 | ||
259 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, | 278 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, |
@@ -362,7 +381,7 @@ static u32 flow_hash(const struct sw_flow_key *key, int key_start, | |||
362 | /* Make sure number of hash bytes are multiple of u32. */ | 381 | /* Make sure number of hash bytes are multiple of u32. */ |
363 | BUILD_BUG_ON(sizeof(long) % sizeof(u32)); | 382 | BUILD_BUG_ON(sizeof(long) % sizeof(u32)); |
364 | 383 | ||
365 | return jhash2(hash_key, hash_u32s, 0); | 384 | return arch_fast_hash2(hash_key, hash_u32s, 0); |
366 | } | 385 | } |
367 | 386 | ||
368 | static int flow_key_start(const struct sw_flow_key *key) | 387 | static int flow_key_start(const struct sw_flow_key *key) |
@@ -429,11 +448,11 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti, | |||
429 | return NULL; | 448 | return NULL; |
430 | } | 449 | } |
431 | 450 | ||
432 | struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, | 451 | struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *tbl, |
433 | const struct sw_flow_key *key, | 452 | const struct sw_flow_key *key, |
434 | u32 *n_mask_hit) | 453 | u32 *n_mask_hit) |
435 | { | 454 | { |
436 | struct table_instance *ti = rcu_dereference(tbl->ti); | 455 | struct table_instance *ti = rcu_dereference_ovsl(tbl->ti); |
437 | struct sw_flow_mask *mask; | 456 | struct sw_flow_mask *mask; |
438 | struct sw_flow *flow; | 457 | struct sw_flow *flow; |
439 | 458 | ||
@@ -447,6 +466,14 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, | |||
447 | return NULL; | 466 | return NULL; |
448 | } | 467 | } |
449 | 468 | ||
469 | struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, | ||
470 | const struct sw_flow_key *key) | ||
471 | { | ||
472 | u32 __always_unused n_mask_hit; | ||
473 | |||
474 | return ovs_flow_tbl_lookup_stats(tbl, key, &n_mask_hit); | ||
475 | } | ||
476 | |||
450 | int ovs_flow_tbl_num_masks(const struct flow_table *table) | 477 | int ovs_flow_tbl_num_masks(const struct flow_table *table) |
451 | { | 478 | { |
452 | struct sw_flow_mask *mask; | 479 | struct sw_flow_mask *mask; |
@@ -478,16 +505,11 @@ static struct sw_flow_mask *mask_alloc(void) | |||
478 | 505 | ||
479 | mask = kmalloc(sizeof(*mask), GFP_KERNEL); | 506 | mask = kmalloc(sizeof(*mask), GFP_KERNEL); |
480 | if (mask) | 507 | if (mask) |
481 | mask->ref_count = 0; | 508 | mask->ref_count = 1; |
482 | 509 | ||
483 | return mask; | 510 | return mask; |
484 | } | 511 | } |
485 | 512 | ||
486 | static void mask_add_ref(struct sw_flow_mask *mask) | ||
487 | { | ||
488 | mask->ref_count++; | ||
489 | } | ||
490 | |||
491 | static bool mask_equal(const struct sw_flow_mask *a, | 513 | static bool mask_equal(const struct sw_flow_mask *a, |
492 | const struct sw_flow_mask *b) | 514 | const struct sw_flow_mask *b) |
493 | { | 515 | { |
@@ -514,11 +536,7 @@ static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl, | |||
514 | return NULL; | 536 | return NULL; |
515 | } | 537 | } |
516 | 538 | ||
517 | /** | 539 | /* Add 'mask' into the mask list, if it is not already there. */ |
518 | * add a new mask into the mask list. | ||
519 | * The caller needs to make sure that 'mask' is not the same | ||
520 | * as any masks that are already on the list. | ||
521 | */ | ||
522 | static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, | 540 | static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, |
523 | struct sw_flow_mask *new) | 541 | struct sw_flow_mask *new) |
524 | { | 542 | { |
@@ -532,9 +550,11 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, | |||
532 | mask->key = new->key; | 550 | mask->key = new->key; |
533 | mask->range = new->range; | 551 | mask->range = new->range; |
534 | list_add_rcu(&mask->list, &tbl->mask_list); | 552 | list_add_rcu(&mask->list, &tbl->mask_list); |
553 | } else { | ||
554 | BUG_ON(!mask->ref_count); | ||
555 | mask->ref_count++; | ||
535 | } | 556 | } |
536 | 557 | ||
537 | mask_add_ref(mask); | ||
538 | flow->mask = mask; | 558 | flow->mask = mask; |
539 | return 0; | 559 | return 0; |
540 | } | 560 | } |