On 18 Oct 2022, at 19:34, Ales Musil wrote:
> When the packet was traveling through patch port boundary
> OvS would check if any of the actions is reversible,
> if not it would clone the packet. However, the check
> was only at the first level of the second bridge.
> That caused some issues when the packet had gone
> through more actions, some of them might have been
> irreversible.
>
> In order to keep the semantics the same we might
> need to run the actions twice in the worst case
> scenario. During the clone there are 4 scenarios
> that can happen.
>
> 1) The action is last one for that flow,
> in that case we just continue without clone.
>
> 2) There is irreversible action in the action
> set (first level). In this case we know that
> there is at leas one irreversible action which
> is enough to do clone.
>
> 3) All actions in first level are reversible,
> we can try to run all actions as if we don't
> need any clone and inspect the ofpbuf at the
> end. In positive case there are no irreversible
> actions so we will just submit the buffer and continue.
>
> 4) This is same as 3) with the difference that
> there is irreversible action in the ofpbuf.
> To keep the semantics we need to re-run the actions
> and treat it as clone. This requires resotration
> of the xlate_ctx.
>
> Add test cases for all irreversible actions
> to see if they are properly cloned.
>
> Signed-off-by: Ales Musil <[email protected]>
> ---
Thanks for following through with all these revisions!!
See some small comments below.
//Eelco
<SNIP>
Why did you remove odp_sample_contains_irreversible_actions(), is it because
OVN always makes this irreversible, or is it try that in any case OVS will add
some reversible actions? If there is a way for OVS to add only reversible
actions in sample, we should re-introduce this check.
> +
> +static bool
> +odp_cpl_contains_irreversible_actions(const struct nlattr *attr)
> +{
> + static const struct nl_policy ovs_cpl_policy[] = {
> + [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = {.type = NL_A_U16},
> + [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = {.type = NL_A_NESTED},
> + [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL] = {.type =
> NL_A_NESTED},
> + };
> + struct nlattr *a[ARRAY_SIZE(ovs_cpl_policy)];
> +
> + if (!nl_parse_nested(attr, ovs_cpl_policy, a, ARRAY_SIZE(a))) {
> + return false;
> + }
> +
> + const struct nlattr *greater =
> + a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER];
> + const struct nlattr *less =
> + a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL];
> + const void *greater_data = nl_attr_get(greater);
> + const void *less_data = nl_attr_get(less);
> + size_t greater_len = nl_attr_get_size(greater);
> + size_t less_len = nl_attr_get_size(less);
> +
> + return odp_contains_irreversible_action(greater_data, greater_len) ||
> + odp_contains_irreversible_action(less_data, less_len);
> +}
> +
> +/* Check if any of the actions in the ofpbuf is irreversible. */
> +bool
> +odp_contains_irreversible_action(const void *attrs, size_t attrs_len)
> +{
> + const struct nlattr *attr;
> + int left;
> +
> + NL_ATTR_FOR_EACH (attr, left, attrs, attrs_len) {
> + uint16_t type = attr->nla_type;
> +
> + switch ((enum ovs_action_attr) type) {
> + /* Skip "clone" or "sample" because it already encapsulates
"because they already encapsulate"
> + * irreversible actions. */
> + case OVS_ACTION_ATTR_CLONE:
> + case OVS_ACTION_ATTR_SAMPLE:
See comment above.
> + continue;
> + /* Check nested actions of "check_packet_len". */
> + case OVS_ACTION_ATTR_CHECK_PKT_LEN:
> + if (odp_cpl_contains_irreversible_actions(attr)) {
> + return true;
> + }
> + continue;
> + /* Check any other action. */
> + case OVS_ACTION_ATTR_CT:
> + case OVS_ACTION_ATTR_CT_CLEAR:
> + case OVS_ACTION_ATTR_TRUNC:
> + case OVS_ACTION_ATTR_PUSH_ETH:
> + case OVS_ACTION_ATTR_POP_ETH:
> + case OVS_ACTION_ATTR_PUSH_NSH:
> + case OVS_ACTION_ATTR_POP_NSH:
> + case OVS_ACTION_ATTR_METER:
> + case OVS_ACTION_ATTR_TUNNEL_PUSH:
> + case OVS_ACTION_ATTR_TUNNEL_POP:
> + case OVS_ACTION_ATTR_UNSPEC:
> + case OVS_ACTION_ATTR_OUTPUT:
> + case OVS_ACTION_ATTR_USERSPACE:
> + case OVS_ACTION_ATTR_SET:
> + case OVS_ACTION_ATTR_PUSH_VLAN:
> + case OVS_ACTION_ATTR_POP_VLAN:
> + case OVS_ACTION_ATTR_RECIRC:
> + case OVS_ACTION_ATTR_HASH:
> + case OVS_ACTION_ATTR_SET_MASKED:
> + case OVS_ACTION_ATTR_LB_OUTPUT:
> + case OVS_ACTION_ATTR_ADD_MPLS:
> + case OVS_ACTION_ATTR_PUSH_MPLS:
> + case OVS_ACTION_ATTR_POP_MPLS:
> + case OVS_ACTION_ATTR_DROP:
> + if (nlattr_action_is_irreversible(type)) {
> + return true;
> + }
> + continue;
> + case __OVS_ACTION_ATTR_MAX:
> + break;
> + }
Maybe add a small comment here, as it might not make sense for people reading
this why you use the continue; logic here. Something like:
/* All unknown action types fallthrough the switch statement and
* are by default assumed irreversible. */
> + return true;
> + }
> + return false;
> +}
<SNIP>
> @@ -5858,37 +5963,22 @@ 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, bool is_last_action,
> - bool group_bucket_action OVS_UNUSED)
> +do_clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
> + struct xlate_ctx *ctx)
> {
> - struct ofpbuf old_stack = ctx->stack;
> - union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)];
> - ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
> - ofpbuf_put(&ctx->stack, old_stack.data, old_stack.size);
> -
> - struct ofpbuf old_action_set = ctx->action_set;
> - uint64_t actset_stub[1024 / 8];
> - ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
> - ofpbuf_put(&ctx->action_set, old_action_set.data, old_action_set.size);
> -
> size_t offset, ac_offset;
> - struct flow old_flow = ctx->xin->flow;
> -
> - if (reversible_actions(actions, actions_len) || is_last_action) {
> - old_flow = ctx->xin->flow;
> - do_xlate_actions(actions, actions_len, ctx, is_last_action, false);
> - xlate_ctx_process_freezing(ctx);
> - goto xlate_done;
> - }
> + union mf_subvalue stack_buffer[1024 / sizeof(union mf_subvalue)];
> + uint64_t act_set_buffer[1024 / 8];
Why not make these two part of the xlate_ctx_clone_state structure itself?
This might also help allocating this data structure on the heap if needed due
to a large number of recursions (See discussion on Mike's patch with Ilya).
> + struct xlate_ctx_clone_state old_state =
> + xlate_ctx_clone_state_store(ctx, stack_buffer, sizeof stack_buffer,
> + act_set_buffer, sizeof act_set_buffer);
>
> /* Commit datapath actions before emitting the clone action to
> * avoid emitting those actions twice. Once inside
> * the clone, another time for the action after clone. */
> xlate_commit_actions(ctx);
> - struct flow old_base = ctx->base_flow;
> - bool old_was_mpls = ctx->was_mpls;
> - bool old_conntracked = ctx->conntracked;
nit: add an extra cr/lf here, right before the below comment.
> + /* The base needs to be stored after xlate_commit_actions. */
> + old_state.base_flow = ctx->base_flow;
>
<SNIP>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev