From: Sugesh Chandran <sugesh.chand...@intel.com>

Openvswitch datapath recirculates packets for tunneling, i.e.
the incoming packets are encapsulated at first pass. Further actions are
applied on encapsulated packets on the second pass after recirculating.
The proposed patch compute and append the post tunnel actions at the time of
translation itself instead of recirculating at datapath. These actions are 
solely
depends on tunnel attributes so there is no need of datapath recirculation.
By avoiding the recirculation at datapath, the patch offers upto 30%
performance improvement for VxLAN tunneling in our testing.
The action execution logic is also extended with new CLONE action to define
the packet cloning when the actions are combined. The lenght in the CLONE
action specifies the size of nested action set.

Signed-off-by: Sugesh Chandran <sugesh.chand...@intel.com>
Signed-off-by: Zoltán Balogh <zoltan.bal...@ericsson.com>
Co-authored-by: Zoltán Balogh <zoltan.bal...@ericsson.com>
---
 datapath/linux/compat/include/linux/openvswitch.h |   1 +
 lib/dpif-netdev.c                                 |  20 +-
 lib/dpif.c                                        |   1 +
 lib/odp-execute.c                                 |  57 ++++-
 lib/odp-util.c                                    |  96 +++++++-
 lib/odp-util.h                                    |   5 +
 ofproto/ofproto-dpif-sflow.c                      |   1 +
 ofproto/ofproto-dpif-xlate.c                      | 267 +++++++++++-----------
 8 files changed, 288 insertions(+), 160 deletions(-)

diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
b/datapath/linux/compat/include/linux/openvswitch.h
index 12260d8..91849d6 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -818,6 +818,7 @@ enum ovs_action_attr {
 #ifndef __KERNEL__
        OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
        OVS_ACTION_ATTR_TUNNEL_POP,    /* u32 port number. */
+       OVS_ACTION_ATTR_CLONE,
 #endif
        __OVS_ACTION_ATTR_MAX,        /* Nothing past this will be accepted
                                       * from userspace. */
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 3901129..0b85fe4 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -4547,24 +4547,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
*packets_,
 
     case OVS_ACTION_ATTR_TUNNEL_PUSH:
         if (*depth < MAX_RECIRC_DEPTH) {
-            struct dp_packet_batch tnl_pkt;
-            struct dp_packet_batch *orig_packets_ = packets_;
-            int err;
-
-            if (!may_steal) {
-                dp_packet_batch_clone(&tnl_pkt, packets_);
-                packets_ = &tnl_pkt;
-                dp_packet_batch_reset_cutlen(orig_packets_);
-            }
-
             dp_packet_batch_apply_cutlen(packets_);
-
-            err = push_tnl_action(pmd, a, packets_);
-            if (!err) {
-                (*depth)++;
-                dp_netdev_recirculate(pmd, packets_);
-                (*depth)--;
-            }
+            push_tnl_action(pmd, a, packets_);
             return;
         }
         break;
@@ -4714,6 +4698,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
*packets_,
         break;
     }
 
+    case OVS_ACTION_ATTR_CLONE:
+        break;
     case OVS_ACTION_ATTR_PUSH_VLAN:
     case OVS_ACTION_ATTR_POP_VLAN:
     case OVS_ACTION_ATTR_PUSH_MPLS:
diff --git a/lib/dpif.c b/lib/dpif.c
index 50c3382..b698da2 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1183,6 +1183,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch 
*packets_,
     case OVS_ACTION_ATTR_SAMPLE:
     case OVS_ACTION_ATTR_TRUNC:
     case OVS_ACTION_ATTR_UNSPEC:
+    case OVS_ACTION_ATTR_CLONE:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 73e1016..77bca94 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -541,6 +541,7 @@ requires_datapath_assistance(const struct nlattr *a)
     case OVS_ACTION_ATTR_USERSPACE:
     case OVS_ACTION_ATTR_RECIRC:
     case OVS_ACTION_ATTR_CT:
+    case OVS_ACTION_ATTR_CLONE:
         return true;
 
     case OVS_ACTION_ATTR_SET:
@@ -562,6 +563,29 @@ requires_datapath_assistance(const struct nlattr *a)
     return false;
 }
 
