diff options
Diffstat (limited to 'net/openvswitch/flow_table.c')
-rw-r--r-- | net/openvswitch/flow_table.c | 228 |
1 files changed, 178 insertions, 50 deletions
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index 5899bf161c61..4613df8c8290 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c | |||
@@ -85,6 +85,8 @@ struct sw_flow *ovs_flow_alloc(void) | |||
85 | 85 | ||
86 | flow->sf_acts = NULL; | 86 | flow->sf_acts = NULL; |
87 | flow->mask = NULL; | 87 | flow->mask = NULL; |
88 | flow->id.unmasked_key = NULL; | ||
89 | flow->id.ufid_len = 0; | ||
88 | flow->stats_last_writer = NUMA_NO_NODE; | 90 | flow->stats_last_writer = NUMA_NO_NODE; |
89 | 91 | ||
90 | /* Initialize the default stat node. */ | 92 | /* Initialize the default stat node. */ |
@@ -139,6 +141,8 @@ static void flow_free(struct sw_flow *flow) | |||
139 | { | 141 | { |
140 | int node; | 142 | int node; |
141 | 143 | ||
144 | if (ovs_identifier_is_key(&flow->id)) | ||
145 | kfree(flow->id.unmasked_key); | ||
142 | kfree((struct sw_flow_actions __force *)flow->sf_acts); | 146 | kfree((struct sw_flow_actions __force *)flow->sf_acts); |
143 | for_each_node(node) | 147 | for_each_node(node) |
144 | if (flow->stats[node]) | 148 | if (flow->stats[node]) |
@@ -200,18 +204,28 @@ static struct table_instance *table_instance_alloc(int new_size) | |||
200 | 204 | ||
201 | int ovs_flow_tbl_init(struct flow_table *table) | 205 | int ovs_flow_tbl_init(struct flow_table *table) |
202 | { | 206 | { |
203 | struct table_instance *ti; | 207 | struct table_instance *ti, *ufid_ti; |
204 | 208 | ||
205 | ti = table_instance_alloc(TBL_MIN_BUCKETS); | 209 | ti = table_instance_alloc(TBL_MIN_BUCKETS); |
206 | 210 | ||
207 | if (!ti) | 211 | if (!ti) |
208 | return -ENOMEM; | 212 | return -ENOMEM; |
209 | 213 | ||
214 | ufid_ti = table_instance_alloc(TBL_MIN_BUCKETS); | ||
215 | if (!ufid_ti) | ||
216 | goto free_ti; | ||
217 | |||
210 | rcu_assign_pointer(table->ti, ti); | 218 | rcu_assign_pointer(table->ti, ti); |
219 | rcu_assign_pointer(table->ufid_ti, ufid_ti); | ||
211 | INIT_LIST_HEAD(&table->mask_list); | 220 | INIT_LIST_HEAD(&table->mask_list); |
212 | table->last_rehash = jiffies; | 221 | table->last_rehash = jiffies; |
213 | table->count = 0; | 222 | table->count = 0; |
223 | table->ufid_count = 0; | ||
214 | return 0; | 224 | return 0; |
225 | |||
226 | free_ti: | ||
227 | __table_instance_destroy(ti); | ||
228 | return -ENOMEM; | ||
215 | } | 229 | } |
216 | 230 | ||
217 | static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) | 231 | static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) |
@@ -221,13 +235,16 @@ static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) | |||
221 | __table_instance_destroy(ti); | 235 | __table_instance_destroy(ti); |
222 | } | 236 | } |
223 | 237 | ||
224 | static void table_instance_destroy(struct table_instance *ti, bool deferred) | 238 | static void table_instance_destroy(struct table_instance *ti, |
239 | struct table_instance *ufid_ti, | ||
240 | bool deferred) | ||
225 | { | 241 | { |
226 | int i; | 242 | int i; |
227 | 243 | ||
228 | if (!ti) | 244 | if (!ti) |
229 | return; | 245 | return; |
230 | 246 | ||
247 | BUG_ON(!ufid_ti); | ||
231 | if (ti->keep_flows) | 248 | if (ti->keep_flows) |
232 | goto skip_flows; | 249 | goto skip_flows; |
233 | 250 | ||
@@ -236,18 +253,24 @@ static void table_instance_destroy(struct table_instance *ti, bool deferred) | |||
236 | struct hlist_head *head = flex_array_get(ti->buckets, i); | 253 | struct hlist_head *head = flex_array_get(ti->buckets, i); |
237 | struct hlist_node *n; | 254 | struct hlist_node *n; |
238 | int ver = ti->node_ver; | 255 | int ver = ti->node_ver; |
256 | int ufid_ver = ufid_ti->node_ver; | ||
239 | 257 | ||
240 | hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { | 258 | hlist_for_each_entry_safe(flow, n, head, flow_table.node[ver]) { |
241 | hlist_del_rcu(&flow->hash_node[ver]); | 259 | hlist_del_rcu(&flow->flow_table.node[ver]); |
260 | if (ovs_identifier_is_ufid(&flow->id)) | ||
261 | hlist_del_rcu(&flow->ufid_table.node[ufid_ver]); | ||
242 | ovs_flow_free(flow, deferred); | 262 | ovs_flow_free(flow, deferred); |
243 | } | 263 | } |
244 | } | 264 | } |
245 | 265 | ||
246 | skip_flows: | 266 | skip_flows: |
247 | if (deferred) | 267 | if (deferred) { |
248 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); | 268 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); |
249 | else | 269 | call_rcu(&ufid_ti->rcu, flow_tbl_destroy_rcu_cb); |
270 | } else { | ||
250 | __table_instance_destroy(ti); | 271 | __table_instance_destroy(ti); |
272 | __table_instance_destroy(ufid_ti); | ||
273 | } | ||
251 | } | 274 | } |
252 | 275 | ||
253 | /* No need for locking this function is called from RCU callback or | 276 | /* No need for locking this function is called from RCU callback or |
@@ -256,8 +279,9 @@ skip_flows: | |||
256 | void ovs_flow_tbl_destroy(struct flow_table *table) | 279 | void ovs_flow_tbl_destroy(struct flow_table *table) |
257 | { | 280 | { |
258 | struct table_instance *ti = rcu_dereference_raw(table->ti); | 281 | struct table_instance *ti = rcu_dereference_raw(table->ti); |
282 | struct table_instance *ufid_ti = rcu_dereference_raw(table->ufid_ti); | ||
259 | 283 | ||
260 | table_instance_destroy(ti, false); | 284 | table_instance_destroy(ti, ufid_ti, false); |
261 | } | 285 | } |
262 | 286 | ||
263 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, | 287 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, |
@@ -272,7 +296,7 @@ struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, | |||
272 | while (*bucket < ti->n_buckets) { | 296 | while (*bucket < ti->n_buckets) { |
273 | i = 0; | 297 | i = 0; |
274 | head = flex_array_get(ti->buckets, *bucket); | 298 | head = flex_array_get(ti->buckets, *bucket); |
275 | hlist_for_each_entry_rcu(flow, head, hash_node[ver]) { | 299 | hlist_for_each_entry_rcu(flow, head, flow_table.node[ver]) { |
276 | if (i < *last) { | 300 | if (i < *last) { |
277 | i++; | 301 | i++; |
278 | continue; | 302 | continue; |
@@ -294,16 +318,26 @@ static struct hlist_head *find_bucket(struct table_instance *ti, u32 hash) | |||
294 | (hash & (ti->n_buckets - 1))); | 318 | (hash & (ti->n_buckets - 1))); |
295 | } | 319 | } |
296 | 320 | ||
297 | static void table_instance_insert(struct table_instance *ti, struct sw_flow *flow) | 321 | static void table_instance_insert(struct table_instance *ti, |
322 | struct sw_flow *flow) | ||
323 | { | ||
324 | struct hlist_head *head; | ||
325 | |||
326 | head = find_bucket(ti, flow->flow_table.hash); | ||
327 | hlist_add_head_rcu(&flow->flow_table.node[ti->node_ver], head); | ||
328 | } | ||
329 | |||
330 | static void ufid_table_instance_insert(struct table_instance *ti, | ||
331 | struct sw_flow *flow) | ||
298 | { | 332 | { |
299 | struct hlist_head *head; | 333 | struct hlist_head *head; |
300 | 334 | ||
301 | head = find_bucket(ti, flow->hash); | 335 | head = find_bucket(ti, flow->ufid_table.hash); |
302 | hlist_add_head_rcu(&flow->hash_node[ti->node_ver], head); | 336 | hlist_add_head_rcu(&flow->ufid_table.node[ti->node_ver], head); |
303 | } | 337 | } |
304 | 338 | ||
305 | static void flow_table_copy_flows(struct table_instance *old, | 339 | static void flow_table_copy_flows(struct table_instance *old, |
306 | struct table_instance *new) | 340 | struct table_instance *new, bool ufid) |
307 | { | 341 | { |
308 | int old_ver; | 342 | int old_ver; |
309 | int i; | 343 | int i; |
@@ -318,15 +352,21 @@ static void flow_table_copy_flows(struct table_instance *old, | |||
318 | 352 | ||
319 | head = flex_array_get(old->buckets, i); | 353 | head = flex_array_get(old->buckets, i); |
320 | 354 | ||
321 | hlist_for_each_entry(flow, head, hash_node[old_ver]) | 355 | if (ufid) |
322 | table_instance_insert(new, flow); | 356 | hlist_for_each_entry(flow, head, |
357 | ufid_table.node[old_ver]) | ||
358 | ufid_table_instance_insert(new, flow); | ||
359 | else | ||
360 | hlist_for_each_entry(flow, head, | ||
361 | flow_table.node[old_ver]) | ||
362 | table_instance_insert(new, flow); | ||
323 | } | 363 | } |
324 | 364 | ||
325 | old->keep_flows = true; | 365 | old->keep_flows = true; |
326 | } | 366 | } |
327 | 367 | ||
328 | static struct table_instance *table_instance_rehash(struct table_instance *ti, | 368 | static struct table_instance *table_instance_rehash(struct table_instance *ti, |
329 | int n_buckets) | 369 | int n_buckets, bool ufid) |
330 | { | 370 | { |
331 | struct table_instance *new_ti; | 371 | struct table_instance *new_ti; |
332 | 372 | ||
@@ -334,32 +374,45 @@ static struct table_instance *table_instance_rehash(struct table_instance *ti, | |||
334 | if (!new_ti) | 374 | if (!new_ti) |
335 | return NULL; | 375 | return NULL; |
336 | 376 | ||
337 | flow_table_copy_flows(ti, new_ti); | 377 | flow_table_copy_flows(ti, new_ti, ufid); |
338 | 378 | ||
339 | return new_ti; | 379 | return new_ti; |
340 | } | 380 | } |
341 | 381 | ||
342 | int ovs_flow_tbl_flush(struct flow_table *flow_table) | 382 | int ovs_flow_tbl_flush(struct flow_table *flow_table) |
343 | { | 383 | { |
344 | struct table_instance *old_ti; | 384 | struct table_instance *old_ti, *new_ti; |
345 | struct table_instance *new_ti; | 385 | struct table_instance *old_ufid_ti, *new_ufid_ti; |
346 | 386 | ||
347 | old_ti = ovsl_dereference(flow_table->ti); | ||
348 | new_ti = table_instance_alloc(TBL_MIN_BUCKETS); | 387 | new_ti = table_instance_alloc(TBL_MIN_BUCKETS); |
349 | if (!new_ti) | 388 | if (!new_ti) |
350 | return -ENOMEM; | 389 | return -ENOMEM; |
390 | new_ufid_ti = table_instance_alloc(TBL_MIN_BUCKETS); | ||
391 | if (!new_ufid_ti) | ||
392 | goto err_free_ti; | ||
393 | |||
394 | old_ti = ovsl_dereference(flow_table->ti); | ||
395 | old_ufid_ti = ovsl_dereference(flow_table->ufid_ti); | ||
351 | 396 | ||
352 | rcu_assign_pointer(flow_table->ti, new_ti); | 397 | rcu_assign_pointer(flow_table->ti, new_ti); |
398 | rcu_assign_pointer(flow_table->ufid_ti, new_ufid_ti); | ||
353 | flow_table->last_rehash = jiffies; | 399 | flow_table->last_rehash = jiffies; |
354 | flow_table->count = 0; | 400 | flow_table->count = 0; |
401 | flow_table->ufid_count = 0; | ||
355 | 402 | ||
356 | table_instance_destroy(old_ti, true); | 403 | table_instance_destroy(old_ti, old_ufid_ti, true); |
357 | return 0; | 404 | return 0; |
405 | |||
406 | err_free_ti: | ||
407 | __table_instance_destroy(new_ti); | ||
408 | return -ENOMEM; | ||
358 | } | 409 | } |
359 | 410 | ||
360 | static u32 flow_hash(const struct sw_flow_key *key, int key_start, | 411 | static u32 flow_hash(const struct sw_flow_key *key, |
361 | int key_end) | 412 | const struct sw_flow_key_range *range) |
362 | { | 413 | { |
414 | int key_start = range->start; | ||
415 | int key_end = range->end; | ||
363 | const u32 *hash_key = (const u32 *)((const u8 *)key + key_start); | 416 | const u32 *hash_key = (const u32 *)((const u8 *)key + key_start); |
364 | int hash_u32s = (key_end - key_start) >> 2; | 417 | int hash_u32s = (key_end - key_start) >> 2; |
365 | 418 | ||
@@ -395,19 +448,20 @@ static bool cmp_key(const struct sw_flow_key *key1, | |||
395 | 448 | ||
396 | static bool flow_cmp_masked_key(const struct sw_flow *flow, | 449 | static bool flow_cmp_masked_key(const struct sw_flow *flow, |
397 | const struct sw_flow_key *key, | 450 | const struct sw_flow_key *key, |
398 | int key_start, int key_end) | 451 | const struct sw_flow_key_range *range) |
399 | { | 452 | { |
400 | return cmp_key(&flow->key, key, key_start, key_end); | 453 | return cmp_key(&flow->key, key, range->start, range->end); |
401 | } | 454 | } |
402 | 455 | ||
403 | bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, | 456 | static bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, |
404 | const struct sw_flow_match *match) | 457 | const struct sw_flow_match *match) |
405 | { | 458 | { |
406 | struct sw_flow_key *key = match->key; | 459 | struct sw_flow_key *key = match->key; |
407 | int key_start = flow_key_start(key); | 460 | int key_start = flow_key_start(key); |
408 | int key_end = match->range.end; | 461 | int key_end = match->range.end; |
409 | 462 | ||
410 | return cmp_key(&flow->unmasked_key, key, key_start, key_end); | 463 | BUG_ON(ovs_identifier_is_ufid(&flow->id)); |
464 | return cmp_key(flow->id.unmasked_key, key, key_start, key_end); | ||
411 | } | 465 | } |
412 | 466 | ||
413 | static struct sw_flow *masked_flow_lookup(struct table_instance *ti, | 467 | static struct sw_flow *masked_flow_lookup(struct table_instance *ti, |
@@ -416,18 +470,15 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti, | |||
416 | { | 470 | { |
417 | struct sw_flow *flow; | 471 | struct sw_flow *flow; |
418 | struct hlist_head *head; | 472 | struct hlist_head *head; |
419 | int key_start = mask->range.start; | ||
420 | int key_end = mask->range.end; | ||
421 | u32 hash; | 473 | u32 hash; |
422 | struct sw_flow_key masked_key; | 474 | struct sw_flow_key masked_key; |
423 | 475 | ||
424 | ovs_flow_mask_key(&masked_key, unmasked, mask); | 476 | ovs_flow_mask_key(&masked_key, unmasked, mask); |
425 | hash = flow_hash(&masked_key, key_start, key_end); | 477 | hash = flow_hash(&masked_key, &mask->range); |
426 | head = find_bucket(ti, hash); | 478 | head = find_bucket(ti, hash); |
427 | hlist_for_each_entry_rcu(flow, head, hash_node[ti->node_ver]) { | 479 | hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) { |
428 | if (flow->mask == mask && flow->hash == hash && | 480 | if (flow->mask == mask && flow->flow_table.hash == hash && |
429 | flow_cmp_masked_key(flow, &masked_key, | 481 | flow_cmp_masked_key(flow, &masked_key, &mask->range)) |
430 | key_start, key_end)) | ||
431 | return flow; | 482 | return flow; |
432 | } | 483 | } |
433 | return NULL; | 484 | return NULL; |
@@ -469,7 +520,48 @@ struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl, | |||
469 | /* Always called under ovs-mutex. */ | 520 | /* Always called under ovs-mutex. */ |
470 | list_for_each_entry(mask, &tbl->mask_list, list) { | 521 | list_for_each_entry(mask, &tbl->mask_list, list) { |
471 | flow = masked_flow_lookup(ti, match->key, mask); | 522 | flow = masked_flow_lookup(ti, match->key, mask); |
472 | if (flow && ovs_flow_cmp_unmasked_key(flow, match)) /* Found */ | 523 | if (flow && ovs_identifier_is_key(&flow->id) && |
524 | ovs_flow_cmp_unmasked_key(flow, match)) | ||
525 | return flow; | ||
526 | } | ||
527 | return NULL; | ||
528 | } | ||
529 | |||
530 | static u32 ufid_hash(const struct sw_flow_id *sfid) | ||
531 | { | ||
532 | return jhash(sfid->ufid, sfid->ufid_len, 0); | ||
533 | } | ||
534 | |||
535 | static bool ovs_flow_cmp_ufid(const struct sw_flow *flow, | ||
536 | const struct sw_flow_id *sfid) | ||
537 | { | ||
538 | if (flow->id.ufid_len != sfid->ufid_len) | ||
539 | return false; | ||
540 | |||
541 | return !memcmp(flow->id.ufid, sfid->ufid, sfid->ufid_len); | ||
542 | } | ||
543 | |||
544 | bool ovs_flow_cmp(const struct sw_flow *flow, const struct sw_flow_match *match) | ||
545 | { | ||
546 | if (ovs_identifier_is_ufid(&flow->id)) | ||
547 | return flow_cmp_masked_key(flow, match->key, &match->range); | ||
548 | |||
549 | return ovs_flow_cmp_unmasked_key(flow, match); | ||
550 | } | ||
551 | |||
552 | struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *tbl, | ||
553 | const struct sw_flow_id *ufid) | ||
554 | { | ||
555 | struct table_instance *ti = rcu_dereference_ovsl(tbl->ufid_ti); | ||
556 | struct sw_flow *flow; | ||
557 | struct hlist_head *head; | ||
558 | u32 hash; | ||
559 | |||
560 | hash = ufid_hash(ufid); | ||
561 | head = find_bucket(ti, hash); | ||
562 | hlist_for_each_entry_rcu(flow, head, ufid_table.node[ti->node_ver]) { | ||
563 | if (flow->ufid_table.hash == hash && | ||
564 | ovs_flow_cmp_ufid(flow, ufid)) | ||
473 | return flow; | 565 | return flow; |
474 | } | 566 | } |
475 | return NULL; | 567 | return NULL; |
@@ -486,9 +578,10 @@ int ovs_flow_tbl_num_masks(const struct flow_table *table) | |||
486 | return num; | 578 | return num; |
487 | } | 579 | } |
488 | 580 | ||
489 | static struct table_instance *table_instance_expand(struct table_instance *ti) | 581 | static struct table_instance *table_instance_expand(struct table_instance *ti, |
582 | bool ufid) | ||
490 | { | 583 | { |
491 | return table_instance_rehash(ti, ti->n_buckets * 2); | 584 | return table_instance_rehash(ti, ti->n_buckets * 2, ufid); |
492 | } | 585 | } |
493 | 586 | ||
494 | /* Remove 'mask' from the mask list, if it is not needed any more. */ | 587 | /* Remove 'mask' from the mask list, if it is not needed any more. */ |
@@ -513,10 +606,15 @@ static void flow_mask_remove(struct flow_table *tbl, struct sw_flow_mask *mask) | |||
513 | void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) | 606 | void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) |
514 | { | 607 | { |
515 | struct table_instance *ti = ovsl_dereference(table->ti); | 608 | struct table_instance *ti = ovsl_dereference(table->ti); |
609 | struct table_instance *ufid_ti = ovsl_dereference(table->ufid_ti); | ||
516 | 610 | ||
517 | BUG_ON(table->count == 0); | 611 | BUG_ON(table->count == 0); |
518 | hlist_del_rcu(&flow->hash_node[ti->node_ver]); | 612 | hlist_del_rcu(&flow->flow_table.node[ti->node_ver]); |
519 | table->count--; | 613 | table->count--; |
614 | if (ovs_identifier_is_ufid(&flow->id)) { | ||
615 | hlist_del_rcu(&flow->ufid_table.node[ufid_ti->node_ver]); | ||
616 | table->ufid_count--; | ||
617 | } | ||
520 | 618 | ||
521 | /* RCU delete the mask. 'flow->mask' is not NULLed, as it should be | 619 | /* RCU delete the mask. 'flow->mask' is not NULLed, as it should be |
522 | * accessible as long as the RCU read lock is held. | 620 | * accessible as long as the RCU read lock is held. |
@@ -585,34 +683,64 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, | |||
585 | } | 683 | } |
586 | 684 | ||
587 | /* Must be called with OVS mutex held. */ | 685 | /* Must be called with OVS mutex held. */ |
588 | int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, | 686 | static void flow_key_insert(struct flow_table *table, struct sw_flow *flow) |
589 | const struct sw_flow_mask *mask) | ||
590 | { | 687 | { |
591 | struct table_instance *new_ti = NULL; | 688 | struct table_instance *new_ti = NULL; |
592 | struct table_instance *ti; | 689 | struct table_instance *ti; |
593 | int err; | ||
594 | |||
595 | err = flow_mask_insert(table, flow, mask); | ||
596 | if (err) | ||
597 | return err; | ||
598 | 690 | ||
599 | flow->hash = flow_hash(&flow->key, flow->mask->range.start, | 691 | flow->flow_table.hash = flow_hash(&flow->key, &flow->mask->range); |
600 | flow->mask->range.end); | ||
601 | ti = ovsl_dereference(table->ti); | 692 | ti = ovsl_dereference(table->ti); |
602 | table_instance_insert(ti, flow); | 693 | table_instance_insert(ti, flow); |
603 | table->count++; | 694 | table->count++; |
604 | 695 | ||
605 | /* Expand table, if necessary, to make room. */ | 696 | /* Expand table, if necessary, to make room. */ |
606 | if (table->count > ti->n_buckets) | 697 | if (table->count > ti->n_buckets) |
607 | new_ti = table_instance_expand(ti); | 698 | new_ti = table_instance_expand(ti, false); |
608 | else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL)) | 699 | else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL)) |
609 | new_ti = table_instance_rehash(ti, ti->n_buckets); | 700 | new_ti = table_instance_rehash(ti, ti->n_buckets, false); |
610 | 701 | ||
611 | if (new_ti) { | 702 | if (new_ti) { |
612 | rcu_assign_pointer(table->ti, new_ti); | 703 | rcu_assign_pointer(table->ti, new_ti); |
613 | table_instance_destroy(ti, true); | 704 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); |
614 | table->last_rehash = jiffies; | 705 | table->last_rehash = jiffies; |
615 | } | 706 | } |
707 | } | ||
708 | |||
709 | /* Must be called with OVS mutex held. */ | ||
710 | static void flow_ufid_insert(struct flow_table *table, struct sw_flow *flow) | ||
711 | { | ||
712 | struct table_instance *ti; | ||
713 | |||
714 | flow->ufid_table.hash = ufid_hash(&flow->id); | ||
715 | ti = ovsl_dereference(table->ufid_ti); | ||
716 | ufid_table_instance_insert(ti, flow); | ||
717 | table->ufid_count++; | ||
718 | |||
719 | /* Expand table, if necessary, to make room. */ | ||
720 | if (table->ufid_count > ti->n_buckets) { | ||
721 | struct table_instance *new_ti; | ||
722 | |||
723 | new_ti = table_instance_expand(ti, true); | ||
724 | if (new_ti) { | ||
725 | rcu_assign_pointer(table->ufid_ti, new_ti); | ||
726 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); | ||
727 | } | ||
728 | } | ||
729 | } | ||
730 | |||
731 | /* Must be called with OVS mutex held. */ | ||
732 | int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, | ||
733 | const struct sw_flow_mask *mask) | ||
734 | { | ||
735 | int err; | ||
736 | |||
737 | err = flow_mask_insert(table, flow, mask); | ||
738 | if (err) | ||
739 | return err; | ||
740 | flow_key_insert(table, flow); | ||
741 | if (ovs_identifier_is_ufid(&flow->id)) | ||
742 | flow_ufid_insert(table, flow); | ||
743 | |||
616 | return 0; | 744 | return 0; |
617 | } | 745 | } |
618 | 746 | ||