[ovs-dev,merge,native,tunneling,and,port,4/7] ofproto-dpif-xlate: Keep track of the last action

Message ID 1505245749-3402-4-git-send-email-azhou@ovn.org
State Accepted
Headers show
Series
  • [ovs-dev,merge,native,tunneling,and,port,1/7] ofproto-dpif: Unfreeze within clone
Related show

Commit Message

Andy Zhou Sept. 12, 2017, 7:49 p.m.
When Openflow clone is last action of an action list, it does not
make sense for xlated actions to generate datapath clone action.

This patch attemps to Keep track of last clone action, but not
necessarily for every case.

Signed-off-by: Andy Zhou <azhou@ovn.org>
---
 include/openvswitch/ofp-actions.h |   7 ++
 ofproto/ofproto-dpif-xlate.c      | 214 +++++++++++++++++++++++---------------
 2 files changed, 140 insertions(+), 81 deletions(-)

Comments

Gregory Rose Sept. 21, 2017, 5:33 p.m. | #1
On 09/12/2017 12:49 PM, Andy Zhou wrote:
> When Openflow clone is last action of an action list, it does not
> make sense for xlated actions to generate datapath clone action.
> 
> This patch attemps to Keep track of last clone action, but not
> necessarily for every case.
> 
> Signed-off-by: Andy Zhou <azhou@ovn.org>
> ---
>   include/openvswitch/ofp-actions.h |   7 ++
>   ofproto/ofproto-dpif-xlate.c      | 214 +++++++++++++++++++++++---------------
>   2 files changed, 140 insertions(+), 81 deletions(-)
> 
> diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
> index ad8e1bec06ba..03c1d11dc9a2 100644
> --- a/include/openvswitch/ofp-actions.h
> +++ b/include/openvswitch/ofp-actions.h
> @@ -216,6 +216,13 @@ ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len)
>       return ALIGNED_CAST(struct ofpact *, (uint8_t *) ofpacts + ofpacts_len);
>   }
>   
> +static inline bool
> +ofpact_last(const struct ofpact *a, const struct ofpact *ofpacts,
> +            size_t ofpact_len)
> +{
> +    return ofpact_next(a) == ofpact_end(ofpacts, ofpact_len);
> +}
> +
>   static inline const struct ofpact *
>   ofpact_find_type_flattened(const struct ofpact *a, enum ofpact_type type,
>                              const struct ofpact * const end)
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 223313d4ecba..9e39a4ff7f49 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -507,11 +507,12 @@ static struct xlate_cfg *new_xcfg = NULL;
>   
>   static bool may_receive(const struct xport *, struct xlate_ctx *);
>   static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
> -                             struct xlate_ctx *);
> +                             struct xlate_ctx *, bool);
>   static void xlate_normal(struct xlate_ctx *);
>   static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
>                                  uint8_t table_id, bool may_packet_in,
> -                               bool honor_table_miss, bool with_ct_orig);
> +                               bool honor_table_miss, bool with_ct_orig,
> +                               bool is_last_action);
>   static bool input_vid_is_valid(const struct xlate_ctx *,
>                                  uint16_t vid, struct xbundle *);
>   static void xvlan_copy(struct xvlan *dst, const struct xvlan *src);
> @@ -536,7 +537,8 @@ struct xlate_bond_recirc {
>   };
>   
>   static void compose_output_action(struct xlate_ctx *, ofp_port_t ofp_port,
> -                                  const struct xlate_bond_recirc *xr);
> +                                  const struct xlate_bond_recirc *xr,
> +                                  bool is_last_action);
>   
>   static struct xbridge *xbridge_lookup(struct xlate_cfg *,
>                                         const struct ofproto_dpif *);
> @@ -2218,7 +2220,8 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
>       memcpy(&old_vlans, &ctx->xin->flow.vlans, sizeof(old_vlans));
>       xvlan_put(&ctx->xin->flow, &out_xvlan);
>   
> -    compose_output_action(ctx, xport->ofp_port, use_recirc ? &xr : NULL);
> +    compose_output_action(ctx, xport->ofp_port, use_recirc ? &xr : NULL,
> +                          false);
>       memcpy(&ctx->xin->flow.vlans, &old_vlans, sizeof(old_vlans));
>   }
>   
> @@ -3557,7 +3560,7 @@ apply_nested_clone_actions(struct xlate_ctx *ctx, const struct xport *in_dev,
>           if (xport_stp_forward_state(out_dev) &&
>               xport_rstp_forward_state(out_dev)) {
>               xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true,
> -                               false);
> +                               false, true);
>               if (!ctx->freezing) {
>                   xlate_action_set(ctx);
>               }
> @@ -3572,7 +3575,7 @@ apply_nested_clone_actions(struct xlate_ctx *ctx, const struct xport *in_dev,
>               mirror_mask_t old_mirrors2 = ctx->mirrors;
>   
>               xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true,
> -                               false);
> +                               false, true);
>               ctx->mirrors = old_mirrors2;
>               ctx->base_flow = old_base_flow;
>               ctx->odp_actions->size = old_size;
> @@ -3716,7 +3719,8 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>   
>   static void
>   compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> -                        const struct xlate_bond_recirc *xr, bool check_stp)
> +                        const struct xlate_bond_recirc *xr, bool check_stp,
> +                        bool is_last_action OVS_UNUSED)
>   {
>       const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
>       struct flow_wildcards *wc = ctx->wc;
> @@ -3882,13 +3886,15 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>   
>   static void
>   compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> -                      const struct xlate_bond_recirc *xr)
> +                      const struct xlate_bond_recirc *xr,
> +                      bool is_last_action)
>   {
> -    compose_output_action__(ctx, ofp_port, xr, true);
> +    compose_output_action__(ctx, ofp_port, xr, true, is_last_action);
>   }
>   
>   static void
> -xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule, bool deepens)
> +xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule,
> +                  bool deepens, bool is_last_action)
>   {
>       struct rule_dpif *old_rule = ctx->rule;
>       ovs_be64 old_cookie = ctx->rule_cookie;
> @@ -3904,7 +3910,8 @@ xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule, bool deepens)
>       ctx->rule = rule;
>       ctx->rule_cookie = rule->up.flow_cookie;
>       actions = rule_get_actions(&rule->up);
> -    do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx);
> +    do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx,
> +                     is_last_action);
>       ctx->rule_cookie = old_cookie;
>       ctx->rule = old_rule;
>       ctx->depth -= deepens;
> @@ -3979,7 +3986,7 @@ tuple_swap(struct flow *flow, struct flow_wildcards *wc)
>   static void
>   xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id,
>                      bool may_packet_in, bool honor_table_miss,
> -                   bool with_ct_orig)
> +                   bool with_ct_orig, bool is_last_action)
>   {
>       /* Check if we need to recirculate before matching in a table. */
>       if (ctx->was_mpls) {
> @@ -4030,7 +4037,8 @@ xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id,
>   
>               struct ovs_list *old_trace = ctx->xin->trace;
>               xlate_report_table(ctx, rule, table_id);
> -            xlate_recursively(ctx, rule, table_id <= old_table_id);
> +            xlate_recursively(ctx, rule, table_id <= old_table_id,
> +                              is_last_action);
>               ctx->xin->trace = old_trace;
>           }
>   
> @@ -4057,7 +4065,8 @@ xlate_group_stats(struct xlate_ctx *ctx, struct group_dpif *group,
>   }
>   
>   static void
> -xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket)
> +xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket,
> +                   bool is_last_action)
>   {
>       uint64_t action_list_stub[1024 / 8];
>       struct ofpbuf action_list = OFPBUF_STUB_INITIALIZER(action_list_stub);
> @@ -4068,7 +4077,7 @@ xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket)
>   
>       ofpacts_execute_action_set(&action_list, &action_set);
>       ctx->depth++;
> -    do_xlate_actions(action_list.data, action_list.size, ctx);
> +    do_xlate_actions(action_list.data, action_list.size, ctx, is_last_action);
>       ctx->depth--;
>   
>       ofpbuf_uninit(&action_list);
> @@ -4106,23 +4115,26 @@ xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket)
>   }
>   
>   static void
> -xlate_all_group(struct xlate_ctx *ctx, struct group_dpif *group)
> +xlate_all_group(struct xlate_ctx *ctx, struct group_dpif *group,
> +                bool is_last_action)
>   {
>       struct ofputil_bucket *bucket;
>       LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
> -        xlate_group_bucket(ctx, bucket);
> +        bool last = is_last_action && !bucket->list_node.next;
> +        xlate_group_bucket(ctx, bucket, last);
>       }
>       xlate_group_stats(ctx, group, NULL);
>   }
>   
>   static void
> -xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group)
> +xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group,
> +               bool is_last_action)
>   {
>       struct ofputil_bucket *bucket;
>   
>       bucket = group_first_live_bucket(ctx, group, 0);
>       if (bucket) {
> -        xlate_group_bucket(ctx, bucket);
> +        xlate_group_bucket(ctx, bucket, is_last_action);
>           xlate_group_stats(ctx, group, bucket);
>       } else if (ctx->xin->xcache) {
>           ofproto_group_unref(&group->up);
> @@ -4130,7 +4142,8 @@ xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group)
>   }
>   
>   static void
> -xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
> +xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group,
> +                           bool is_last_action)
>   {
>       struct flow_wildcards *wc = ctx->wc;
>       struct ofputil_bucket *bucket;
> @@ -4140,7 +4153,7 @@ xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
>       flow_mask_hash_fields(&ctx->xin->flow, wc, NX_HASH_FIELDS_SYMMETRIC_L4);
>       bucket = group_best_live_bucket(ctx, group, basis);
>       if (bucket) {
> -        xlate_group_bucket(ctx, bucket);
> +        xlate_group_bucket(ctx, bucket, is_last_action);
>           xlate_group_stats(ctx, group, bucket);
>       } else if (ctx->xin->xcache) {
>           ofproto_group_unref(&group->up);
> @@ -4148,7 +4161,8 @@ xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
>   }
>   
>   static void
> -xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
> +xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group,
> +                               bool is_last_action)
>   {
>       const struct field_array *fields = &group->up.props.fields;
>       const uint8_t *mask_values = fields->values;
> @@ -4186,7 +4200,7 @@ xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
>   
>       struct ofputil_bucket *bucket = group_best_live_bucket(ctx, group, basis);
>       if (bucket) {
> -        xlate_group_bucket(ctx, bucket);
> +        xlate_group_bucket(ctx, bucket, is_last_action);
>           xlate_group_stats(ctx, group, bucket);
>       } else if (ctx->xin->xcache) {
>           ofproto_group_unref(&group->up);
> @@ -4194,7 +4208,8 @@ xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
>   }
>   
>   static void
> -xlate_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
> +xlate_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group,
> +                           bool is_last_action)
>   {
>       struct ofputil_bucket *bucket;
>   
> @@ -4218,7 +4233,7 @@ xlate_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
>               ctx->wc->masks.dp_hash |= mask;
>               bucket = group_best_live_bucket(ctx, group, basis);
>               if (bucket) {
> -                xlate_group_bucket(ctx, bucket);
> +                xlate_group_bucket(ctx, bucket, is_last_action);
>                   xlate_group_stats(ctx, group, bucket);
>               }
>           }
> @@ -4226,7 +4241,8 @@ xlate_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
>   }
>   
>   static void
> -xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
> +xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group,
> +                   bool is_last_action)
>   {
>       const char *selection_method = group->up.props.selection_method;
>   
> @@ -4238,11 +4254,11 @@ xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
>       }
>   
>       if (selection_method[0] == '\0') {
> -        xlate_default_select_group(ctx, group);
> +        xlate_default_select_group(ctx, group, is_last_action);
>       } else if (!strcasecmp("hash", selection_method)) {
> -        xlate_hash_fields_select_group(ctx, group);
> +        xlate_hash_fields_select_group(ctx, group, is_last_action);
>       } else if (!strcasecmp("dp_hash", selection_method)) {
> -        xlate_dp_hash_select_group(ctx, group);
> +        xlate_dp_hash_select_group(ctx, group, is_last_action);
>       } else {
>           /* Parsing of groups should ensure this never happens */
>           OVS_NOT_REACHED();
> @@ -4250,7 +4266,8 @@ xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
>   }
>   
>   static void
> -xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group)
> +xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group,
> +                     bool is_last_action)
>   {
>       bool was_in_group = ctx->in_group;
>       ctx->in_group = true;
> @@ -4258,13 +4275,13 @@ xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group)
>       switch (group->up.type) {
>       case OFPGT11_ALL:
>       case OFPGT11_INDIRECT:
> -        xlate_all_group(ctx, group);
> +        xlate_all_group(ctx, group, is_last_action);
>           break;
>       case OFPGT11_SELECT:
> -        xlate_select_group(ctx, group);
> +        xlate_select_group(ctx, group, is_last_action);
>           break;
>       case OFPGT11_FF:
> -        xlate_ff_group(ctx, group);
> +        xlate_ff_group(ctx, group, is_last_action);
>           break;
>       default:
>           OVS_NOT_REACHED();
> @@ -4274,7 +4291,8 @@ xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group)
>   }
>   
>   static bool
> -xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id)
> +xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id,
> +                   bool is_last_action)
>   {
>       if (xlate_resubmit_resource_check(ctx)) {
>           struct group_dpif *group;
> @@ -4288,7 +4306,7 @@ xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id)
>                            group_id);
>               return true;
>           }
> -        xlate_group_action__(ctx, group);
> +        xlate_group_action__(ctx, group, is_last_action);
>       }
>   
>       return false;
> @@ -4296,7 +4314,8 @@ xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id)
>   
>   static void
>   xlate_ofpact_resubmit(struct xlate_ctx *ctx,
> -                      const struct ofpact_resubmit *resubmit)
> +                      const struct ofpact_resubmit *resubmit,
> +                      bool is_last_action)
>   {
>       ofp_port_t in_port;
>       uint8_t table_id;
> @@ -4321,26 +4340,47 @@ xlate_ofpact_resubmit(struct xlate_ctx *ctx,
>       }
>   
>       xlate_table_action(ctx, in_port, table_id, may_packet_in,
> -                       honor_table_miss, resubmit->with_ct_orig);
> +                       honor_table_miss, resubmit->with_ct_orig,
> +                       is_last_action);
>   }
>   
>   static void
> -flood_packets(struct xlate_ctx *ctx, bool all)
> +flood_packet_to_port(struct xlate_ctx *ctx, const struct xport *xport,
> +                     bool all, bool is_last_action)
>   {
> -    const struct xport *xport;
> +    if (!xport) {
> +        return;
> +    }
> +
> +    if (all) {
> +        compose_output_action__(ctx, xport->ofp_port, NULL, false,
> +                                is_last_action);
> +    } else {
> +        compose_output_action(ctx, xport->ofp_port, NULL, is_last_action);
> +    } > +}
> +
> +static void
> +flood_packets(struct xlate_ctx *ctx, bool all, bool is_last_action)
> +{
> +    const struct xport *xport, *last = NULL;
>   
> +    /* Use 'last' the keep track of the last output port. */
>       HMAP_FOR_EACH (xport, ofp_node, &ctx->xbridge->xports) {
>           if (xport->ofp_port == ctx->xin->flow.in_port.ofp_port) {
>               continue;
>           }
>   
> -        if (all) {
> -            compose_output_action__(ctx, xport->ofp_port, NULL, false);
> -        } else if (!(xport->config & OFPUTIL_PC_NO_FLOOD)) {
> -            compose_output_action(ctx, xport->ofp_port, NULL);
> +        if (all || !(xport->config & OFPUTIL_PC_NO_FLOOD)) {
> +            /* 'last' is not the last port, send a packet out, and
> +             * update 'last'. */
> +            flood_packet_to_port(ctx, last, all, false);
> +            last = xport;
>           }
>       }
>   
> +    /* Send the packet to the 'last' port. */
> +    flood_packet_to_port(ctx, last, all, is_last_action);
>       ctx->nf_output_iface = NF_OUT_FLOOD;
>   }
>   
> @@ -4834,7 +4874,8 @@ compose_dec_mpls_ttl_action(struct xlate_ctx *ctx)
>   
>   static void
>   xlate_output_action(struct xlate_ctx *ctx,
> -                    ofp_port_t port, uint16_t max_len, bool may_packet_in)
> +                    ofp_port_t port, uint16_t max_len, bool may_packet_in,
> +                    bool is_last_action)
>   {
>       ofp_port_t prev_nf_output_iface = ctx->nf_output_iface;
>   
> @@ -4842,20 +4883,21 @@ xlate_output_action(struct xlate_ctx *ctx,
>   
>       switch (port) {
>       case OFPP_IN_PORT:
> -        compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port, NULL);
> +        compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port, NULL,
> +                              is_last_action);
>           break;
>       case OFPP_TABLE:
>           xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
> -                           0, may_packet_in, true, false);
> +                           0, may_packet_in, true, false, is_last_action);
>           break;
>       case OFPP_NORMAL:
>           xlate_normal(ctx);
>           break;
>       case OFPP_FLOOD:
> -        flood_packets(ctx,  false);
> +        flood_packets(ctx, false, is_last_action);
>           break;
>       case OFPP_ALL:
> -        flood_packets(ctx, true);
> +        flood_packets(ctx, true, is_last_action);
>           break;
>       case OFPP_CONTROLLER:
>           execute_controller_action(ctx, max_len,
> @@ -4870,7 +4912,7 @@ xlate_output_action(struct xlate_ctx *ctx,
>       case OFPP_LOCAL:
>       default:
>           if (port != ctx->xin->flow.in_port.ofp_port) {
> -            compose_output_action(ctx, port, NULL);
> +            compose_output_action(ctx, port, NULL, is_last_action);
>           } else {
>               xlate_report(ctx, OFT_WARN, "skipping output to input port");
>           }
> @@ -4889,7 +4931,8 @@ xlate_output_action(struct xlate_ctx *ctx,
>   
>   static void
>   xlate_output_reg_action(struct xlate_ctx *ctx,
> -                        const struct ofpact_output_reg *or)
> +                        const struct ofpact_output_reg *or,
> +                        bool is_last_action)
>   {
>       uint64_t port = mf_get_subfield(&or->src, &ctx->xin->flow);
>       if (port <= UINT16_MAX) {
> @@ -4899,7 +4942,8 @@ xlate_output_reg_action(struct xlate_ctx *ctx,
>   
>           memset(&value, 0xff, sizeof value);
>           mf_write_subfield_flow(&or->src, &value, &ctx->wc->masks);
> -        xlate_output_action(ctx, u16_to_ofp(port), or->max_len, false);
> +        xlate_output_action(ctx, u16_to_ofp(port), or->max_len, false,
> +                            is_last_action);
>       } else {
>           xlate_report(ctx, OFT_WARN, "output port %"PRIu64" is out of range",
>                        port);
> @@ -4908,7 +4952,8 @@ xlate_output_reg_action(struct xlate_ctx *ctx,
>   
>   static void
>   xlate_output_trunc_action(struct xlate_ctx *ctx,
> -                    ofp_port_t port, uint32_t max_len)
> +                    ofp_port_t port, uint32_t max_len,
> +                    bool is_last_action)
>   {
>       bool support_trunc = ctx->xbridge->support.trunc;
>       struct ovs_action_trunc *trunc;
> @@ -4945,7 +4990,7 @@ xlate_output_trunc_action(struct xlate_ctx *ctx,
>                                   OVS_ACTION_ATTR_TRUNC,
>                                   sizeof *trunc);
>               trunc->max_len = max_len;
> -            xlate_output_action(ctx, port, max_len, false);
> +            xlate_output_action(ctx, port, max_len, false, is_last_action);
>               if (!support_trunc) {
>                   ctx->xout->slow |= SLOW_ACTION;
>               }
> @@ -4958,7 +5003,8 @@ xlate_output_trunc_action(struct xlate_ctx *ctx,
>   
>   static void
>   xlate_enqueue_action(struct xlate_ctx *ctx,
> -                     const struct ofpact_enqueue *enqueue)
> +                     const struct ofpact_enqueue *enqueue,
> +                     bool is_last_action)
>   {
>       ofp_port_t ofp_port = enqueue->port;
>       uint32_t queue_id = enqueue->queue;
> @@ -4969,7 +5015,7 @@ xlate_enqueue_action(struct xlate_ctx *ctx,
>       error = dpif_queue_to_priority(ctx->xbridge->dpif, queue_id, &priority);
>       if (error) {
>           /* Fall back to ordinary output action. */
> -        xlate_output_action(ctx, enqueue->port, 0, false);
> +        xlate_output_action(ctx, enqueue->port, 0, false, is_last_action);
>           return;
>       }
>   
> @@ -4983,7 +5029,7 @@ xlate_enqueue_action(struct xlate_ctx *ctx,
>       /* Add datapath actions. */
>       flow_priority = ctx->xin->flow.skb_priority;
>       ctx->xin->flow.skb_priority = priority;
> -    compose_output_action(ctx, ofp_port, NULL);
> +    compose_output_action(ctx, ofp_port, NULL, is_last_action);
>       ctx->xin->flow.skb_priority = flow_priority;
>   
>       /* Update NetFlow output port. */
> @@ -5031,7 +5077,8 @@ slave_enabled_cb(ofp_port_t ofp_port, void *xbridge_)
>   
>   static void
>   xlate_bundle_action(struct xlate_ctx *ctx,
> -                    const struct ofpact_bundle *bundle)
> +                    const struct ofpact_bundle *bundle,
> +                    bool is_last_action)
>   {
>       ofp_port_t port;
>   
> @@ -5041,7 +5088,7 @@ xlate_bundle_action(struct xlate_ctx *ctx,
>           nxm_reg_load(&bundle->dst, ofp_to_u16(port), &ctx->xin->flow, ctx->wc);
>           xlate_report_subfield(ctx, &bundle->dst);
>       } else {
> -        xlate_output_action(ctx, port, 0, false);
> +        xlate_output_action(ctx, port, 0, false, is_last_action);
>       }
>   }
>   
> @@ -5335,7 +5382,7 @@ reversible_actions(const struct ofpact *ofpacts, size_t ofpacts_len)
>   
>   static void
>   clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
> -                    struct xlate_ctx *ctx)
> +                    struct xlate_ctx *ctx, bool is_last_action)
>   {
>       struct ofpbuf old_stack = ctx->stack;
>       union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)];
> @@ -5350,9 +5397,9 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
>       size_t offset, ac_offset;
>       struct flow old_flow = ctx->xin->flow;
>   
> -    if (reversible_actions(actions, actions_len)) {
> +    if (reversible_actions(actions, actions_len) || is_last_action) {
>           old_flow = ctx->xin->flow;
> -        do_xlate_actions(actions, actions_len, ctx);
> +        do_xlate_actions(actions, actions_len, ctx, is_last_action);
>           if (ctx->freezing) {
>               finish_freezing(ctx);
>           }
> @@ -5373,7 +5420,7 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
>       if (ctx->xbridge->support.clone) { /* Use clone action */
>           /* Use clone action as datapath clone. */
>           offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_CLONE);
> -        do_xlate_actions(actions, actions_len, ctx);
> +        do_xlate_actions(actions, actions_len, ctx, true);
>           if (ctx->freezing) {
>               finish_freezing(ctx);
>           }
> @@ -5386,7 +5433,7 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
>           offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_SAMPLE);
>           ac_offset = nl_msg_start_nested(ctx->odp_actions,
>                                           OVS_SAMPLE_ATTR_ACTIONS);
> -        do_xlate_actions(actions, actions_len, ctx);
> +        do_xlate_actions(actions, actions_len, ctx, true);
>           if (ctx->freezing) {
>               finish_freezing(ctx);
>           }
> @@ -5425,11 +5472,12 @@ xlate_done:
>   }
>   
>   static void
> -compose_clone(struct xlate_ctx *ctx, const struct ofpact_nest *oc)
> +compose_clone(struct xlate_ctx *ctx, const struct ofpact_nest *oc,
> +              bool is_last_action)
>   {
>       size_t oc_actions_len = ofpact_nest_get_action_len(oc);
>   
> -    clone_xlate_actions(oc->actions, oc_actions_len, ctx);
> +    clone_xlate_actions(oc->actions, oc_actions_len, ctx, is_last_action);
>   }
>   
>   static void
> @@ -5511,7 +5559,7 @@ xlate_action_set(struct xlate_ctx *ctx)
>           struct ovs_list *old_trace = ctx->xin->trace;
>           ctx->xin->trace = xlate_report(ctx, OFT_TABLE,
>                                          "--. Executing action set:");
> -        do_xlate_actions(action_list.data, action_list.size, ctx);
> +        do_xlate_actions(action_list.data, action_list.size, ctx, true);
>           ctx->xin->trace = old_trace;
>   
>           ctx->in_action_set = false;
> @@ -5735,7 +5783,8 @@ put_ct_nat(struct xlate_ctx *ctx)
>   }
>   
>   static void
> -compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
> +compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
> +                         bool is_last_action)
>   {
>       ovs_u128 old_ct_label_mask = ctx->wc->masks.ct_label;
>       uint32_t old_ct_mark_mask = ctx->wc->masks.ct_mark;
> @@ -5750,7 +5799,8 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
>       ctx->ct_nat_action = NULL;
>       ctx->wc->masks.ct_mark = 0;
>       ctx->wc->masks.ct_label = OVS_U128_ZERO;
> -    do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx);
> +    do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx,
> +                     is_last_action);
>   
>       if (ofc->zone_src.field) {
>           zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow);
> @@ -6146,7 +6196,7 @@ xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx,
>   
>   static void
>   do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
> -                 struct xlate_ctx *ctx)
> +                 struct xlate_ctx *ctx, bool is_last_action)
>   {
>       struct flow_wildcards *wc = ctx->wc;
>       struct flow *flow = &ctx->xin->flow;
> @@ -6167,6 +6217,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>           const struct ofpact_metadata *metadata;
>           const struct ofpact_set_field *set_field;
>           const struct mf_field *mf;
> +        bool last = is_last_action && ofpact_last(a, ofpacts, ofpacts_len)
> +                    && ctx->action_set.size;
>   
>           if (ctx->error) {
>               break;
> @@ -6194,11 +6246,11 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>           switch (a->type) {
>           case OFPACT_OUTPUT:
>               xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
> -                                ofpact_get_OUTPUT(a)->max_len, true);
> +                                ofpact_get_OUTPUT(a)->max_len, true, last);
>               break;
>   
>           case OFPACT_GROUP:
> -            if (xlate_group_action(ctx, ofpact_get_GROUP(a)->group_id)) {
> +            if (xlate_group_action(ctx, ofpact_get_GROUP(a)->group_id, last)) {
>                   /* Group could not be found. */
>   
>                   /* XXX: Terminates action list translation, but does not
> @@ -6227,7 +6279,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>           case OFPACT_ENQUEUE:
>               memset(&wc->masks.skb_priority, 0xff,
>                      sizeof wc->masks.skb_priority);
> -            xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a));
> +            xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a), last);
>               break;
>   
>           case OFPACT_SET_VLAN_VID:
> @@ -6339,7 +6391,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>                * to avoid that, only adding any actions that follow the resubmit
>                * to the frozen actions.
>                */
> -            xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
> +            xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a), last);
>               continue;
>   
>           case OFPACT_SET_TUNNEL:
> @@ -6437,16 +6489,16 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>               break;
>   
>           case OFPACT_BUNDLE:
> -            xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
> +            xlate_bundle_action(ctx, ofpact_get_BUNDLE(a), last);
>               break;
>   
>           case OFPACT_OUTPUT_REG:
> -            xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a));
> +            xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a), last);
>               break;
>   
>           case OFPACT_OUTPUT_TRUNC:
>               xlate_output_trunc_action(ctx, ofpact_get_OUTPUT_TRUNC(a)->port,
> -                                ofpact_get_OUTPUT_TRUNC(a)->max_len);
> +                                ofpact_get_OUTPUT_TRUNC(a)->max_len, last);
>               break;
>   
>           case OFPACT_LEARN:
> @@ -6502,7 +6554,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>               ovs_assert(ctx->table_id < ogt->table_id);
>   
>               xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
> -                               ogt->table_id, true, true, false);
> +                               ogt->table_id, true, true, false, last);
>               break;
>           }
>   
> @@ -6511,7 +6563,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>               break;
>   
>           case OFPACT_CLONE:
> -            compose_clone(ctx, ofpact_get_CLONE(a));
> +            compose_clone(ctx, ofpact_get_CLONE(a), last);
>               break;
>   
>           case OFPACT_ENCAP:
> @@ -6531,7 +6583,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>           }
>   
>           case OFPACT_CT:
> -            compose_conntrack_action(ctx, ofpact_get_CT(a));
> +            compose_conntrack_action(ctx, ofpact_get_CT(a), last);
>               break;
>   
>           case OFPACT_CT_CLEAR:
> @@ -7099,7 +7151,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
>               }
>   
>               mirror_ingress_packet(&ctx);
> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx);
> +            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true);
>               if (ctx.error) {
>                   goto exit;
>               }
> @@ -7127,7 +7179,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
>               && xbridge->has_in_band
>               && in_band_must_output_to_local_port(flow)
>               && !actions_output_to_local_port(&ctx)) {
> -            compose_output_action(&ctx, OFPP_LOCAL, NULL);
> +            compose_output_action(&ctx, OFPP_LOCAL, NULL, false);
>           }
>   
>           if (user_cookie_offset) {
> 

That's a large patch but I couldn't find any problems with it, so...

Tested-by: Greg Rose <gvrose8192@gmail.com>
Reviewed-by: Greg Rose <gvrose8192@gmail.com>

Patch

diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index ad8e1bec06ba..03c1d11dc9a2 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -216,6 +216,13 @@  ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len)
     return ALIGNED_CAST(struct ofpact *, (uint8_t *) ofpacts + ofpacts_len);
 }
 
