On 2023/11/15 5:41, Ilya Maximets wrote:
> On 11/7/23 08:30, Nobuhiro MIKI wrote:
>> A conjunctive flow consists of two or more multiple flows with
>> conjunction actions. When input to the ofproto/trace command
>> matches a conjunctive flow, it outputs flows of all dimensions.
>>
>> Acked-by: Simon Horman <[email protected]>
>> Signed-off-by: Nobuhiro MIKI <[email protected]>
>> ---
>>  lib/classifier.c             | 51 ++++++++++++++++++++++---
>>  lib/classifier.h             |  4 +-
>>  lib/ovs-router.c             |  5 ++-
>>  lib/tnl-ports.c              |  6 +--
>>  ofproto/ofproto-dpif-xlate.c | 47 ++++++++++++++++++++---
>>  ofproto/ofproto-dpif-xlate.h |  3 ++
>>  ofproto/ofproto-dpif.c       | 25 +++++++++----
>>  ofproto/ofproto-dpif.h       |  3 +-
>>  tests/classifier.at          | 72 ++++++++++++++++++++++++++++++++++++
>>  tests/test-classifier.c      |  8 ++--
>>  10 files changed, 194 insertions(+), 30 deletions(-)
> 
> Hi, thanks for the new version!
> 
> I went through the code and I see that you fixed all the issues
> I reported.  I meant to apply the change, but then noticed one
> more issue with the handling of the trace, see below.
> I hope, it's the last one.
> 

Hi Ilya,

Thank you for pointing this out.
Based on your comments I have come up with the following fix.
I will prepare v6.

Best Regards,
Nobuhiro Miki

