Prior to this patch MPLS recirculation was not performed on a table
lookup following an MPLS_POP action.  This patch refactors MPLS
recirculation triggering so that a table action can be re-done after
recirculation if that table action follows an MPLS_POP action.

Recirculation for a patch port traversal (which also does a table
lookup) after an MPLS_POP action does not need to store the output
action, as recirculation without any post-recirculation actions causes
the table lookup to happen anyway.

Furthermore, the stack actions now have the same post-MPLS_POP
optimization as the SET_FIELD and MOVE actions had already:
recirculation is triggered only if the register in the action is L3 or
higher.

Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
---
 ofproto/ofproto-dpif-xlate.c |  204 +++++++++++++++++++++---------------------
 1 file changed, 101 insertions(+), 103 deletions(-)

diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index c030f73..51d0b76 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -299,10 +299,7 @@ struct xlate_ctx {
     /* True if a packet was but is no longer MPLS (due to an MPLS pop action).
      * This is a trigger for recirculation in cases where translating an action
      * or looking up a flow requires access to the fields of the packet after
-     * the MPLS label stack that was originally present.
-     *
-     * XXX: output to a table and patch port do not currently recirculate even
-     * if this is true. */
+     * the MPLS label stack that was originally present. */
     bool was_mpls;
 
     /* OpenFlow 1.1+ action set.
@@ -318,6 +315,19 @@ struct xlate_ctx {
 
 static void xlate_action_set(struct xlate_ctx *ctx);
 
+static void
+ctx_trigger_recirculation(struct xlate_ctx *ctx)
+{
+    ctx->exit = true;
+    ctx->recirc_action_offset = ctx->action_set.size;
+}
+
+static bool
+ctx_first_recirculation_action(const struct xlate_ctx *ctx)
+{
+    return ctx->recirc_action_offset == ctx->action_set.size;
+}
+
 static inline bool
 exit_recirculates(const struct xlate_ctx *ctx)
 {
@@ -326,7 +336,8 @@ exit_recirculates(const struct xlate_ctx *ctx)
     return ctx->recirc_action_offset >= 0;
 }
 
-static void compose_recirculate_action(struct xlate_ctx *ctx);
+static void compose_recirculate_action(struct xlate_ctx *ctx,
+                                       uint8_t recirc_table_id);
 
 /* A controller may use OFPP_NONE as the ingress port to indicate that
  * it did not arrive on a "real" port.  'ofpp_none_bundle' exists for
@@ -2822,7 +2833,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
                 }
                 /* Check if need to recirculate. */
                 if (exit_recirculates(ctx)) {
-                    compose_recirculate_action(ctx);
+                    compose_recirculate_action(ctx, 0);
                 }
             } else {
                 /* Forwarding is disabled by STP and RSTP.  Let OFPP_NORMAL and
@@ -3057,6 +3068,11 @@ 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)
 {
+    /* Check if we need to recirculate before matching in a table. */
+    if (ctx->was_mpls) {
+        ctx_trigger_recirculation(ctx);
+        return;
+    }
     if (xlate_resubmit_resource_check(ctx)) {
         struct flow_wildcards *wc;
         uint8_t old_table_id = ctx->table_id;
@@ -3363,7 +3379,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
 
 /* Called only when ctx->recirc_action_offset is set. */
 static void
-compose_recirculate_action(struct xlate_ctx *ctx)
+compose_recirculate_action(struct xlate_ctx *ctx, uint8_t recirc_table_id)
 {
     struct recirc_metadata md;
     uint32_t id;
@@ -3382,8 +3398,8 @@ compose_recirculate_action(struct xlate_ctx *ctx)
         /* Allocate a unique recirc id for the given metadata state in the
          * flow.  The life-cycle of this recirc id is managed by associating it
          * with the udpif key ('ukey') created for each new datapath flow. */
-        id = recirc_alloc_id_ctx(ctx->xbridge->ofproto, 0, &md, &ctx->stack,
-                                 ctx->recirc_action_offset,
+        id = recirc_alloc_id_ctx(ctx->xbridge->ofproto, recirc_table_id, &md,
+                                 &ctx->stack, ctx->recirc_action_offset,
                                  ctx->action_set.size, ctx->action_set.data);
         if (!id) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -3395,8 +3411,8 @@ compose_recirculate_action(struct xlate_ctx *ctx)
         /* Look up an existing recirc id for the given metadata state in the
          * flow.  No new reference is taken, as the ID is RCU protected and is
          * only required temporarily for verification. */
-        id = recirc_find_id(ctx->xbridge->ofproto, 0, &md, &ctx->stack,
-                            ctx->recirc_action_offset,
+        id = recirc_find_id(ctx->xbridge->ofproto, recirc_table_id, &md,
+                            &ctx->stack, ctx->recirc_action_offset,
                             ctx->action_set.size, ctx->action_set.data);
         /* We let zero 'id' to be used in the RECIRC action below, which will
          * fail all revalidations as zero is not a valid recirculation ID. */
@@ -3857,85 +3873,6 @@ xlate_action_set(struct xlate_ctx *ctx)
     ofpbuf_uninit(&action_list);
 }
 
-static bool
-ofpact_needs_recirculation_after_mpls(const struct ofpact *a, struct xlate_ctx 
*ctx)
-{
-    struct flow_wildcards *wc = &ctx->xout->wc;
-    struct flow *flow = &ctx->xin->flow;
-
-    if (!ctx->was_mpls) {
-        return false;
-    }
-
-    switch (a->type) {
-    case OFPACT_OUTPUT:
-    case OFPACT_GROUP:
-    case OFPACT_CONTROLLER:
-    case OFPACT_STRIP_VLAN:
-    case OFPACT_SET_VLAN_PCP:
-    case OFPACT_SET_VLAN_VID:
-    case OFPACT_ENQUEUE:
-    case OFPACT_PUSH_VLAN:
-    case OFPACT_SET_ETH_SRC:
-    case OFPACT_SET_ETH_DST:
-    case OFPACT_SET_TUNNEL:
-    case OFPACT_SET_QUEUE:
-    case OFPACT_POP_QUEUE:
-    case OFPACT_CONJUNCTION:
-    case OFPACT_NOTE:
-    case OFPACT_UNROLL_XLATE:
-    case OFPACT_OUTPUT_REG:
-    case OFPACT_EXIT:
-    case OFPACT_METER:
-    case OFPACT_WRITE_METADATA:
-    case OFPACT_WRITE_ACTIONS:
-    case OFPACT_CLEAR_ACTIONS:
-    case OFPACT_SAMPLE:
-        return false;
-
-    case OFPACT_POP_MPLS:
-    case OFPACT_DEC_MPLS_TTL:
-    case OFPACT_SET_MPLS_TTL:
-    case OFPACT_SET_MPLS_TC:
-    case OFPACT_SET_MPLS_LABEL:
-    case OFPACT_SET_IPV4_SRC:
-    case OFPACT_SET_IPV4_DST:
-    case OFPACT_SET_IP_DSCP:
-    case OFPACT_SET_IP_ECN:
-    case OFPACT_SET_IP_TTL:
-    case OFPACT_SET_L4_SRC_PORT:
-    case OFPACT_SET_L4_DST_PORT:
-    case OFPACT_RESUBMIT:
-    case OFPACT_STACK_PUSH:
-    case OFPACT_STACK_POP:
-    case OFPACT_DEC_TTL:
-    case OFPACT_MULTIPATH:
-    case OFPACT_BUNDLE:
-    case OFPACT_LEARN:
-    case OFPACT_FIN_TIMEOUT:
-    case OFPACT_GOTO_TABLE:
-        return true;
-
-    case OFPACT_REG_MOVE:
-        return (mf_is_l3_or_higher(ofpact_get_REG_MOVE(a)->dst.field) ||
-                mf_is_l3_or_higher(ofpact_get_REG_MOVE(a)->src.field));
-
-    case OFPACT_SET_FIELD:
-        return mf_is_l3_or_higher(ofpact_get_SET_FIELD(a)->field);
-
-    case OFPACT_PUSH_MPLS:
-        /* Recirculate if it is an IP packet with a zero ttl.  This may
-         * indicate that the packet was previously MPLS and an MPLS pop action
-         * converted it to IP. In this case recirculating should reveal the IP
-         * TTL which is used as the basis for a new MPLS LSE. */
-        return (!flow_count_mpls_labels(flow, wc)
-                && flow->nw_ttl == 0
-                && is_ip_any(flow));
-    }
-
-    OVS_NOT_REACHED();
-}
-
 static void
 recirc_put_unroll_xlate(struct xlate_ctx *ctx)
 {
@@ -4034,6 +3971,17 @@ recirc_unroll_actions(const struct ofpact *ofpacts, 
size_t ofpacts_len,
     }
 }
 
+#define CHECK_MPLS_RECIRCULATION()      \
+    if (ctx->was_mpls) {                \
+        ctx_trigger_recirculation(ctx); \
+        break;                          \
+    }
+#define CHECK_MPLS_RECIRCULATION_COND(COND) \
+    if (ctx->was_mpls && (COND)) {          \
+        ctx_trigger_recirculation(ctx);     \
+        break;                              \
+    }
+
 static void
 do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                  struct xlate_ctx *ctx)
@@ -4053,18 +4001,13 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
         const struct ofpact_set_field *set_field;
         const struct mf_field *mf;
 
-        if (!ctx->exit && ofpact_needs_recirculation_after_mpls(a, ctx)) {
-            ctx->exit = true;
-            ctx->recirc_action_offset = ctx->action_set.size;
-        }
-
+        /* Check if need to store the remaining actions for later execution. */
         if (ctx->exit) {
-            /* Check if need to store the remaining actions for later
-             * execution. */
             if (exit_recirculates(ctx)) {
-                recirc_unroll_actions(a, OFPACT_ALIGN(ofpacts_len -
-                                                      ((uint8_t *)a -
-                                                       (uint8_t *)ofpacts)),
+                recirc_unroll_actions(a,
+                                      OFPACT_ALIGN(ofpacts_len -
+                                                   ((uint8_t *)a -
+                                                    (uint8_t *)ofpacts)),
                                       ctx);
             }
             break;
@@ -4078,6 +4021,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
 
         case OFPACT_GROUP:
             if (xlate_group_action(ctx, ofpact_get_GROUP(a)->group_id)) {
+                /* Group could not be found. */
                 return;
             }
             break;
@@ -4137,6 +4081,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_SET_IPV4_SRC:
+            CHECK_MPLS_RECIRCULATION();
             if (flow->dl_type == htons(ETH_TYPE_IP)) {
                 memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
                 flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
@@ -4144,6 +4089,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_SET_IPV4_DST:
+            CHECK_MPLS_RECIRCULATION();
             if (flow->dl_type == htons(ETH_TYPE_IP)) {
                 memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
                 flow->nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
@@ -4151,6 +4097,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_SET_IP_DSCP:
+            CHECK_MPLS_RECIRCULATION();
             if (is_ip_any(flow)) {
                 wc->masks.nw_tos |= IP_DSCP_MASK;
                 flow->nw_tos &= ~IP_DSCP_MASK;
@@ -4159,6 +4106,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_SET_IP_ECN:
+            CHECK_MPLS_RECIRCULATION();
             if (is_ip_any(flow)) {
                 wc->masks.nw_tos |= IP_ECN_MASK;
                 flow->nw_tos &= ~IP_ECN_MASK;
@@ -4167,6 +4115,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_SET_IP_TTL:
+            CHECK_MPLS_RECIRCULATION();
             if (is_ip_any(flow)) {
                 wc->masks.nw_ttl = 0xff;
                 flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl;
@@ -4174,6 +4123,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_SET_L4_SRC_PORT:
+            CHECK_MPLS_RECIRCULATION();
             if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
                 memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
                 memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
@@ -4182,6 +4132,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_SET_L4_DST_PORT:
+            CHECK_MPLS_RECIRCULATION();
             if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
                 memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
                 memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
@@ -4210,10 +4161,15 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_REG_MOVE:
+            CHECK_MPLS_RECIRCULATION_COND(
+                mf_is_l3_or_higher(ofpact_get_REG_MOVE(a)->dst.field) ||
+                mf_is_l3_or_higher(ofpact_get_REG_MOVE(a)->src.field));
             nxm_execute_reg_move(ofpact_get_REG_MOVE(a), flow, wc);
             break;
 
         case OFPACT_SET_FIELD:
+            CHECK_MPLS_RECIRCULATION_COND(
+                mf_is_l3_or_higher(ofpact_get_SET_FIELD(a)->field));
             set_field = ofpact_get_SET_FIELD(a);
             mf = set_field->field;
 
@@ -4239,43 +4195,62 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_STACK_PUSH:
+            CHECK_MPLS_RECIRCULATION_COND(
+                mf_is_l3_or_higher(ofpact_get_STACK_PUSH(a)->subfield.field));
             nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), flow, wc,
                                    &ctx->stack);
             break;
 
         case OFPACT_STACK_POP:
+            CHECK_MPLS_RECIRCULATION_COND(
+                mf_is_l3_or_higher(ofpact_get_STACK_POP(a)->subfield.field));
             nxm_execute_stack_pop(ofpact_get_STACK_POP(a), flow, wc,
                                   &ctx->stack);
             break;
 
         case OFPACT_PUSH_MPLS:
+            /* Recirculate if it is an IP packet with a zero ttl.  This may
+             * indicate that the packet was previously MPLS and an MPLS pop
+             * action converted it to IP. In this case recirculating should
+             * reveal the IP TTL which is used as the basis for a new MPLS
+             * LSE. */
+            CHECK_MPLS_RECIRCULATION_COND(
+                !flow_count_mpls_labels(flow, wc)
+                && flow->nw_ttl == 0
+                && is_ip_any(flow));
             compose_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a));
             break;
 
         case OFPACT_POP_MPLS:
+            CHECK_MPLS_RECIRCULATION();
             compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
             break;
 
         case OFPACT_SET_MPLS_LABEL:
+            CHECK_MPLS_RECIRCULATION();
             compose_set_mpls_label_action(
                 ctx, ofpact_get_SET_MPLS_LABEL(a)->label);
-        break;
+            break;
 
         case OFPACT_SET_MPLS_TC:
+            CHECK_MPLS_RECIRCULATION();
             compose_set_mpls_tc_action(ctx, ofpact_get_SET_MPLS_TC(a)->tc);
             break;
 
         case OFPACT_SET_MPLS_TTL:
+            CHECK_MPLS_RECIRCULATION();
             compose_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl);
             break;
 
         case OFPACT_DEC_MPLS_TTL:
+            CHECK_MPLS_RECIRCULATION();
             if (compose_dec_mpls_ttl_action(ctx)) {
                 return;
             }
             break;
 
         case OFPACT_DEC_TTL:
+            CHECK_MPLS_RECIRCULATION();
             wc->masks.nw_ttl = 0xff;
             if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
                 return;
@@ -4287,10 +4262,12 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_MULTIPATH:
+            CHECK_MPLS_RECIRCULATION();
             multipath_execute(ofpact_get_MULTIPATH(a), flow, wc);
             break;
 
         case OFPACT_BUNDLE:
+            CHECK_MPLS_RECIRCULATION();
             xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
             break;
 
@@ -4299,6 +4276,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_LEARN:
+            CHECK_MPLS_RECIRCULATION();
             xlate_learn_action(ctx, ofpact_get_LEARN(a));
             break;
 
@@ -4325,6 +4303,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
         }
         case OFPACT_FIN_TIMEOUT:
+            CHECK_MPLS_RECIRCULATION();
             memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
             ctx->xout->has_fin_timeout = true;
             xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a));
@@ -4368,6 +4347,17 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
             break;
         }
+
+        /* Check if need to store this and the remaining actions for later
+         * execution. */
+        if (ctx->exit && ctx_first_recirculation_action(ctx)) {
+            recirc_unroll_actions(a,
+                                  OFPACT_ALIGN(ofpacts_len -
+                                               ((uint8_t *)a -
+                                                (uint8_t *)ofpacts)),
+                                  ctx);
+            break;
+        }
     }
 }
 