+static inline bool
+ofpact_last(const struct ofpact *a, const struct ofpact *ofpacts,
+            size_t ofpact_len)
+{
+    return ofpact_next(a) == ofpact_end(ofpacts, ofpact_len);
+}
+
 static inline const struct ofpact *
 ofpact_find_type_flattened(const struct ofpact *a, enum ofpact_type type,
                            const struct ofpact * const end)
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 223313d4ecba..9e39a4ff7f49 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -507,11 +507,12 @@  static struct xlate_cfg *new_xcfg = NULL;
 
 static bool may_receive(const struct xport *, struct xlate_ctx *);
 static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
-                             struct xlate_ctx *);
+                             struct xlate_ctx *, bool);
 static void xlate_normal(struct xlate_ctx *);
 static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
                                uint8_t table_id, bool may_packet_in,
-                               bool honor_table_miss, bool with_ct_orig);
+                               bool honor_table_miss, bool with_ct_orig,
+                               bool is_last_action);
 static bool input_vid_is_valid(const struct xlate_ctx *,
                                uint16_t vid, struct xbundle *);
 static void xvlan_copy(struct xvlan *dst, const struct xvlan *src);
@@ -536,7 +537,8 @@  struct xlate_bond_recirc {
 };
 
 static void compose_output_action(struct xlate_ctx *, ofp_port_t ofp_port,
-                                  const struct xlate_bond_recirc *xr);
+                                  const struct xlate_bond_recirc *xr,
+                                  bool is_last_action);
 
 static struct xbridge *xbridge_lookup(struct xlate_cfg *,
                                       const struct ofproto_dpif *);
