On 3/27/24 08:45, Ales Musil wrote:
> On Wed, Mar 27, 2024 at 7:14 AM Han Zhou <hz...@ovn.org> wrote:
> 
>>
>>
>> On Tue, Mar 19, 2024 at 9:45 AM Ales Musil <amu...@redhat.com> wrote:
>>>
>>>
>>>
>>> On Tue, Mar 19, 2024 at 5:43 PM Ales Musil <amu...@redhat.com> wrote:
>>>>
>>>> Instead of tracking address set per struct expr_constant_set track it
>>>> per individual struct expr_constant. This allows more fine grained
>>>> control for I-P processing of address sets in controller. It helps with
>>>> scenarios like matching on two address sets in one expression e.g.
>>>> "ip4.src == {$as1, $as2}". This allows any addition or removal of
>>>> individual adress from the set to be incrementally processed instead
>>>> of reprocessing all the flows.
>>>>
>>>> This unfortunately doesn't help with the following flows:
>>>> "ip4.src == $as1 && ip4.dst == $as2"
>>>> "ip4.src == $as1 || ip4.dst == $as2"
>>>>
>>>> The memory impact should be minimal as there is only increase of 8 bytes
>>>> per the struct expr_constant.
>>>>
>>>> Signed-off-by: Ales Musil <amu...@redhat.com>
>>
>> Thanks Ales for the improvement! The approach looks good to me. Please see
>> some comments below:
>>
>>
>>
> Hi Han,
> 
> thank you for the review.
> 
> 
>>
>>>> ---
>>>>  controller/lflow.c      |  4 +-
>>>>  include/ovn/actions.h   |  2 +-
>>>>  include/ovn/expr.h      | 46 ++++++++++----------
>>>>  lib/actions.c           | 20 ++++-----
>>>>  lib/expr.c              | 95 +++++++++++++++++------------------------
>>>>  tests/ovn-controller.at | 14 +++---
>>>>  6 files changed, 83 insertions(+), 98 deletions(-)
>>>>
>>>> diff --git a/controller/lflow.c b/controller/lflow.c
>>>> index 895d17d19..730dc879d 100644
>>>> --- a/controller/lflow.c
>>>> +++ b/controller/lflow.c
>>>> @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in
>> *l_ctx_in,
>>>>  }
>>>>
>>>>  static bool
>>>> -as_info_from_expr_const(const char *as_name, const union expr_constant
>> *c,
>>>> +as_info_from_expr_const(const char *as_name, const struct
>> expr_constant *c,
>>>>                          struct addrset_info *as_info)
>>>>  {
>>>>      as_info->name = as_name;
>>>> @@ -714,7 +714,7 @@ lflow_handle_addr_set_update(const char *as_name,
>>>>          if (as_diff->deleted) {
>>>>              struct addrset_info as_info;
>>>>              for (size_t i = 0; i < as_diff->deleted->n_values; i++) {
>>>> -                union expr_constant *c = &as_diff->deleted->values[i];
>>>> +                struct expr_constant *c = &as_diff->deleted->values[i];
>>>>                  if (!as_info_from_expr_const(as_name, c, &as_info)) {
>>>>                      continue;
>>>>                  }
>>>> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
>>>> index dcacbb1ff..1e20f9b81 100644
>>>> --- a/include/ovn/actions.h
>>>> +++ b/include/ovn/actions.h
>>>> @@ -238,7 +238,7 @@ struct ovnact_next {
>>>>  struct ovnact_load {
>>>>      struct ovnact ovnact;
>>>>      struct expr_field dst;
>>>> -    union expr_constant imm;
>>>> +    struct expr_constant imm;
>>>>  };
>>>>
>>>>  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
>>>> diff --git a/include/ovn/expr.h b/include/ovn/expr.h
>>>> index c48f82398..e54edb5bf 100644
>>>> --- a/include/ovn/expr.h
>>>> +++ b/include/ovn/expr.h
>>>> @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type type, enum
>> expr_relop *relop);
>>>>  struct expr {
>>>>      struct ovs_list node;       /* In parent EXPR_T_AND or EXPR_T_OR
>> if any. */
>>>>      enum expr_type type;        /* Expression type. */
>>>> -    char *as_name;              /* Address set name. Null if it is not
>> an
>>>> +    const char *as_name;        /* Address set name. Null if it is not
>> an
>>>>                                     address set. */
>>>>
>>>>      union {
>>>> @@ -505,40 +505,42 @@ enum expr_constant_type {
>>>>  };
>>>>
>>>>  /* A string or integer constant (one must know which from context). */
>>>> -union expr_constant {
>>>> -    /* Integer constant.
>>>> -     *
>>>> -     * The width of a constant isn't always clear, e.g. if you write
>> "1",
>>>> -     * there's no way to tell whether you mean for that to be a 1-bit
>> constant
>>>> -     * or a 128-bit constant or somewhere in between. */
>>>> -    struct {
>>>> -        union mf_subvalue value;
>>>> -        union mf_subvalue mask; /* Only initialized if 'masked'. */
>>>> -        bool masked;
>>>> -
>>>> -        enum lex_format format; /* From the constant's lex_token. */
>>>> -    };
>>>> +struct expr_constant {
>>>> +    const char *as_name;
>>>>
>>>> -    /* Null-terminated string constant. */
>>>> -    char *string;
>>>> +    union {
>>>> +        /* Integer constant.
>>>> +         *
>>>> +         * The width of a constant isn't always clear, e.g. if you
>> write "1",
>>>> +         * there's no way to tell whether you mean for that to be a
>> 1-bit
>>>> +         * constant or a 128-bit constant or somewhere in between. */
>>>> +        struct {
>>>> +            union mf_subvalue value;
>>>> +            union mf_subvalue mask; /* Only initialized if 'masked'. */
>>>> +            bool masked;
>>>> +
>>>> +            enum lex_format format; /* From the constant's lex_token.
>> */
>>>> +        };
>>>> +
>>>> +        /* Null-terminated string constant. */
>>>> +        char *string;
>>>> +    };
>>>>  };
>>>>
>>>>  bool expr_constant_parse(struct lexer *,
>>>>                           const struct expr_field *,
>>>> -                         union expr_constant *);
>>>> -void expr_constant_format(const union expr_constant *,
>>>> +                         struct expr_constant *);
>>>> +void expr_constant_format(const struct expr_constant *,
>>>>                            enum expr_constant_type, struct ds *);
>>>> -void expr_constant_destroy(const union expr_constant *,
>>>> +void expr_constant_destroy(const struct expr_constant *,
>>>>                             enum expr_constant_type);
>>>>
>>>>  /* A collection of "union expr_constant"s of the same type. */
>>>>  struct expr_constant_set {
>>>> -    union expr_constant *values;  /* Constants. */
>>>> +    struct expr_constant *values; /* Constants. */
>>>>      size_t n_values;              /* Number of constants. */
>>>>      enum expr_constant_type type; /* Type of the constants. */
>>>>      bool in_curlies;              /* Whether the constants were in {}.
>> */
>>>> -    char *as_name;                /* Name of an address set. It is
>> NULL if not
>>>> -                                     an address set. */
>>>>  };
>>>>
>>>>  bool expr_constant_set_parse(struct lexer *, struct expr_constant_set
>> *);
>>>> diff --git a/lib/actions.c b/lib/actions.c
>>>> index 71615fc53..6574c4df4 100644
>>>> --- a/lib/actions.c
>>>> +++ b/lib/actions.c
>>>> @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load *load,
>>>>              const struct ovnact_encode_params *ep,
>>>>              struct ofpbuf *ofpacts)
>>>>  {
>>>> -    const union expr_constant *c = &load->imm;
>>>> +    const struct expr_constant *c = &load->imm;
>>>>      struct mf_subfield dst = expr_resolve_field(&load->dst);
>>>>      struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
>> dst.field,
>>>>                                                         NULL, NULL);
>>>> @@ -2122,7 +2122,7 @@ encode_event_empty_lb_backends_opts(struct ofpbuf
>> *ofpacts,
>>>>          /* All empty_lb_backends fields are of type 'str' */
>>>>          ovs_assert(!strcmp(o->option->type, "str"));
>>>>
>>>> -        const union expr_constant *c = o->value.values;
>>>> +        const struct expr_constant *c = o->value.values;
>>>>          size_t size = strlen(c->string);
>>>>          struct controller_event_opt_header hdr =
>>>>              (struct controller_event_opt_header) {
>>>> @@ -2598,7 +2598,7 @@ validate_empty_lb_backends(struct action_context
>> *ctx,
>>>>  {
>>>>      for (size_t i = 0; i < n_options; i++) {
>>>>          const struct ovnact_gen_option *o = &options[i];
>>>> -        const union expr_constant *c = o->value.values;
>>>> +        const struct expr_constant *c = o->value.values;
>>>>          struct sockaddr_storage ss;
>>>>          struct uuid uuid;
>>>>
>>>> @@ -2801,7 +2801,7 @@ encode_put_dhcpv4_option(const struct
>> ovnact_gen_option *o,
>>>>      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>>>>      opt_header[0] = o->option->code;
>>>>
>>>> -    const union expr_constant *c = o->value.values;
>>>> +    const struct expr_constant *c = o->value.values;
>>>>      size_t n_values = o->value.n_values;
>>>>      if (!strcmp(o->option->type, "bool") ||
>>>>          !strcmp(o->option->type, "uint8")) {
>>>> @@ -2967,7 +2967,7 @@ encode_put_dhcpv6_option(const struct
>> ovnact_gen_option *o,
>>>>                           struct ofpbuf *ofpacts)
>>>>  {
>>>>      struct dhcpv6_opt_header *opt = ofpbuf_put_uninit(ofpacts, sizeof
>> *opt);
>>>> -    const union expr_constant *c = o->value.values;
>>>> +    const struct expr_constant *c = o->value.values;
>>>>      size_t n_values = o->value.n_values;
>>>>      size_t size;
>>>>
>>>> @@ -3023,7 +3023,7 @@ encode_PUT_DHCPV4_OPTS(const struct
>> ovnact_put_opts *pdo,
>>>>          find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_CODE);
>>>>      if (boot_opt) {
>>>>          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>>>> -        const union expr_constant *c = boot_opt->value.values;
>>>> +        const struct expr_constant *c = boot_opt->value.values;
>>>>          opt_header[0] = boot_opt->option->code;
>>>>          opt_header[1] = strlen(c->string);
>>>>          ofpbuf_put(ofpacts, c->string, opt_header[1]);
>>>> @@ -3033,7 +3033,7 @@ encode_PUT_DHCPV4_OPTS(const struct
>> ovnact_put_opts *pdo,
>>>>          find_opt(pdo->options, pdo->n_options,
>> DHCP_OPT_BOOTFILE_ALT_CODE);
>>>>      if (boot_alt_opt) {
>>>>          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>>>> -        const union expr_constant *c = boot_alt_opt->value.values;
>>>> +        const struct expr_constant *c = boot_alt_opt->value.values;
>>>>          opt_header[0] = boot_alt_opt->option->code;
>>>>          opt_header[1] = strlen(c->string);
>>>>          ofpbuf_put(ofpacts, c->string, opt_header[1]);
>>>> @@ -3043,7 +3043,7 @@ encode_PUT_DHCPV4_OPTS(const struct
>> ovnact_put_opts *pdo,
>>>>          pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
>>>>      if (next_server_opt) {
>>>>          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>>>> -        const union expr_constant *c = next_server_opt->value.values;
>>>> +        const struct expr_constant *c = next_server_opt->value.values;
>>>>          opt_header[0] = next_server_opt->option->code;
>>>>          opt_header[1] = sizeof(ovs_be32);
>>>>          ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
>>>> @@ -3247,7 +3247,7 @@ parse_put_nd_ra_opts(struct action_context *ctx,
>> const struct expr_field *dst,
>>>>      /* Let's validate the options. */
>>>>      for (size_t i = 0; i < po->n_options; i++) {
>>>>          const struct ovnact_gen_option *o = &po->options[i];
>>>> -        const union expr_constant *c = o->value.values;
>>>> +        const struct expr_constant *c = o->value.values;
>>>>          if (o->value.n_values > 1) {
>>>>              lexer_error(ctx->lexer, "Invalid value for \"%s\" option",
>>>>                          o->option->name);
>>>> @@ -3430,7 +3430,7 @@ static void
>>>>  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
>>>>                          struct ofpbuf *ofpacts, ptrdiff_t ra_offset)
>>>>  {
>>>> -    const union expr_constant *c = o->value.values;
>>>> +    const struct expr_constant *c = o->value.values;
>>>>
>>>>      switch (o->option->code) {
>>>>      case ND_RA_FLAG_ADDR_MODE:
>>>> diff --git a/lib/expr.c b/lib/expr.c
>>>> index 0cb44e1b6..286782025 100644
>>>> --- a/lib/expr.c
>>>> +++ b/lib/expr.c
>>>> @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct expr *a,
>> struct expr *b)
>>>>          } else {
>>>>              ovs_list_push_back(&a->andor, &b->node);
>>>>          }
>>>> -        free(a->as_name);
>>>>          a->as_name = NULL;
>>>>          return a;
>>>>      } else if (b->type == type) {
>>>>          ovs_list_push_front(&b->andor, &a->node);
>>>> -        free(b->as_name);
>>>>          b->as_name = NULL;
>>>>          return b;
>>>>      } else {
>>>> @@ -521,12 +519,13 @@ static bool parse_field(struct expr_context *,
>> struct expr_field *);
>>>>
>>>>  static struct expr *
>>>>  make_cmp__(const struct expr_field *f, enum expr_relop r,
>>>> -             const union expr_constant *c)
>>>> +             const struct expr_constant *c)
>>>>  {
>>>>      struct expr *e = xzalloc(sizeof *e);
>>>>      e->type = EXPR_T_CMP;
>>>>      e->cmp.symbol = f->symbol;
>>>>      e->cmp.relop = r;
>>>> +    e->as_name = c->as_name;
>>>>      if (f->symbol->width) {
>>>>          bitwise_copy(&c->value, sizeof c->value, 0,
>>>>                       &e->cmp.value, sizeof e->cmp.value, f->ofs,
>>>> @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f, enum
>> expr_relop r,
>>>>
>>>>  /* Returns the minimum reasonable width for integer constant 'c'. */
>>>>  static int
>>>> -expr_constant_width(const union expr_constant *c)
>>>> +expr_constant_width(const struct expr_constant *c)
>>>>  {
>>>>      if (c->masked) {
>>>>          return mf_subvalue_width(&c->mask);
>>>> @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
>>>>          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
>>>>                           e, make_cmp__(f, r, &cs->values[i]));
>>>>      }
>>>> -    /* Track address set */
>>>> -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
>>>> -        e->as_name = xstrdup(cs->as_name);
>>>> -    }
>>>> +
>>>>  exit:
>>>>      expr_constant_set_destroy(cs);
>>>>      return e;
>>>> @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context *ctx, struct
>> expr_constant_set *cs,
>>>>          }
>>>>      }
>>>>
>>>> -    struct expr_constant_set *addr_sets
>>>> -        = (ctx->addr_sets
>>>> -           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
>>>> -           : NULL);
>>>> -    if (!addr_sets) {
>>>> +    struct shash_node *node = ctx->addr_sets
>>>> +                              ? shash_find(ctx->addr_sets,
>> ctx->lexer->token.s)
>>>> +                              : NULL;
>>>> +    if (!node) {
>>>>          lexer_syntax_error(ctx->lexer, "expecting address set name");
>>>>          return false;
>>>>      }
>>>> @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context *ctx, struct
>> expr_constant_set *cs,
>>>>          return false;
>>>>      }
>>>>
>>>> -    if (!cs->n_values) {
>>>> -        cs->as_name = xstrdup(ctx->lexer->token.s);
>>>> -    }
>>>> -
>>>> +    struct expr_constant_set *addr_sets = node->data;
>>>>      size_t n_values = cs->n_values + addr_sets->n_values;
>>>>      if (n_values >= *allocated_values) {
>>>>          cs->values = xrealloc(cs->values, n_values * sizeof
>> *cs->values);
>>>>          *allocated_values = n_values;
>>>>      }
>>>>      for (size_t i = 0; i < addr_sets->n_values; i++) {
>>>> -        cs->values[cs->n_values++] = addr_sets->values[i];
>>>> +        struct expr_constant *c = &cs->values[cs->n_values++];
>>>> +        *c = addr_sets->values[i];
>>>> +        c->as_name = node->name;
>>
>> I am not sure if it is safe enough to just store the reference. We need to
>> check carefully if there is any chance the pointer becomes dangling when
>> the corresponding address set is destroyed or in any other circumstances.
>>
> 
> Currently it is safe to store as it is only for the duration of the whole
> expression, in the current code base the set outlives the expression.
> However it might be a problem later on, I don't know how viable it would be
> in terms of memory to store clone.
> 
> 
>>
>>>>      }
>>>>
>>>>      return true;
>>>> @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx, struct
>> expr_constant_set *cs,
>>>>          *allocated_values = n_values;
>>>>      }
>>>>      for (size_t i = 0; i < port_group->n_values; i++) {
>>>> -        cs->values[cs->n_values++].string =
>>>> -            xstrdup(port_group->values[i].string);
>>>> +        struct expr_constant *c = &cs->values[cs->n_values++];
>>>> +        c->string = xstrdup(port_group->values[i].string);
>>>> +        c->as_name = NULL;
>>>>      }
>>>>
>>>>      return true;
>>>> @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx, struct
>> expr_constant_set *cs,
>>>>                                  sizeof *cs->values);
>>>>      }
>>>>
>>>> -    /* Combining other values to the constant set that is tracking an
>>>> -     * address set, so untrack it. */
>>>> -    free(cs->as_name);
>>>> -    cs->as_name = NULL;
>>>> -
>>>>      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
>>>>          lexer_error(ctx->lexer, "Unexpanded template.");
>>>>          return false;
>>>> @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx, struct
>> expr_constant_set *cs,
>>>>          if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
>>>>              return false;
>>>>          }
>>>> -        cs->values[cs->n_values++].string =
>> xstrdup(ctx->lexer->token.s);
>>>> +        struct expr_constant *c = &cs->values[cs->n_values++];
>>>> +        c->string = xstrdup(ctx->lexer->token.s);
>>>> +        c->as_name = NULL;
>>>>          lexer_get(ctx->lexer);
>>>>          return true;
>>>>      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
>>>> @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx, struct
>> expr_constant_set *cs,
>>>>              return false;
>>>>          }
>>>>
>>>> -        union expr_constant *c = &cs->values[cs->n_values++];
>>>> +        struct expr_constant *c = &cs->values[cs->n_values++];
>>>>          c->value = ctx->lexer->token.value;
>>>>          c->format = ctx->lexer->token.format;
>>>>          c->masked = ctx->lexer->token.type == LEX_T_MASKED_INTEGER;
>>>>          if (c->masked) {
>>>>              c->mask = ctx->lexer->token.mask;
>>>>          }
>>>> +        c->as_name = NULL;
>>>>          lexer_get(ctx->lexer);
>>>>          return true;
>>>>      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
>>>> @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context *ctx, struct
>> expr_constant_set *cs)
>>>>   * indeterminate. */
>>>>  bool
>>>>  expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
>>>> -                    union expr_constant *c)
>>>> +                    struct expr_constant *c)
>>>>  {
>>>>      if (lexer->error) {
>>>>          return false;
>>>> @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer, const
>> struct expr_field *f,
>>>>  /* Appends to 's' a re-parseable representation of constant 'c' with
>> the given
>>>>   * 'type'. */
>>>>  void
>>>> -expr_constant_format(const union expr_constant *c,
>>>> +expr_constant_format(const struct expr_constant *c,
>>>>                       enum expr_constant_type type, struct ds *s)
>>>>  {
>>>>      if (type == EXPR_C_STRING) {
>>>> @@ -1010,7 +1003,7 @@ expr_constant_format(const union expr_constant *c,
>>>>   *
>>>>   * Does not free(c). */
>>>>  void
>>>> -expr_constant_destroy(const union expr_constant *c,
>>>> +expr_constant_destroy(const struct expr_constant *c,
>>>>                        enum expr_constant_type type)
>>>>  {
>>>>      if (c && type == EXPR_C_STRING) {
>>>> @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct
>> expr_constant_set *cs, struct ds *s)
>>>>          ds_put_char(s, '{');
>>>>      }
>>>>
>>>> -    for (const union expr_constant *c = cs->values;
>>>> +    for (const struct expr_constant *c = cs->values;
>>>>           c < &cs->values[cs->n_values]; c++) {
>>>>          if (c != cs->values) {
>>>>              ds_put_cstr(s, ", ");
>>>> @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
>> expr_constant_set *cs)
>>>>              }
>>>>          }
>>>>          free(cs->values);
>>>> -        free(cs->as_name);
>>>>      }
>>>>  }
>>>>
>>>>  static int
>>>>  compare_expr_constant_integer_cb(const void *a_, const void *b_)
>>>>  {
>>>> -    const union expr_constant *a = a_;
>>>> -    const union expr_constant *b = b_;
>>>> +    const struct expr_constant *a = a_;
>>>> +    const struct expr_constant *b = b_;
>>>>
>>>>      int d = memcmp(&a->value, &b->value, sizeof a->value);
>>>>      if (d) {
>>>> @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const char
>> *const *values, size_t n_values)
>>>>              VLOG_WARN("Invalid constant set entry: '%s', token type:
>> %d",
>>>>                        values[i], lex.token.type);
>>>>          } else {
>>>> -            union expr_constant *c = &cs->values[cs->n_values++];
>>>> +            struct expr_constant *c = &cs->values[cs->n_values++];
>>>>              c->value = lex.token.value;
>>>>              c->format = lex.token.format;
>>>>              c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
>>>> @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const char
>> *const *values, size_t n_values)
>>>>
>>>>  static void
>>>>  expr_constant_set_add_value(struct expr_constant_set **p_cs,
>>>> -                            union expr_constant *c, size_t *allocated)
>>>> +                            struct expr_constant *c, size_t *allocated)
>>>>  {
>>>>      struct expr_constant_set *cs = *p_cs;
>>>>      if (!cs) {
>>>> @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash
>> *const_sets, const char *name,
>>>>                          values[i], name);
>>>>              continue;
>>>>          }
>>>> -        union expr_constant *c = &cs->values[cs->n_values++];
>>>> +        struct expr_constant *c = &cs->values[cs->n_values++];
>>>>          c->string = xstrdup(values[i]);
>>>>      }
>>>>
>>>> @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context *ctx, bool
>> *atomic)
>>>>
>>>>              *atomic = true;
>>>>
>>>> -            union expr_constant *cst = xzalloc(sizeof *cst);
>>>> +            struct expr_constant *cst = xzalloc(sizeof *cst);
>>>>              cst->format = LEX_F_HEXADECIMAL;
>>>>              cst->masked = false;
>>>>
>>>> @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context *ctx, bool
>> *atomic)
>>>>              c.values = cst;
>>>>              c.n_values = 1;
>>>>              c.in_curlies = false;
>>>> -            c.as_name = NULL;
>>>>              return make_cmp(ctx, &f, EXPR_R_NE, &c);
>>>>          } else if (parse_relop(ctx, &r) && parse_constant_set(ctx,
>> &c)) {
>>>>              return make_cmp(ctx, &f, r, &c);
>>>> @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash *symtab)
>>>>  static struct expr *
>>>>  expr_clone_cmp(struct expr *expr)
>>>>  {
>>>> -    ovs_assert(!expr->as_name);
>>>>      struct expr *new = xmemdup(expr, sizeof *expr);
>>>>      if (!new->cmp.symbol->width) {
>>>>          new->cmp.string = xstrdup(new->cmp.string);
>>>> @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
>>>>  static struct expr *
>>>>  expr_clone_condition(struct expr *expr)
>>>>  {
>>>> -    ovs_assert(!expr->as_name);
>>>>      struct expr *new = xmemdup(expr, sizeof *expr);
>>>>      new->cond.string = xstrdup(new->cond.string);
>>>>      return new;
>>>> @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
>>>>          return;
>>>>      }
>>>>
>>>> -    free(expr->as_name);
>>>> -
>>>>      struct expr *sub;
>>>>
>>>>      switch (expr->type) {
>>>> @@ -2567,7 +2554,6 @@ crush_or_supersets(struct expr *expr, const
>> struct expr_symbol *symbol)
>>>>       * mask sizes. */
>>>>      size_t n = ovs_list_size(&expr->andor);
>>>>      struct expr **subs = xmalloc(n * sizeof *subs);
>>>> -    bool modified = false;
>>>>      /* Linked list over the 'subs' array to quickly skip deleted
>> elements,
>>>>       * i.e. the index of the next potentially non-NULL element. */
>>>>      size_t *next = xmalloc(n * sizeof *next);
>>>> @@ -2596,14 +2582,14 @@ crush_or_supersets(struct expr *expr, const
>> struct expr_symbol *symbol)
>>>>              next[last] = i;
>>>>              last = i;
>>>>          } else {
>>>> +            /* Remove address set reference from the duplicate. */
>>>> +            subs[last]->as_name = NULL;
>>>>              expr_destroy(subs[i]);
>>>>              subs[i] = NULL;
>>>> -            modified = true;
>>>>          }
>>>>      }
>>>>
>>>> -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
>>>> -        || (!modified && expr->as_name)) {
>>>> +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL) {
>>>>          /* Not a fully maskable field or this expression is tracking an
>>>>           * address set.  Don't try to optimize to preserve address set
>> I-P. */
>>
>> The above comment needs to be updated since it obviously doesn't preserve
>> address set I-P any more. What's the reason it is not considered any more?
>>
> 
> Because of the AS reference per sub expression we can actually allow this
> optimization to happen. In the case of the smaller mask we will lose track
> of only that address instead of the whole set. I'll update the comment.
> 
> 
>>
>>>>          goto done;
>>>> @@ -2658,10 +2644,11 @@ crush_or_supersets(struct expr *expr, const
>> struct expr_symbol *symbol)
>>>>              if (expr_bitmap_intersect_check(a_value, a_mask, b_value,
>> b_mask,
>>>>                                              bit_width)
>>>>                  && bitmap_is_superset(b_mask, a_mask, bit_width)) {
>>>> -                /* 'a' is the same expression with a smaller mask. */
>>>> +                /* 'a' is the same expression with a smaller mask.
>>>> +                 * Remove address set reference from the duplicate. */
>>>> +                a->as_name = NULL;
>>>>                  expr_destroy(subs[j]);
>>>>                  subs[j] = NULL;
>>>> -                modified = true;
>>>>
>>>>                  /* Shorten the path for the next round. */
>>>>                  if (last) {
>>>> @@ -2685,12 +2672,6 @@ done:
>>>>          }
>>>>      }
>>>>
>>>> -    if (modified) {
>>>> -        /* Members modified, so untrack address set. */
>>>> -        free(expr->as_name);
>>>> -        expr->as_name = NULL;
>>>> -    }
>>>> -
>>>>      free(next);
>>>>      free(subs);
>>>>      return expr;
>>>> @@ -3271,10 +3252,10 @@ add_disjunction(const struct expr *or,
>>>>      LIST_FOR_EACH (sub, node, &or->andor) {
>>>>          struct expr_match *match = expr_match_new(m, clause, n_clauses,
>>>>                                                    conj_id);
>>>> -        if (or->as_name) {
>>>> +        if (sub->as_name) {
>>>>              ovs_assert(sub->type == EXPR_T_CMP);
>>>>              ovs_assert(sub->cmp.symbol->width);
>>>> -            match->as_name = xstrdup(or->as_name);
>>>> +            match->as_name = xstrdup(sub->as_name);
>>>>              match->as_ip = sub->cmp.value.ipv6;
>>>>              match->as_mask = sub->cmp.mask.ipv6;
>>>>          }
>>>> diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
>>>> index fdcc5aab2..626dd34f6 100644
>>>> --- a/tests/ovn-controller.at
>>>> +++ b/tests/ovn-controller.at
>>>> @@ -1630,7 +1630,9 @@
>> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
>> actions=lo
>>>>  done
>>>>
>>>>  reprocess_count_new=$(read_counter consider_logical_flow)
>>>> -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
>> [10
>>>> +# First 2 reprocess are due to change from 1 IP in AS to 2.
>>>> +# Last 5 is due to overlap in IP addresses between as1 and as2.
>>>> +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
>> [7
>>>>  ])
>>>>
>>>>  # Remove the IPs from as1 and as2, 1 IP each time.
>>>> @@ -1653,9 +1655,9 @@ for i in $(seq 10); do
>>>>  done
>>>>
>>>>  reprocess_count_new=$(read_counter consider_logical_flow)
>>>> -# In this case the as1 and as2 are merged to a single OR expr, so we
>> lose track of
>>>> -# address set information - can't handle deletion incrementally.
>>>> -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
>> [10
>>>> +# First 5 are due to overlap in IP addresses between as1 and as2.
>>>> +# Last 2 are due to change from 2 IP in AS to 1.
>>>> +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
>> [7
>>>>  ])
>>>>
>>>>  OVN_CLEANUP([hv1])
>>>> @@ -1822,7 +1824,7 @@
>> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
>> actions=co
>>>>
>>  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
>> actions=conjunction,2/2)
>>>>  ])
>>>>  reprocess_count_new=$(read_counter consider_logical_flow)
>>>> -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
>> [1
>>>> +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
>> [0
>>>>  ])
>>>>
>>>>  # Delete 2 IPs
>>>> @@ -1842,7 +1844,7 @@
>> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
>> actions=co
>>>>
>>  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
>> actions=conjunction,2/2)
>>>>  ])
>>>>  reprocess_count_new=$(read_counter consider_logical_flow)
>>>> -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
>> [1
>>>> +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
>> [0
>>>>  ])
>>>>
>>>>  OVN_CLEANUP([hv1])
>>>> --
>>>> 2.44.0
>>>>
>>>
>>> Please consider it as RFC, I forgot to add the tag into subject.
>>
>> Is there a reason why this is RFC? Do you have any TODOs in mind?
>>
> 
> Mainly to be sure if this approach is heading the right direction, which it
> seems it does. There shouldn't be any additional TODO right now.
> 
> 
>> I am just not 100% confident about corner cases. The existing test cases
>> seem to be handled correctly. We might need to think about if there are any
>> other potentially risky scenarios that need to be tested, although I don't
>> see any obvious problem for now.
>>
> 
> 
> So things that came to my mind are covered by the tests, let's see if
> others will think about potentially dangerous scenarios.
> 
> 
>> Thanks,
>> Han
>>
>>> --
>>>
>>> Ales Musil
>>>
>>> Senior Software Engineer - OVN Core
>>>
>>> Red Hat EMEA
>>>
>>> amu...@redhat.com
>>
> 
> 
> I'll wait a bit for feedback from others before posting v2.
> 

Overall this looks OK to me.  Looking forward to v2.

Regards,
Dumitru

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

Reply via email to