> <snip>
> 
>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>> index e243773307b7..36b05803afb8 100644
>> --- a/ofproto/ofproto-dpif-xlate.c
>> +++ b/ofproto/ofproto-dpif-xlate.c
>> @@ -866,6 +866,34 @@ xlate_report_action_set(const struct xlate_ctx *ctx, 
>> const char *verb)
>>      }
>>  }
>>  
>> +static void
>> +xlate_report_conj_matches(const struct xlate_ctx *ctx,
>> +                          const struct ofputil_port_map *map)
>> +{
>> +    struct ds s = DS_EMPTY_INITIALIZER;
>> +    struct hmapx_node *node;
>> +    struct cls_rule *rule;
>> +
>> +    /* NOTE: The conj flows have meaning in order.  For each flow that is a
>> +     * component of conj flows, 'k' in 'conjunction(id, k/n)' represents the
>> +     * dimension.  When there are multiple flows with the same id, it may be
>> +     * implicitly expected that they would be output in ascending order of 
>> 'k'.
>> +     *
>> +     * However, because of the use of hmapx strucutre and the fact that the
>> +     * classifier returns them in arbitrary order, they are output in 
>> arbitrary
>> +     * order here. */
>> +    HMAPX_FOR_EACH (node, &ctx->xout->conj_flows) {
>> +        ds_clear(&s);
>> +
>> +        rule = node->data;
>> +
>> +        cls_rule_format(rule, ofproto_get_tun_tab(&ctx->xin->ofproto->up),
>> +                        map, &s);
>> +        xlate_report(ctx, OFT_DETAIL, "conj. %s", ds_cstr(&s));
>> +    }
>> +
>> +    ds_destroy(&s);
>> +}
>>  
>>  /* If tracing is enabled in 'ctx', appends a node representing 'rule' (in
>>   * OpenFlow table 'table_id') to the trace and makes this node the parent 
>> for
>> @@ -882,6 +910,8 @@ xlate_report_table(const struct xlate_ctx *ctx, struct 
>> rule_dpif *rule,
>>          return;
>>      }
>>  
>> +    struct ofputil_port_map map = OFPUTIL_PORT_MAP_INITIALIZER(&map);
>> +
>>      struct ds s = DS_EMPTY_INITIALIZER;
>>      ds_put_format(&s, "%2d. ", table_id);
>>      if (rule == ctx->xin->ofproto->miss_rule) {
>> @@ -892,8 +922,6 @@ xlate_report_table(const struct xlate_ctx *ctx, struct 
>> rule_dpif *rule,
>>          ds_put_cstr(&s, "Packets are IP fragments and "
>>                      "the fragment handling mode is \"drop\".");
>>      } else {
>> -        struct ofputil_port_map map = OFPUTIL_PORT_MAP_INITIALIZER(&map);
>> -
>>          if (ctx->xin->names) {
>>              struct ofproto_dpif *ofprotop;
>>              ofprotop = ofproto_dpif_lookup_by_name(ctx->xbridge->name);
>> @@ -904,8 +932,6 @@ xlate_report_table(const struct xlate_ctx *ctx, struct 
>> rule_dpif *rule,
>>                           ofproto_get_tun_tab(&ctx->xin->ofproto->up),
>>                           &map, &s, OFP_DEFAULT_PRIORITY);
>>  
>> -        ofputil_port_map_destroy(&map);
>> -
>>          if (ds_last(&s) != ' ') {
>>              ds_put_cstr(&s, ", ");
>>          }
>> @@ -918,6 +944,9 @@ xlate_report_table(const struct xlate_ctx *ctx, struct 
>> rule_dpif *rule,
>>      ctx->xin->trace = &oftrace_report(ctx->xin->trace, OFT_TABLE,
>>                                        ds_cstr(&s))->subs;
>>      ds_destroy(&s);
>> +
>> +    xlate_report_conj_matches(ctx, &map);
>> +    ofputil_port_map_destroy(&map);
>>  }
>>  
>>  /* If tracing is enabled in 'ctx', adds an OFT_DETAIL trace node to 'ctx'
>> @@ -4653,7 +4682,8 @@ xlate_table_action(struct xlate_ctx *ctx, ofp_port_t 
>> in_port, uint8_t table_id,
>>                                             ctx->xin->resubmit_stats,
>>                                             &ctx->table_id, in_port,
>>                                             may_packet_in, honor_table_miss,
>> -                                           ctx->xin->xcache);
>> +                                           ctx->xin->xcache,
>> +                                           &ctx->xout->conj_flows);
> 
> So, here we should also check that trace is enabled before passing
> the hash map into a function, otherwise we'll have an unnecessary
> performance impact when the trace is not enabled.  The same as in
> the xlate_actions() function below.
> 
> There is one more issue with this code though.  We're doing a
> recursive flow translation for one of the actions that jumps into
> a different OpenFlow table, but we carry the content of the hash map
> along and will print those unrelated rules when we will report the
> result of that recursive flow translation.
> 
> For example, let's say we have following flow table:
> 
> conj_id=1,actions=resubmit(,2)
> priority=10,ip,actions=conjunction(1,1/2)
> priority=10,in_port=p1,actions=conjunction(1,2/2)
> priority=10,in_port=p2,actions=conjunction(1,2/2)
> 
> table=2,conj_id=7,actions=resubmit(,3)
> table=2,priority=20,ip,actions=conjunction(7,1/2)
> table=2,priority=20,in_port=p1,actions=conjunction(7,2/2)
> table=2,priority=20,in_port=p2,actions=conjunction(7,2/2)
> 
> table=3,actions=drop
> 
> We have 3 tables and we jump from table 0 to table 2 and to table 3.
> The trace should look like this:
> 
>  bridge("br0")
>  -------------
>   0. conj_id=1, priority 32768
>       -> conj. priority=10,in_port=p1
>       -> conj. priority=10,ip
>      resubmit(,2)
>   2. conj_id=7, priority 32768
>       -> conj. priority=20,ip
>       -> conj. priority=20,in_port=p1
>      resubmit(,3)
>   3. priority 32768
>      drop
> 
> We match on the conjunctive flow in table 0, resubmit to table 2,
> match on a different conjunctive flow in table 2, resubmit to table
> 3 and match on a simple drop flow.
> 
> However, with the current implementation, the hash map is carried
> through the recursive flow translation and we get it growing and
> printed out for each flow, even not conjunctive ones:
> 
>  bridge("br0")
>  -------------
>   0. conj_id=1, priority 32768
>       -> conj. priority=10,in_port=p1
>       -> conj. priority=10,ip
>      resubmit(,2)
>   2. conj_id=7, priority 32768
>       -> conj. priority=10,in_port=p1
>       -> conj. priority=20,in_port=p1
>       -> conj. priority=20,ip
>       -> conj. priority=10,ip
>      resubmit(,3)
>   3. priority 32768
>       -> conj. priority=10,in_port=p1
>       -> conj. priority=20,in_port=p1
>       -> conj. priority=20,ip
>       -> conj. priority=10,ip
>      drop
> 
> This is happening because the hash map is only cleared/destroyed
> at the very end of the flow translation.
> 
> Instead, we should create it and clear/destroy after each lookup.
> The 'conj_flows'  also doesn't seem to belong in the 'xlate_out'
> structure.  It might be better to have it in the 'xlate_ctx' structure,
> close to the 'rule', as they are related, or just allocate on stack.
> 
> One approach might be to do something like this:
> 
> @@ -4659,6 +4660,7 @@ xlate_table_action(struct xlate_ctx *ctx, ofp_port_t 
> in_port, uint8_t table_id,
>      }
>      if (xlate_resubmit_resource_check(ctx)) {
>          uint8_t old_table_id = ctx->table_id;
> +        struct hmapx *conj_flows = NULL;
>          struct rule_dpif *rule;
>  
>          ctx->table_id = table_id;
> @@ -4675,6 +4677,13 @@ xlate_table_action(struct xlate_ctx *ctx, ofp_port_t 
> in_port, uint8_t table_id,
>              }
>              tuple_swap(&ctx->xin->flow, ctx->wc);
>          }
> +
> +
> +        if (ctx->xin->trace) {
> +            conj_flows = xzalloc(sizeof *conj_flows);
> +            hmapx_init(conj_flows);
> +        }
> +
>          rule = rule_dpif_lookup_from_table(ctx->xbridge->ofproto,
>                                             ctx->xin->tables_version,
>                                             &ctx->xin->flow, ctx->wc,
> @@ -4682,7 +4691,7 @@ xlate_table_action(struct xlate_ctx *ctx, ofp_port_t 
> in_port, uint8_t table_id,
>                                             &ctx->table_id, in_port,
>                                             may_packet_in, honor_table_miss,
>                                             ctx->xin->xcache,
> -                                           &ctx->xout->conj_flows);
> +                                           conj_flows);
>          /* Swap back. */
>          if (with_ct_orig) {
>              tuple_swap(&ctx->xin->flow, ctx->wc);
> @@ -4702,13 +4711,15 @@ 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_report_table(ctx, rule, table_id, conj_flows);
>              xlate_recursively(ctx, rule, table_id <= old_table_id,
>                                is_last_action, xlator);
>              ctx->xin->trace = old_trace;
>          }
>  
>          ctx->table_id = old_table_id;
> +        hmapx_destroy(conj_flows);
> +        free(conj_flows);
>          return;
>      }
>  }
> ---
> 
> I.e. just use a temporary hash map, if needed.  And do the same
> in the xlate_actions() function as well.
> 
> Another way to fix the problem might be to add hmapx_clear() call at
> the end of xlate_report_conj_matches(), but that would require removing
> a const qualifier from the ctx pointer in this function.  And I'd prefer
> moving the conj_flows from xlate_out to xlate_ctx in this case, as it is
> not the translation output, but a temporary context.  And we need that
> info for a very short amount of time actually.
> 
> What do you think?
> 