@@ -2218,7 +2220,8 @@  output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
     memcpy(&old_vlans, &ctx->xin->flow.vlans, sizeof(old_vlans));
     xvlan_put(&ctx->xin->flow, &out_xvlan);
 
-    compose_output_action(ctx, xport->ofp_port, use_recirc ? &xr : NULL);
+    compose_output_action(ctx, xport->ofp_port, use_recirc ? &xr : NULL,
+                          false);
     memcpy(&ctx->xin->flow.vlans, &old_vlans, sizeof(old_vlans));
 }
 
@@ -3557,7 +3560,7 @@  apply_nested_clone_actions(struct xlate_ctx *ctx, const struct xport *in_dev,
         if (xport_stp_forward_state(out_dev) &&
             xport_rstp_forward_state(out_dev)) {
             xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true,
-                               false);
+                               false, true);
             if (!ctx->freezing) {
                 xlate_action_set(ctx);
             }
@@ -3572,7 +3575,7 @@  apply_nested_clone_actions(struct xlate_ctx *ctx, const struct xport *in_dev,
             mirror_mask_t old_mirrors2 = ctx->mirrors;
 
             xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true,
-                               false);
+                               false, true);
             ctx->mirrors = old_mirrors2;
             ctx->base_flow = old_base_flow;
             ctx->odp_actions->size = old_size;