@@ -4716,7 +4706,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out 
*xout)
             ctx.xbridge = new_bridge;
         }
 
-        /* Set the post-recirculation table id. */
+        /* Set the post-recirculation table id.  Note: A table lookup is done
+         * only if there are no post-recirculation actions. */
         ctx.table_id = recirc->table_id;
 
         /* Restore pipeline metadata. May change flow's in_port and other
@@ -4747,9 +4738,16 @@ xlate_actions(struct xlate_in *xin, struct xlate_out 
*xout)
         /* Restore recirculation actions.  If there are no actions, processing
          * will start with a lookup in the table set above. */
         if (recirc->ofpacts_len > recirc->action_set_len) {
+            struct ds ds = DS_EMPTY_INITIALIZER;
+
             xin->ofpacts_len = recirc->ofpacts_len - recirc->action_set_len;
             xin->ofpacts = recirc->ofpacts +
                 recirc->action_set_len / sizeof *recirc->ofpacts;
+
+            ofpacts_format(xin->ofpacts, xin->ofpacts_len, &ds);
+            VLOG_INFO("Post-recirculation actions for ID %"PRIu32": %s",
+                      flow->recirc_id, ds_cstr(&ds));
+            ds_destroy(&ds);
         }
     } else if (OVS_UNLIKELY(flow->recirc_id)) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -4861,7 +4859,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out 
*xout)
             }
             /* Check if need to recirculate. */
             if (exit_recirculates(&ctx)) {
-                compose_recirculate_action(&ctx);
+                compose_recirculate_action(&ctx, 0);
             }
         }
 
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to