+static inline size_t
+find_combine_action_end(const struct nlattr **actions_, size_t *actions_len,
+                        const struct nlattr **comb_start,
+                        bool *comb_last_action)
+{
+    const struct nlattr *a;
+    const struct nlattr *actions = *actions_;
+    unsigned int left;
+    bool last_action;
+    size_t comb_size = nl_attr_get_u32(actions); /* Size of combined actions */
+    *actions_len -= NLA_ALIGN(actions->nla_len);
+    actions = nl_attr_next(actions);
+
+    *comb_start = actions;
+    a = (void *) ((uint8_t *) actions + NLA_ALIGN(comb_size));
+    left = (*actions_len) - comb_size;
+    last_action = (left <= NLA_ALIGN(a->nla_len));
+    *actions_ = a;
+    *comb_last_action = last_action ? true : false;
+    *actions_len = left;
+    return comb_size;
+}
+
 void
 odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
                     const struct nlattr *actions, size_t actions_len,
@@ -570,7 +594,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch 
*batch, bool steal,
     struct dp_packet **packets = batch->packets;
     int cnt = batch->count;
     const struct nlattr *a;
-    unsigned int left;
+    size_t left;
     int i;
 
     NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
@@ -582,7 +606,35 @@ odp_execute_actions(void *dp, struct dp_packet_batch 
*batch, bool steal,
                 /* Allow 'dp_execute_action' to steal the packet data if we do
                  * not need it any more. */
                 bool may_steal = steal && last_action;
-
+                if ((enum ovs_action_attr) type ==
+                                           OVS_ACTION_ATTR_CLONE) {
+                    size_t comb_action_len;
+                    const struct nlattr *comb_actions = NULL;
+                    bool comb_last_action = true;
+                    comb_action_len = find_combine_action_end(&a, &left,
+                                                            &comb_actions,
+                                                            &comb_last_action);
+                    if (OVS_UNLIKELY(!comb_action_len)) {
+                        /* No combine present, execute current action */
+                        goto execute;
+                    }
+                    if (OVS_LIKELY(comb_last_action)) {
+                        odp_execute_actions(dp, batch, 1, comb_actions,
+                                        comb_action_len, dp_execute_action);
+                        return;
+                    }
+                    else  {
+                        /* Packets to be duplicated for the sub action */
+                        struct dp_packet_batch comb_pkt_batch;
+                        dp_packet_batch_clone(&comb_pkt_batch, batch);
+                        dp_packet_batch_reset_cutlen(batch);
+                        odp_execute_actions(dp, &comb_pkt_batch, steal,
+                                        comb_actions, comb_action_len,
+                                        dp_execute_action);
+                    }
+                    continue;
+                }
+execute:
                 dp_execute_action(dp, batch, a, may_steal);
 
                 if (last_action) {
@@ -692,6 +744,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch 
*batch, bool steal,
         case OVS_ACTION_ATTR_RECIRC:
         case OVS_ACTION_ATTR_CT:
         case OVS_ACTION_ATTR_UNSPEC:
+        case OVS_ACTION_ATTR_CLONE:
         case __OVS_ACTION_ATTR_MAX:
             OVS_NOT_REACHED();
         }
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 1e70e3a..4a311d9 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -121,6 +121,7 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_SET_MASKED: return ATTR_LEN_VARIABLE;
     case OVS_ACTION_ATTR_SAMPLE: return ATTR_LEN_VARIABLE;
     case OVS_ACTION_ATTR_CT: return ATTR_LEN_VARIABLE;
+    case OVS_ACTION_ATTR_CLONE: return sizeof(uint32_t);
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -509,7 +510,7 @@ format_odp_tnl_push_header(struct ds *ds, struct 
ovs_action_push_tnl *data)
                       gnh->oam ? "oam," : "",
                       gnh->critical ? "crit," : "",
                       ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
- 
+
         if (gnh->opt_len) {
             ds_put_cstr(ds, ",options(");
             format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
@@ -865,6 +866,14 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     case OVS_ACTION_ATTR_CT:
         format_odp_conntrack_action(ds, a);
         break;
+    case OVS_ACTION_ATTR_CLONE: {
+        uint32_t comb_len;
+        comb_len = nl_attr_get_u32(a);
+        if (comb_len) {
+            ds_put_cstr(ds,"{");
+        }
+        break;
+    }
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
     default:
@@ -873,27 +882,81 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     }
 }
 