@@ -3716,7 +3719,8 @@  terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 
 static void
 compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
-                        const struct xlate_bond_recirc *xr, bool check_stp)
+                        const struct xlate_bond_recirc *xr, bool check_stp,
+                        bool is_last_action OVS_UNUSED)
 {
     const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
     struct flow_wildcards *wc = ctx->wc;
@@ -3882,13 +3886,15 @@  compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 
 static void
 compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port,
-                      const struct xlate_bond_recirc *xr)
+                      const struct xlate_bond_recirc *xr,
+                      bool is_last_action)
 {
-    compose_output_action__(ctx, ofp_port, xr, true);
+    compose_output_action__(ctx, ofp_port, xr, true, is_last_action);
 }
 
 static void
-xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule, bool deepens)
+xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule,
+                  bool deepens, bool is_last_action)
 {
     struct rule_dpif *old_rule = ctx->rule;
     ovs_be64 old_cookie = ctx->rule_cookie;
@@ -3904,7 +3910,8 @@  xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule, bool deepens)
     ctx->rule = rule;
     ctx->rule_cookie = rule->up.flow_cookie;
     actions = rule_get_actions(&rule->up);
-    do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx);
+    do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx,
+                     is_last_action);
     ctx->rule_cookie = old_cookie;
     ctx->rule = old_rule;
     ctx->depth -= deepens;
