diff options
Diffstat (limited to 'net/openvswitch/actions.c')
-rw-r--r-- | net/openvswitch/actions.c | 50 |
1 files changed, 39 insertions, 11 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index e70d8b18e962..fe5cda0deb39 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
@@ -38,7 +38,7 @@ | |||
38 | #include "vport.h" | 38 | #include "vport.h" |
39 | 39 | ||
40 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | 40 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
41 | const struct nlattr *attr, int len, bool keep_skb); | 41 | const struct nlattr *attr, int len); |
42 | 42 | ||
43 | static int make_writable(struct sk_buff *skb, int write_len) | 43 | static int make_writable(struct sk_buff *skb, int write_len) |
44 | { | 44 | { |
@@ -434,11 +434,17 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, | |||
434 | return ovs_dp_upcall(dp, skb, &upcall); | 434 | return ovs_dp_upcall(dp, skb, &upcall); |
435 | } | 435 | } |
436 | 436 | ||
437 | static bool last_action(const struct nlattr *a, int rem) | ||
438 | { | ||
439 | return a->nla_len == rem; | ||
440 | } | ||
441 | |||
437 | static int sample(struct datapath *dp, struct sk_buff *skb, | 442 | static int sample(struct datapath *dp, struct sk_buff *skb, |
438 | const struct nlattr *attr) | 443 | const struct nlattr *attr) |
439 | { | 444 | { |
440 | const struct nlattr *acts_list = NULL; | 445 | const struct nlattr *acts_list = NULL; |
441 | const struct nlattr *a; | 446 | const struct nlattr *a; |
447 | struct sk_buff *sample_skb; | ||
442 | int rem; | 448 | int rem; |
443 | 449 | ||
444 | for (a = nla_data(attr), rem = nla_len(attr); rem > 0; | 450 | for (a = nla_data(attr), rem = nla_len(attr); rem > 0; |
@@ -455,8 +461,34 @@ static int sample(struct datapath *dp, struct sk_buff *skb, | |||
455 | } | 461 | } |
456 | } | 462 | } |
457 | 463 | ||
458 | return do_execute_actions(dp, skb, nla_data(acts_list), | 464 | rem = nla_len(acts_list); |
459 | nla_len(acts_list), true); | 465 | a = nla_data(acts_list); |
466 | |||
467 | /* Actions list is either empty or only contains a single user-space | ||
468 | * action, the latter being a special case as it is the only known | ||
469 | * usage of the sample action. | ||
470 | * In these special cases don't clone the skb as there are no | ||
471 | * side-effects in the nested actions. | ||
472 | * Otherwise, clone in case the nested actions have side effects. | ||
473 | */ | ||
474 | if (likely(rem == 0 || (nla_type(a) == OVS_ACTION_ATTR_USERSPACE && | ||
475 | last_action(a, rem)))) { | ||
476 | sample_skb = skb; | ||
477 | skb_get(skb); | ||
478 | } else { | ||
479 | sample_skb = skb_clone(skb, GFP_ATOMIC); | ||
480 | if (!sample_skb) /* Skip sample action when out of memory. */ | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | /* Note that do_execute_actions() never consumes skb. | ||
485 | * In the case where skb has been cloned above it is the clone that | ||
486 | * is consumed. Otherwise the skb_get(skb) call prevents | ||
487 | * consumption by do_execute_actions(). Thus, it is safe to simply | ||
488 | * return the error code and let the caller (also | ||
489 | * do_execute_actions()) free skb on error. | ||
490 | */ | ||
491 | return do_execute_actions(dp, sample_skb, a, rem); | ||
460 | } | 492 | } |
461 | 493 | ||
462 | static int execute_set_action(struct sk_buff *skb, | 494 | static int execute_set_action(struct sk_buff *skb, |
@@ -507,7 +539,7 @@ static int execute_set_action(struct sk_buff *skb, | |||
507 | 539 | ||
508 | /* Execute a list of actions against 'skb'. */ | 540 | /* Execute a list of actions against 'skb'. */ |
509 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | 541 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
510 | const struct nlattr *attr, int len, bool keep_skb) | 542 | const struct nlattr *attr, int len) |
511 | { | 543 | { |
512 | /* Every output action needs a separate clone of 'skb', but the common | 544 | /* Every output action needs a separate clone of 'skb', but the common |
513 | * case is just a single output action, so that doing a clone and | 545 | * case is just a single output action, so that doing a clone and |
@@ -562,12 +594,9 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
562 | } | 594 | } |
563 | } | 595 | } |
564 | 596 | ||
565 | if (prev_port != -1) { | 597 | if (prev_port != -1) |
566 | if (keep_skb) | ||
567 | skb = skb_clone(skb, GFP_ATOMIC); | ||
568 | |||
569 | do_output(dp, skb, prev_port); | 598 | do_output(dp, skb, prev_port); |
570 | } else if (!keep_skb) | 599 | else |
571 | consume_skb(skb); | 600 | consume_skb(skb); |
572 | 601 | ||
573 | return 0; | 602 | return 0; |
@@ -579,6 +608,5 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb) | |||
579 | struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); | 608 | struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); |
580 | 609 | ||
581 | OVS_CB(skb)->tun_key = NULL; | 610 | OVS_CB(skb)->tun_key = NULL; |
582 | return do_execute_actions(dp, skb, acts->actions, | 611 | return do_execute_actions(dp, skb, acts->actions, acts->actions_len); |
583 | acts->actions_len, false); | ||
584 | } | 612 | } |