+static void
+print_odp_actions(struct ds *ds, bool is_first_action,
+                                const struct nlattr **action_,
+                                size_t *action_len, size_t clone_len, bool 
is_clone_ctx)
+{
+
+    enum ovs_action_attr type;
+    const struct nlattr *action = *action_;
+    const struct nlattr *curr_action = action;
+    size_t curr_action_len;
+
+    if (!nl_attr_is_valid(action, *action_len)) {
+        return;
+    }
+    curr_action_len = nl_attr_len_pad(action, *action_len);
+    type = nl_attr_type(action);
+    if(!is_first_action) {
+        ds_put_char(ds, ',');
+    }
+    else {
+        /* Next iteration onwards, its not first action anymore. Reset flag
+         * accordingly.
+         */
+        is_first_action = 0;
+    }
+
+    format_odp_action(ds, action);
+
+    action = nl_attr_next(action);
+    *action_ = action;
+    *action_len -= curr_action_len;
+    if (type ==  OVS_ACTION_ATTR_CLONE) {
+           uint32_t comb_len;
+           comb_len = nl_attr_get_u32(curr_action);
+           if (comb_len) {
+               print_odp_actions(ds, 1, action_, action_len,
+                                           comb_len, true);
+               ds_put_char(ds, '}');
+           }
+           else {
+               return;/* Error, cannot print anything. */
+           }
+    }
+    if (is_clone_ctx) {
+        if(!clone_len) {
+            return;
+        }
+        else {
+            clone_len = (clone_len >= curr_action_len) ?
+                                clone_len - curr_action_len :
+                                0;
+        }
+    }
+    print_odp_actions(ds, is_first_action, action_, action_len, clone_len,
+                                is_clone_ctx);
+}
+
 void
 format_odp_actions(struct ds *ds, const struct nlattr *actions,
                    size_t actions_len)
 {
     if (actions_len) {
         const struct nlattr *a;
-        unsigned int left;
+        size_t left;
 
-        NL_ATTR_FOR_EACH (a, left, actions, actions_len) {
-            if (a != actions) {
-                ds_put_char(ds, ',');
-            }
-            format_odp_action(ds, a);
-        }
+        a = actions;
+        left = actions_len;
+        print_odp_actions(ds, 1, &a, &left, 0, false);
         if (left) {
             int i;
 
             if (left == actions_len) {
                 ds_put_cstr(ds, "<empty>");
             }
-            ds_put_format(ds, ",***%u leftover bytes*** (", left);
+            ds_put_format(ds, ",***%"PRIuSIZE" leftover bytes*** (", left);
             for (i = 0; i < left; i++) {
                 ds_put_format(ds, "%02x", ((const uint8_t *) a)[i]);
             }
@@ -5321,6 +5384,21 @@ odp_put_tunnel_action(const struct flow_tnl *tunnel,
 }
 
 void
+odp_put_combine_start_action(struct ofpbuf *odp_actions, uint32_t action_len)
+{
+    nl_msg_put_u32(odp_actions, OVS_ACTION_ATTR_CLONE, action_len);
+}
+
+void
+odp_update_combine_start_action(struct nlattr *start_combine,
+                                uint32_t combine_size)
+{
+    uint32_t *combine_len;
+    combine_len = (uint32_t *)(start_combine + 1);
+    *combine_len = combine_size;
+}
+
+void
 odp_put_tnl_push_action(struct ofpbuf *odp_actions,
                         struct ovs_action_push_tnl *data)
 {
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 42011bc..a32dc76 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -308,4 +308,9 @@ void odp_put_tunnel_action(const struct flow_tnl *tunnel,
 
 void odp_put_tnl_push_action(struct ofpbuf *odp_actions,
                              struct ovs_action_push_tnl *data);
+void odp_put_combine_start_action(struct ofpbuf *odp_actions,
+                                  uint32_t action_len);
+void
+odp_update_combine_start_action(struct nlattr *start_combine,
+                                uint32_t combine_size);
 #endif /* odp-util.h */
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 37992b4..8d51374 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1163,6 +1163,7 @@ dpif_sflow_read_actions(const struct flow *flow,
        }
        case OVS_ACTION_ATTR_SAMPLE:
        case OVS_ACTION_ATTR_UNSPEC:
+    case OVS_ACTION_ATTR_CLONE:
        case __OVS_ACTION_ATTR_MAX:
        default:
            break;
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 9a15ec3..ded0932 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -408,6 +408,10 @@ static void xlate_action_set(struct xlate_ctx *ctx);
 static void xlate_commit_actions(struct xlate_ctx *ctx);
 
 static void
+group_actions(struct xlate_ctx *ctx, const struct xport *in_dev,
+              struct xport *out_dev);
+
+static void
 ctx_trigger_freeze(struct xlate_ctx *ctx)
 {
     ctx->exit = true;
@@ -3029,7 +3033,23 @@ build_tunnel_send(struct xlate_ctx *ctx, const struct 
xport *xport,
     }
     tnl_push_data.tnl_port = odp_to_u32(tunnel_odp_port);
     tnl_push_data.out_port = odp_to_u32(out_dev->odp_port);
+
+    struct nlattr *start_combine;
+    uint32_t action_size = 0;
+    uint32_t start_action_size = 0;
+    uint32_t push_action_size = 0;
+    start_action_size = ctx->odp_actions->size;
+    odp_put_combine_start_action(ctx->odp_actions, 0);
+    action_size = ctx->odp_actions->size;
     odp_put_tnl_push_action(ctx->odp_actions, &tnl_push_data);
+    push_action_size = ctx->odp_actions->size;
+    group_actions(ctx, xport, out_dev);
+    start_combine = ofpbuf_at(ctx->odp_actions, start_action_size, 
(NLA_HDRLEN));
+    if (ctx->odp_actions->size > push_action_size) {
+        /* Update the combine action only when combined */
+        odp_update_combine_start_action(start_combine,
+                                    (ctx->odp_actions->size - action_size));
+    }
     return 0;
 }
 
@@ -3071,6 +3091,119 @@ xlate_flow_is_protected(const struct xlate_ctx *ctx, 
const struct flow *flow, co
 }
 
 static void
+group_actions(struct xlate_ctx *ctx, const struct xport *in_dev,
+              struct xport *out_dev)
+{
+    struct flow *flow = &ctx->xin->flow;
+    struct flow old_flow = ctx->xin->flow;
+    struct flow_tnl old_flow_tnl_wc = ctx->wc->masks.tunnel;
+    bool old_conntrack = ctx->conntracked;
+    bool old_was_mpls = ctx->was_mpls;
+    ovs_version_t old_version = ctx->xin->tables_version;
+    struct ofpbuf old_stack = ctx->stack;
+    union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)];
+    struct ofpbuf old_action_set = ctx->action_set;
+    uint64_t actset_stub[1024 / 8];
+
+    ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
+    ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
+    flow->in_port.ofp_port = out_dev->ofp_port;
+    flow->metadata = htonll(0);
+    memset(&flow->tunnel, 0, sizeof flow->tunnel);
+    memset(&ctx->wc->masks.tunnel, 0, sizeof ctx->wc->masks.tunnel);
+    flow->tunnel.metadata.tab = 
ofproto_get_tun_tab(&out_dev->xbridge->ofproto->up);
+    ctx->wc->masks.tunnel.metadata.tab = flow->tunnel.metadata.tab;
+    memset(flow->regs, 0, sizeof flow->regs);
+    flow->actset_output = OFPP_UNSET;
+    ctx->conntracked = false;
+    clear_conntrack(ctx);
+    mirror_mask_t old_mirrors = ctx->mirrors;
+    bool independent_mirrors = out_dev->xbridge != ctx->xbridge;
+    if (independent_mirrors) {
+        ctx->mirrors = 0;
+    }
+    ctx->xbridge = out_dev->xbridge;
+
+    /* The bridge is now known so obtain its table version. */
+    ctx->xin->tables_version
+              = ofproto_dpif_get_tables_version(ctx->xbridge->ofproto);
+
+    if (!process_special(ctx, out_dev) && may_receive(out_dev, ctx)) {
+        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);
+            if (!ctx->freezing) {
+                xlate_action_set(ctx);
+            }
+            if (ctx->freezing) {
+                finish_freezing(ctx);
+            }
+        } else {
+            /* Forwarding is disabled by STP and RSTP.  Let OFPP_NORMAL and
+             * the learning action look at the packet, then drop it. */
+            struct flow old_base_flow = ctx->base_flow;
+            size_t old_size = ctx->odp_actions->size;
+            mirror_mask_t old_mirrors2 = ctx->mirrors;
+
+            xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
+            ctx->mirrors = old_mirrors2;
+            ctx->base_flow = old_base_flow;
+            ctx->odp_actions->size = old_size;
+
+            /* Undo changes that may have been done for freezing. */
+            ctx_cancel_freeze(ctx);
+        }
+    }
+
+    if (independent_mirrors) {
+        ctx->mirrors = old_mirrors;
+    }
+    ctx->xin->flow = old_flow;
+    ctx->xbridge = in_dev->xbridge;
+    ofpbuf_uninit(&ctx->action_set);
+    ctx->action_set = old_action_set;
+    ofpbuf_uninit(&ctx->stack);
+    ctx->stack = old_stack;
+
+    /* Restore calling bridge's lookup version. */
+    ctx->xin->tables_version = old_version;
+
+    /* Restore to calling bridge tunneling information */
+    ctx->wc->masks.tunnel = old_flow_tnl_wc;
+
+    /* The out bridge popping MPLS should have no effect on the original
+     * bridge. */
+    ctx->was_mpls = old_was_mpls;
+
+    /* The out bridge's conntrack execution should have no effect on the
+     * original bridge. */
+    ctx->conntracked = old_conntrack;
+
+    /* The fact that the out bridge exits (for any reason) does not mean
+     * that the original bridge should exit.  Specifically, if the out
+     * bridge freezes translation, the original bridge must continue
+     * processing with the original, not the frozen packet! */
+    ctx->exit = false;
+
+    /* Out bridge errors do not propagate back. */
+    ctx->error = XLATE_OK;
+
+    if (ctx->xin->resubmit_stats) {
+        netdev_vport_inc_tx(in_dev->netdev, ctx->xin->resubmit_stats);
+        netdev_vport_inc_rx(out_dev->netdev, ctx->xin->resubmit_stats);
+        if (out_dev->bfd) {
+            bfd_account_rx(out_dev->bfd, ctx->xin->resubmit_stats);
+        }
+    }
+    if (ctx->xin->xcache) {
+        struct xc_entry *entry;
+        entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NETDEV);
+        entry->dev.tx = netdev_ref(in_dev->netdev);
+        entry->dev.rx = netdev_ref(out_dev->netdev);
+        entry->dev.bfd = bfd_ref(out_dev->bfd);
+    }
+}
+
+static void
 compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
                         const struct xlate_bond_recirc *xr, bool check_stp)
 {
@@ -3133,138 +3266,8 @@ compose_output_action__(struct xlate_ctx *ctx, 
ofp_port_t ofp_port,
     }
 
     if (xport->peer) {
-        const struct xport *peer = xport->peer;
-        struct flow old_flow = ctx->xin->flow;
-        struct flow_tnl old_flow_tnl_wc = ctx->wc->masks.tunnel;
-        bool old_conntrack = ctx->conntracked;
-        bool old_was_mpls = ctx->was_mpls;
-        ovs_version_t old_version = ctx->xin->tables_version;
-        struct ofpbuf old_stack = ctx->stack;
-        uint8_t new_stack[1024];
-        struct ofpbuf old_action_set = ctx->action_set;
-        struct ovs_list *old_trace = ctx->xin->trace;
-        uint64_t actset_stub[1024 / 8];
-
-        ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
-        ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
-        flow->in_port.ofp_port = peer->ofp_port;
-        flow->metadata = htonll(0);
-        memset(&flow->tunnel, 0, sizeof flow->tunnel);
-        flow->tunnel.metadata.tab = ofproto_get_tun_tab(
-            &peer->xbridge->ofproto->up);
-        ctx->wc->masks.tunnel.metadata.tab = flow->tunnel.metadata.tab;
-        memset(flow->regs, 0, sizeof flow->regs);
-        flow->actset_output = OFPP_UNSET;
-        clear_conntrack(ctx);
-        ctx->xin->trace = xlate_report(ctx, OFT_BRIDGE,
-                                       "bridge(\"%s\")", peer->xbridge->name);
-
-        /* When the patch port points to a different bridge, then the mirrors
-         * for that bridge clearly apply independently to the packet, so we
-         * reset the mirror bitmap to zero and then restore it after the packet
-         * returns.
-         *
-         * When the patch port points to the same bridge, this is more of a
-         * design decision: can mirrors be re-applied to the packet after it
-         * re-enters the bridge, or should we treat that as doubly mirroring a
-         * single packet?  The former may be cleaner, since it respects the
-         * model in which a patch port is like a physical cable plugged from
-         * one switch port to another, but the latter may be less surprising to
-         * users.  We take the latter choice, for now at least.  (To use the
-         * former choice, hard-code 'independent_mirrors' to "true".) */
-        mirror_mask_t old_mirrors = ctx->mirrors;
-        bool independent_mirrors = peer->xbridge != ctx->xbridge;
-        if (independent_mirrors) {
-            ctx->mirrors = 0;
-        }
-        ctx->xbridge = peer->xbridge;
-
-        /* The bridge is now known so obtain its table version. */
-        ctx->xin->tables_version
-            = ofproto_dpif_get_tables_version(ctx->xbridge->ofproto);
-
-        if (!process_special(ctx, peer) && may_receive(peer, ctx)) {
-            if (xport_stp_forward_state(peer) && 
xport_rstp_forward_state(peer)) {
-                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
-                if (!ctx->freezing) {
-                    xlate_action_set(ctx);
-                }
-                if (ctx->freezing) {
-                    finish_freezing(ctx);
-                }
-            } else {
-                /* Forwarding is disabled by STP and RSTP.  Let OFPP_NORMAL and
-                 * the learning action look at the packet, then drop it. */
-                struct flow old_base_flow = ctx->base_flow;
-                size_t old_size = ctx->odp_actions->size;
-                mirror_mask_t old_mirrors2 = ctx->mirrors;
-
-                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
-                ctx->mirrors = old_mirrors2;
-                ctx->base_flow = old_base_flow;
-                ctx->odp_actions->size = old_size;
-
-                /* Undo changes that may have been done for freezing. */
-                ctx_cancel_freeze(ctx);
-            }
-        }
-
-        ctx->xin->trace = old_trace;
-        if (independent_mirrors) {
-            ctx->mirrors = old_mirrors;
-        }
-        ctx->xin->flow = old_flow;
-        ctx->xbridge = xport->xbridge;
-        ofpbuf_uninit(&ctx->action_set);
-        ctx->action_set = old_action_set;
-        ofpbuf_uninit(&ctx->stack);
-        ctx->stack = old_stack;
-
-        /* Restore calling bridge's lookup version. */
-        ctx->xin->tables_version = old_version;
-
-        /* Since this packet came in on a patch port (from the perspective of
-         * the peer bridge), it cannot have useful tunnel information. As a
-         * result, any wildcards generated on that tunnel also cannot be valid.
-         * The tunnel wildcards must be restored to their original version 
since
-         * the peer bridge uses a separate tunnel metadata table and therefore
-         * any generated wildcards will be garbage in the context of our
-         * metadata table. */
-        ctx->wc->masks.tunnel = old_flow_tnl_wc;
-
-        /* The peer bridge popping MPLS should have no effect on the original
-         * bridge. */
-        ctx->was_mpls = old_was_mpls;
-
-        /* The peer bridge's conntrack execution should have no effect on the
-         * original bridge. */
-        ctx->conntracked = old_conntrack;
-
-        /* The fact that the peer bridge exits (for any reason) does not mean
-         * that the original bridge should exit.  Specifically, if the peer
-         * bridge freezes translation, the original bridge must continue
-         * processing with the original, not the frozen packet! */
-        ctx->exit = false;
-
-        /* Peer bridge errors do not propagate back. */
-        ctx->error = XLATE_OK;
-
-        if (ctx->xin->resubmit_stats) {
-            netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats);
-            netdev_vport_inc_rx(peer->netdev, ctx->xin->resubmit_stats);
-            if (peer->bfd) {
-                bfd_account_rx(peer->bfd, ctx->xin->resubmit_stats);
-            }
-        }
-        if (ctx->xin->xcache) {
-            struct xc_entry *entry;
-
-            entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NETDEV);
-            entry->dev.tx = netdev_ref(xport->netdev);
-            entry->dev.rx = netdev_ref(peer->netdev);
-            entry->dev.bfd = bfd_ref(peer->bfd);
-        }
-        return;
+       group_actions(ctx, xport, xport->peer);
+       return;
     }
 
     flow_vlan_tci = flow->vlan_tci;
-- 
2.7.4

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to