@@ -3979,7 +3986,7 @@  tuple_swap(struct flow *flow, struct flow_wildcards *wc)
 static void
 xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id,
                    bool may_packet_in, bool honor_table_miss,
-                   bool with_ct_orig)
+                   bool with_ct_orig, bool is_last_action)
 {
     /* Check if we need to recirculate before matching in a table. */
     if (ctx->was_mpls) {
@@ -4030,7 +4037,8 @@  xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id,
 
             struct ovs_list *old_trace = ctx->xin->trace;
             xlate_report_table(ctx, rule, table_id);
-            xlate_recursively(ctx, rule, table_id <= old_table_id);
+            xlate_recursively(ctx, rule, table_id <= old_table_id,
+                              is_last_action);
             ctx->xin->trace = old_trace;
         }
 
@@ -4057,7 +4065,8 @@  xlate_group_stats(struct xlate_ctx *ctx, struct group_dpif *group,
 }
 
 static void
-xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket)
+xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket,
+                   bool is_last_action)
 {
     uint64_t action_list_stub[1024 / 8];
     struct ofpbuf action_list = OFPBUF_STUB_INITIALIZER(action_list_stub);
@@ -4068,7 +4077,7 @@  xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket)
 
     ofpacts_execute_action_set(&action_list, &action_set);
     ctx->depth++;
-    do_xlate_actions(action_list.data, action_list.size, ctx);
+    do_xlate_actions(action_list.data, action_list.size, ctx, is_last_action);
     ctx->depth--;
 
     ofpbuf_uninit(&action_list);
@@ -4106,23 +4115,26 @@  xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket)
 }
 
 static void
-xlate_all_group(struct xlate_ctx *ctx, struct group_dpif *group)
+xlate_all_group(struct xlate_ctx *ctx, struct group_dpif *group,
+                bool is_last_action)
 {
     struct ofputil_bucket *bucket;
     LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
-        xlate_group_bucket(ctx, bucket);
+        bool last = is_last_action && !bucket->list_node.next;
+        xlate_group_bucket(ctx, bucket, last);
     }
     xlate_group_stats(ctx, group, NULL);
 }
 
 static void
-xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group)
+xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group,
+               bool is_last_action)
 {
     struct ofputil_bucket *bucket;
 
     bucket = group_first_live_bucket(ctx, group, 0);
     if (bucket) {
-        xlate_group_bucket(ctx, bucket);
+        xlate_group_bucket(ctx, bucket, is_last_action);
         xlate_group_stats(ctx, group, bucket);
     } else if (ctx->xin->xcache) {
         ofproto_group_unref(&group->up);
@@ -4130,7 +4142,8 @@  xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group)
 }
 
 static void
-xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
+xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group,
+                           bool is_last_action)
 {
     struct flow_wildcards *wc = ctx->wc;
     struct ofputil_bucket *bucket;
@@ -4140,7 +4153,7 @@  xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
     flow_mask_hash_fields(&ctx->xin->flow, wc, NX_HASH_FIELDS_SYMMETRIC_L4);
     bucket = group_best_live_bucket(ctx, group, basis);
     if (bucket) {
-        xlate_group_bucket(ctx, bucket);
+        xlate_group_bucket(ctx, bucket, is_last_action);
         xlate_group_stats(ctx, group, bucket);
     } else if (ctx->xin->xcache) {
         ofproto_group_unref(&group->up);
@@ -4148,7 +4161,8 @@  xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
 }
 
 static void
-xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
+xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group,
+                               bool is_last_action)
 {
     const struct field_array *fields = &group->up.props.fields;
     const uint8_t *mask_values = fields->values;
@@ -4186,7 +4200,7 @@  xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
 
     struct ofputil_bucket *bucket = group_best_live_bucket(ctx, group, basis);
     if (bucket) {
-        xlate_group_bucket(ctx, bucket);
+        xlate_group_bucket(ctx, bucket, is_last_action);
         xlate_group_stats(ctx, group, bucket);
     } else if (ctx->xin->xcache) {
         ofproto_group_unref(&group->up);
@@ -4194,7 +4208,8 @@  xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
 }
 
 static void
-xlate_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
+xlate_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group,
+                           bool is_last_action)
 {
     struct ofputil_bucket *bucket;
 
@@ -4218,7 +4233,7 @@  xlate_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
             ctx->wc->masks.dp_hash |= mask;
             bucket = group_best_live_bucket(ctx, group, basis);
             if (bucket) {
-                xlate_group_bucket(ctx, bucket);
+                xlate_group_bucket(ctx, bucket, is_last_action);
                 xlate_group_stats(ctx, group, bucket);
             }
         }
@@ -4226,7 +4241,8 @@  xlate_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
 }
 
 static void
-xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
+xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group,
+                   bool is_last_action)
 {
     const char *selection_method = group->up.props.selection_method;
 
@@ -4238,11 +4254,11 @@  xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
     }
 
     if (selection_method[0] == '\0') {
-        xlate_default_select_group(ctx, group);
+        xlate_default_select_group(ctx, group, is_last_action);
     } else if (!strcasecmp("hash", selection_method)) {
-        xlate_hash_fields_select_group(ctx, group);
+        xlate_hash_fields_select_group(ctx, group, is_last_action);
     } else if (!strcasecmp("dp_hash", selection_method)) {
-        xlate_dp_hash_select_group(ctx, group);
+        xlate_dp_hash_select_group(ctx, group, is_last_action);
     } else {
         /* Parsing of groups should ensure this never happens */
         OVS_NOT_REACHED();
@@ -4250,7 +4266,8 @@  xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
 }
 
 static void
-xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group)
+xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group,
+                     bool is_last_action)
 {
     bool was_in_group = ctx->in_group;
     ctx->in_group = true;
@@ -4258,13 +4275,13 @@  xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group)
     switch (group->up.type) {
     case OFPGT11_ALL:
     case OFPGT11_INDIRECT:
-        xlate_all_group(ctx, group);
+        xlate_all_group(ctx, group, is_last_action);
         break;
     case OFPGT11_SELECT:
-        xlate_select_group(ctx, group);
+        xlate_select_group(ctx, group, is_last_action);
         break;
     case OFPGT11_FF:
-        xlate_ff_group(ctx, group);
+        xlate_ff_group(ctx, group, is_last_action);
         break;
     default:
         OVS_NOT_REACHED();
@@ -4274,7 +4291,8 @@  xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group)
 }
 
 static bool
-xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id)
+xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id,
+                   bool is_last_action)
 {
     if (xlate_resubmit_resource_check(ctx)) {
         struct group_dpif *group;
@@ -4288,7 +4306,7 @@  xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id)
                          group_id);
             return true;
         }
-        xlate_group_action__(ctx, group);
+        xlate_group_action__(ctx, group, is_last_action);
     }
 
     return false;
@@ -4296,7 +4314,8 @@  xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id)
 
 static void
 xlate_ofpact_resubmit(struct xlate_ctx *ctx,
-                      const struct ofpact_resubmit *resubmit)
+                      const struct ofpact_resubmit *resubmit,
+                      bool is_last_action)
 {
     ofp_port_t in_port;
     uint8_t table_id;
@@ -4321,26 +4340,47 @@  xlate_ofpact_resubmit(struct xlate_ctx *ctx,
     }
 
     xlate_table_action(ctx, in_port, table_id, may_packet_in,
-                       honor_table_miss, resubmit->with_ct_orig);
+                       honor_table_miss, resubmit->with_ct_orig,
+                       is_last_action);
 }
 
 static void
