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