I agree with moving conj_flows from xlate_out to xlate_ctx.
How about doing hmapx_clear for conj_flows on the caller
of xlate_report_table?  It would be done without removing
a const qualifier. xlate_report_conj_matches is only
called from xlate_report_table. And xlate_report_table
is called from only two places.

I wonder if conj_flows should be included in xlate_ctx or if it
should be a local variable in the xlate_actions and
xlate_table_actionfunctions. Here, I am thinking of including
conj_flows in xlate_ctx because I want to reduce the duplication
of code fragments, and also because I feel that reducing the
number of malloc/free would be more robust.

Checking true/false of xin->trace is often carefully
tuned using OVS_LIKELY or OVS_UNLIKELY macros. So I will
follow that with this patch.

In addition, I will add a test case including resubmit flow rule.

> One small nit below.
> 
> <snip>
> 
>> +AT_SETUP([conjunctive match with or without port map])
>> +OVS_VSWITCHD_START
>> +add_of_ports br0 1 2
>> +AT_DATA([flows.txt], [dnl
>> +conj_id=1,actions=drop
>> +conj_id=2,actions=drop
>> +
>> +priority=10,ip,actions=conjunction(1,1/2),conjunction(2,1/2)
>> +priority=10,in_port=p1,actions=conjunction(1,2/2)
>> +priority=10,in_port=p2,actions=conjunction(1,2/2)
>> +])
>> +ovs-vsctl show
> 
> Nit: This call seems unnecessary.
> 

Sorry. I'll remove the line.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to