-flood_packets(struct xlate_ctx *ctx, bool all)
+flood_packet_to_port(struct xlate_ctx *ctx, const struct xport *xport,
+                     bool all, bool is_last_action)
 {
-    const struct xport *xport;
+    if (!xport) {
+        return;
+    }
+
+    if (all) {
+        compose_output_action__(ctx, xport->ofp_port, NULL, false,
+                                is_last_action);
+    } else {
+        compose_output_action(ctx, xport->ofp_port, NULL, is_last_action);
+    }
+}
+
+static void
+flood_packets(struct xlate_ctx *ctx, bool all, bool is_last_action)
+{
+    const struct xport *xport, *last = NULL;
 
+    /* Use 'last' the keep track of the last output port. */
     HMAP_FOR_EACH (xport, ofp_node, &ctx->xbridge->xports) {
         if (xport->ofp_port == ctx->xin->flow.in_port.ofp_port) {
             continue;
         }
 
-        if (all) {
-            compose_output_action__(ctx, xport->ofp_port, NULL, false);
-        } else if (!(xport->config & OFPUTIL_PC_NO_FLOOD)) {
-            compose_output_action(ctx, xport->ofp_port, NULL);
+        if (all || !(xport->config & OFPUTIL_PC_NO_FLOOD)) {
+            /* 'last' is not the last port, send a packet out, and
+             * update 'last'. */
+            flood_packet_to_port(ctx, last, all, false);
+            last = xport;
         }
     }
 
+    /* Send the packet to the 'last' port. */
+    flood_packet_to_port(ctx, last, all, is_last_action);
     ctx->nf_output_iface = NF_OUT_FLOOD;
 }
 
@@ -4834,7 +4874,8 @@  compose_dec_mpls_ttl_action(struct xlate_ctx *ctx)
 
 static void
 xlate_output_action(struct xlate_ctx *ctx,
-                    ofp_port_t port, uint16_t max_len, bool may_packet_in)
+                    ofp_port_t port, uint16_t max_len, bool may_packet_in,
+                    bool is_last_action)
 {
     ofp_port_t prev_nf_output_iface = ctx->nf_output_iface;
 
@@ -4842,20 +4883,21 @@  xlate_output_action(struct xlate_ctx *ctx,
 
     switch (port) {
     case OFPP_IN_PORT:
-        compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port, NULL);
+        compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port, NULL,
+                              is_last_action);
         break;
     case OFPP_TABLE:
         xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
-                           0, may_packet_in, true, false);
+                           0, may_packet_in, true, false, is_last_action);
         break;
     case OFPP_NORMAL:
         xlate_normal(ctx);
         break;
     case OFPP_FLOOD:
-        flood_packets(ctx,  false);
+        flood_packets(ctx, false, is_last_action);
         break;
     case OFPP_ALL:
-        flood_packets(ctx, true);
+        flood_packets(ctx, true, is_last_action);
         break;
     case OFPP_CONTROLLER:
         execute_controller_action(ctx, max_len,
@@ -4870,7 +4912,7 @@  xlate_output_action(struct xlate_ctx *ctx,
     case OFPP_LOCAL:
     default:
         if (port != ctx->xin->flow.in_port.ofp_port) {
-            compose_output_action(ctx, port, NULL);
+            compose_output_action(ctx, port, NULL, is_last_action);
         } else {
             xlate_report(ctx, OFT_WARN, "skipping output to input port");
         }
@@ -4889,7 +4931,8 @@  xlate_output_action(struct xlate_ctx *ctx,
 
 static void
 xlate_output_reg_action(struct xlate_ctx *ctx,
-                        const struct ofpact_output_reg *or)
+                        const struct ofpact_output_reg *or,
+                        bool is_last_action)
 {
     uint64_t port = mf_get_subfield(&or->src, &ctx->xin->flow);
     if (port <= UINT16_MAX) {
@@ -4899,7 +4942,8 @@  xlate_output_reg_action(struct xlate_ctx *ctx,
 
         memset(&value, 0xff, sizeof value);
         mf_write_subfield_flow(&or->src, &value, &ctx->wc->masks);
-        xlate_output_action(ctx, u16_to_ofp(port), or->max_len, false);
+        xlate_output_action(ctx, u16_to_ofp(port), or->max_len, false,
+                            is_last_action);
     } else {
         xlate_report(ctx, OFT_WARN, "output port %"PRIu64" is out of range",
                      port);
@@ -4908,7 +4952,8 @@  xlate_output_reg_action(struct xlate_ctx *ctx,
 
 static void
 xlate_output_trunc_action(struct xlate_ctx *ctx,
-                    ofp_port_t port, uint32_t max_len)
+                    ofp_port_t port, uint32_t max_len,
+                    bool is_last_action)
 {
     bool support_trunc = ctx->xbridge->support.trunc;
     struct ovs_action_trunc *trunc;
@@ -4945,7 +4990,7 @@  xlate_output_trunc_action(struct xlate_ctx *ctx,
                                 OVS_ACTION_ATTR_TRUNC,
                                 sizeof *trunc);
             trunc->max_len = max_len;
-            xlate_output_action(ctx, port, max_len, false);
+            xlate_output_action(ctx, port, max_len, false, is_last_action);
             if (!support_trunc) {
                 ctx->xout->slow |= SLOW_ACTION;
             }
@@ -4958,7 +5003,8 @@  xlate_output_trunc_action(struct xlate_ctx *ctx,
 
 static void
 xlate_enqueue_action(struct xlate_ctx *ctx,
-                     const struct ofpact_enqueue *enqueue)
+                     const struct ofpact_enqueue *enqueue,
+                     bool is_last_action)
 {
     ofp_port_t ofp_port = enqueue->port;
     uint32_t queue_id = enqueue->queue;
@@ -4969,7 +5015,7 @@  xlate_enqueue_action(struct xlate_ctx *ctx,
     error = dpif_queue_to_priority(ctx->xbridge->dpif, queue_id, &priority);
     if (error) {
         /* Fall back to ordinary output action. */
-        xlate_output_action(ctx, enqueue->port, 0, false);
+        xlate_output_action(ctx, enqueue->port, 0, false, is_last_action);
         return;
     }
 
@@ -4983,7 +5029,7 @@  xlate_enqueue_action(struct xlate_ctx *ctx,
     /* Add datapath actions. */
     flow_priority = ctx->xin->flow.skb_priority;
     ctx->xin->flow.skb_priority = priority;
-    compose_output_action(ctx, ofp_port, NULL);
+    compose_output_action(ctx, ofp_port, NULL, is_last_action);
     ctx->xin->flow.skb_priority = flow_priority;
 
     /* Update NetFlow output port. */
@@ -5031,7 +5077,8 @@  slave_enabled_cb(ofp_port_t ofp_port, void *xbridge_)
 
 static void
 xlate_bundle_action(struct xlate_ctx *ctx,
-                    const struct ofpact_bundle *bundle)
+                    const struct ofpact_bundle *bundle,
+                    bool is_last_action)
 {
     ofp_port_t port;
 
@@ -5041,7 +5088,7 @@  xlate_bundle_action(struct xlate_ctx *ctx,
         nxm_reg_load(&bundle->dst, ofp_to_u16(port), &ctx->xin->flow, ctx->wc);
         xlate_report_subfield(ctx, &bundle->dst);
     } else {
-        xlate_output_action(ctx, port, 0, false);
+        xlate_output_action(ctx, port, 0, false, is_last_action);
     }
 }
 
@@ -5335,7 +5382,7 @@  reversible_actions(const struct ofpact *ofpacts, size_t ofpacts_len)
 
 static void
 clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
-                    struct xlate_ctx *ctx)
+                    struct xlate_ctx *ctx, bool is_last_action)
 {
     struct ofpbuf old_stack = ctx->stack;
     union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)];
@@ -5350,9 +5397,9 @@  clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
     size_t offset, ac_offset;
     struct flow old_flow = ctx->xin->flow;
 
-    if (reversible_actions(actions, actions_len)) {
+    if (reversible_actions(actions, actions_len) || is_last_action) {
         old_flow = ctx->xin->flow;
-        do_xlate_actions(actions, actions_len, ctx);
+        do_xlate_actions(actions, actions_len, ctx, is_last_action);
         if (ctx->freezing) {
             finish_freezing(ctx);
         }
@@ -5373,7 +5420,7 @@  clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
     if (ctx->xbridge->support.clone) { /* Use clone action */
         /* Use clone action as datapath clone. */
         offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_CLONE);
-        do_xlate_actions(actions, actions_len, ctx);
+        do_xlate_actions(actions, actions_len, ctx, true);
         if (ctx->freezing) {
             finish_freezing(ctx);
         }
@@ -5386,7 +5433,7 @@  clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
         offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_SAMPLE);
         ac_offset = nl_msg_start_nested(ctx->odp_actions,
                                         OVS_SAMPLE_ATTR_ACTIONS);
-        do_xlate_actions(actions, actions_len, ctx);
+        do_xlate_actions(actions, actions_len, ctx, true);
         if (ctx->freezing) {
             finish_freezing(ctx);
         }
@@ -5425,11 +5472,12 @@  xlate_done:
 }
 
 static void
-compose_clone(struct xlate_ctx *ctx, const struct ofpact_nest *oc)
+compose_clone(struct xlate_ctx *ctx, const struct ofpact_nest *oc,
+              bool is_last_action)
 {
     size_t oc_actions_len = ofpact_nest_get_action_len(oc);
 
-    clone_xlate_actions(oc->actions, oc_actions_len, ctx);
+    clone_xlate_actions(oc->actions, oc_actions_len, ctx, is_last_action);
 }
 
 static void
@@ -5511,7 +5559,7 @@  xlate_action_set(struct xlate_ctx *ctx)
         struct ovs_list *old_trace = ctx->xin->trace;
         ctx->xin->trace = xlate_report(ctx, OFT_TABLE,
                                        "--. Executing action set:");
-        do_xlate_actions(action_list.data, action_list.size, ctx);
+        do_xlate_actions(action_list.data, action_list.size, ctx, true);
         ctx->xin->trace = old_trace;
 
         ctx->in_action_set = false;
@@ -5735,7 +5783,8 @@  put_ct_nat(struct xlate_ctx *ctx)
 }
 
 static void
-compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
+compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
+                         bool is_last_action)
 {
     ovs_u128 old_ct_label_mask = ctx->wc->masks.ct_label;
     uint32_t old_ct_mark_mask = ctx->wc->masks.ct_mark;
@@ -5750,7 +5799,8 @@  compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
     ctx->ct_nat_action = NULL;
     ctx->wc->masks.ct_mark = 0;
     ctx->wc->masks.ct_label = OVS_U128_ZERO;
-    do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx);
+    do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx,
+                     is_last_action);
 
     if (ofc->zone_src.field) {
         zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow);
@@ -6146,7 +6196,7 @@  xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx,
 
 static void
 do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
-                 struct xlate_ctx *ctx)
+                 struct xlate_ctx *ctx, bool is_last_action)
 {
     struct flow_wildcards *wc = ctx->wc;
     struct flow *flow = &ctx->xin->flow;
@@ -6167,6 +6217,8 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         const struct ofpact_metadata *metadata;
         const struct ofpact_set_field *set_field;
         const struct mf_field *mf;
+        bool last = is_last_action && ofpact_last(a, ofpacts, ofpacts_len)
+                    && ctx->action_set.size;
 
         if (ctx->error) {
             break;
@@ -6194,11 +6246,11 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         switch (a->type) {
         case OFPACT_OUTPUT:
             xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
-                                ofpact_get_OUTPUT(a)->max_len, true);
+                                ofpact_get_OUTPUT(a)->max_len, true, last);
             break;
 
         case OFPACT_GROUP:
-            if (xlate_group_action(ctx, ofpact_get_GROUP(a)->group_id)) {
+            if (xlate_group_action(ctx, ofpact_get_GROUP(a)->group_id, last)) {
                 /* Group could not be found. */
 
                 /* XXX: Terminates action list translation, but does not
@@ -6227,7 +6279,7 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         case OFPACT_ENQUEUE:
             memset(&wc->masks.skb_priority, 0xff,
                    sizeof wc->masks.skb_priority);
-            xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a));
+            xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a), last);
             break;
 
         case OFPACT_SET_VLAN_VID:
@@ -6339,7 +6391,7 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
              * to avoid that, only adding any actions that follow the resubmit
              * to the frozen actions.
              */
-            xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
+            xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a), last);
             continue;
 
         case OFPACT_SET_TUNNEL:
@@ -6437,16 +6489,16 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_BUNDLE:
-            xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
+            xlate_bundle_action(ctx, ofpact_get_BUNDLE(a), last);
             break;
 
         case OFPACT_OUTPUT_REG:
-            xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a));
+            xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a), last);
             break;
 
         case OFPACT_OUTPUT_TRUNC:
             xlate_output_trunc_action(ctx, ofpact_get_OUTPUT_TRUNC(a)->port,
-                                ofpact_get_OUTPUT_TRUNC(a)->max_len);
+                                ofpact_get_OUTPUT_TRUNC(a)->max_len, last);
             break;
 
         case OFPACT_LEARN:
@@ -6502,7 +6554,7 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             ovs_assert(ctx->table_id < ogt->table_id);
 
             xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
-                               ogt->table_id, true, true, false);
+                               ogt->table_id, true, true, false, last);
             break;
         }
 
@@ -6511,7 +6563,7 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_CLONE:
-            compose_clone(ctx, ofpact_get_CLONE(a));
+            compose_clone(ctx, ofpact_get_CLONE(a), last);
             break;
 
         case OFPACT_ENCAP:
@@ -6531,7 +6583,7 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         }
 
         case OFPACT_CT:
-            compose_conntrack_action(ctx, ofpact_get_CT(a));
+            compose_conntrack_action(ctx, ofpact_get_CT(a), last);
             break;
 
         case OFPACT_CT_CLEAR:
@@ -7099,7 +7151,7 @@  xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
             }
 
             mirror_ingress_packet(&ctx);
-            do_xlate_actions(ofpacts, ofpacts_len, &ctx);
+            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true);
             if (ctx.error) {
                 goto exit;
             }
@@ -7127,7 +7179,7 @@  xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
             && xbridge->has_in_band
             && in_band_must_output_to_local_port(flow)
             && !actions_output_to_local_port(&ctx)) {
-            compose_output_action(&ctx, OFPP_LOCAL, NULL);
+            compose_output_action(&ctx, OFPP_LOCAL, NULL, false);
         }
 
         if (user_cookie_offset) {