[ovs-dev] [PATCH 4/4] ofp-flow: Reduce memory consumption for ofputil_flow_mod, using minimatch.

Armando M. armamig at gmail.com
Thu Mar 29 06:58:08 UTC 2018


On 20 March 2018 at 13:46, Ben Pfaff <blp at ovn.org> wrote:

> Until now, struct ofputil_flow_mod, which represents an OpenFlow flow table
> modification request, has incorporated a struct match, which made the
> overall ofputil_flow_mod about 2.5 kB.  This is OK for a small number of
> flows, but absurdly inflates memory requirements when there are hundreds of
> thousands of flows.  This commit fixes the problem by changing struct match
> to struct minimatch inside ofputil_flow_mod, which reduces its size to
> about 100 bytes plus the actual size of the flow match (usually a few dozen
> bytes).
>
> This affects memory usage of ovs-ofctl (when it adds a large number of
> flows) more than ovs-vswitchd.
>
> Reported-by: Michael Ben-Ami <mbenami at digitalocean.com>
> Signed-off-by: Ben Pfaff <blp at ovn.org>
> ---
>  AUTHORS.rst                    |  1 +
>  include/openvswitch/ofp-flow.h |  2 +-
>  lib/learn.c                    | 12 ++++--
>  lib/learning-switch.c          | 14 +++++--
>  lib/ofp-bundle.c               |  1 +
>  lib/ofp-flow.c                 | 89 ++++++++++++++++++++++++++----
> ------------
>  ofproto/ofproto-dpif-xlate.c   |  6 ++-
>  ofproto/ofproto-dpif.c         | 17 ++++----
>  ofproto/ofproto-dpif.h         |  2 +-
>  ofproto/ofproto-provider.h     |  2 +-
>  ofproto/ofproto.c              | 49 ++++++++++++++---------
>  ovn/controller/ofctrl.c        | 22 ++++++-----
>  utilities/ovs-ofctl.c          | 81 +++++++++++++++++++++++-------
> --------
>  13 files changed, 185 insertions(+), 113 deletions(-)
>
> diff --git a/AUTHORS.rst b/AUTHORS.rst
> index dc69ba3f3c1f..81f9b6f28b88 100644
> --- a/AUTHORS.rst
> +++ b/AUTHORS.rst
> @@ -506,6 +506,7 @@ Marvin Pascual                  marvin at pascual.com.ph
>  Maxime Brun                     m.brun at alphalink.fr
>  Madhu Venugopal                 mavenugo at gmail.com
>  Michael A. Collins              mike.a.collins at ark-net.org
> +Michael Ben-Ami                 mbenami at digitalocean.com
>  Michael Hu                      mhu at nicira.com
>  Michael J. Smalley              michaeljsmalley at gmail.com
>  Michael Mao                     mmao at nicira.com
> diff --git a/include/openvswitch/ofp-flow.h b/include/openvswitch/ofp-flow
> .h
> index 17d48f12e060..2ff2e45b66c4 100644
> --- a/include/openvswitch/ofp-flow.h
> +++ b/include/openvswitch/ofp-flow.h
> @@ -73,7 +73,7 @@ void ofputil_flow_mod_flags_format(struct ds *, enum
> ofputil_flow_mod_flags);
>  struct ofputil_flow_mod {
>      struct ovs_list list_node; /* For queuing flow_mods. */
>
> -    struct match match;
> +    struct minimatch match;
>      int priority;
>
>      /* Cookie matching.  The flow_mod affects only flows that have
> cookies that
> diff --git a/lib/learn.c b/lib/learn.c
> index f5d15503fe79..c4d5b3e0c449 100644
> --- a/lib/learn.c
> +++ b/lib/learn.c
> @@ -91,14 +91,17 @@ learn_check(const struct ofpact_learn *learn, const
> struct match *src_match)
>   * 'ofpacts' and retains ownership of it.  'fm->ofpacts' will point into
> the
>   * 'ofpacts' buffer.
>   *
> + * The caller must eventually destroy fm->match.
> + *
>   * The caller has to actually execute 'fm'. */
>  void
>  learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
>                struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
>  {
>      const struct ofpact_learn_spec *spec;
> +    struct match match;
>
> -    match_init_catchall(&fm->match);
> +    match_init_catchall(&match);
>      fm->priority = learn->priority;
>      fm->cookie = htonll(0);
>      fm->cookie_mask = htonll(0);
> @@ -140,10 +143,10 @@ learn_execute(const struct ofpact_learn *learn,
> const struct flow *flow,
>
>          switch (spec->dst_type) {
>          case NX_LEARN_DST_MATCH:
> -            mf_write_subfield(&spec->dst, &value, &fm->match);
> -            match_add_ethernet_prereq(&fm->match, spec->dst.field);
> +            mf_write_subfield(&spec->dst, &value, &match);
> +            match_add_ethernet_prereq(&match, spec->dst.field);
>              mf_vl_mff_set_tlv_bitmap(
> -                spec->dst.field, &fm->match.flow.tunnel.metadat
> a.present.map);
> +                spec->dst.field, &match.flow.tunnel.metadata.pr
> esent.map);
>              break;
>
>          case NX_LEARN_DST_LOAD:
> @@ -173,6 +176,7 @@ learn_execute(const struct ofpact_learn *learn, const
> struct flow *flow,
>          }
>      }
>
> +    minimatch_init(&fm->match, &match);
>      fm->ofpacts = ofpacts->data;
>      fm->ofpacts_len = ofpacts->size;
>  }
> diff --git a/lib/learning-switch.c b/lib/learning-switch.c
> index 3a9e015bbe9c..04eaa6e8e73f 100644
> --- a/lib/learning-switch.c
> +++ b/lib/learning-switch.c
> @@ -206,7 +206,6 @@ lswitch_handshake(struct lswitch *sw)
>          output.max_len = OFP_DEFAULT_MISS_SEND_LEN;
>
>          struct ofputil_flow_mod fm = {
> -            .match = MATCH_CATCHALL_INITIALIZER,
>              .priority = 0,
>              .table_id = 0,
>              .command = OFPFC_ADD,
> @@ -216,8 +215,10 @@ lswitch_handshake(struct lswitch *sw)
>              .ofpacts = &output.ofpact,
>              .ofpacts_len = sizeof output,
>          };
> -
> +        minimatch_init_catchall(&fm.match);
>          msg = ofputil_encode_flow_mod(&fm, protocol);
> +        minimatch_destroy(&fm.match);
> +
>          error = rconn_send(sw->rconn, msg, NULL);
>          if (error) {
>              VLOG_INFO_RL(&rl, "%s: failed to add default flow (%s)",
> @@ -595,11 +596,16 @@ process_packet_in(struct lswitch *sw, const struct
> ofp_header *oh)
>              .ofpacts = ofpacts.data,
>              .ofpacts_len = ofpacts.size,
>          };
> -        match_init(&fm.match, &flow, &sw->wc);
> -        ofputil_normalize_match_quiet(&fm.match);
> +
> +        struct match match;
> +        match_init(&match, &flow, &sw->wc);
> +        ofputil_normalize_match_quiet(&match);
> +        minimatch_init(&fm.match, &match);
>
>          struct ofpbuf *buffer = ofputil_encode_flow_mod(&fm,
> sw->protocol);
>
> +        minimatch_destroy(&fm.match);
> +
>          queue_tx(sw, buffer);
>
>          /* If the switch didn't buffer the packet, we need to send a
> copy. */
> diff --git a/lib/ofp-bundle.c b/lib/ofp-bundle.c
> index 0921c81bfb15..8f07a30c51f7 100644
> --- a/lib/ofp-bundle.c
> +++ b/lib/ofp-bundle.c
> @@ -33,6 +33,7 @@ ofputil_free_bundle_msgs(struct ofputil_bundle_msg
> *bms, size_t n_bms)
>          switch ((int)bms[i].type) {
>          case OFPTYPE_FLOW_MOD:
>              free(CONST_CAST(struct ofpact *, bms[i].fm.ofpacts));
> +            minimatch_destroy(&bms[i].fm.match);
>              break;
>          case OFPTYPE_GROUP_MOD:
>              ofputil_uninit_group_mod(&bms[i].gm);
> diff --git a/lib/ofp-flow.c b/lib/ofp-flow.c
> index cffadb30381b..d07556245b1f 100644
> --- a/lib/ofp-flow.c
> +++ b/lib/ofp-flow.c
> @@ -139,6 +139,8 @@ ofputil_flow_mod_flags_format(struct ds *s, enum
> ofputil_flow_mod_flags flags)
>   * The caller must initialize 'ofpacts' and retains ownership of it.
>   * 'fm->ofpacts' will point into the 'ofpacts' buffer.
>   *
> + * On success, the caller must eventually destroy fm->match.
> + *
>   * Does not validate the flow_mod actions.  The caller should do that,
> with
>   * ofpacts_check(). */
>  enum ofperr
> @@ -152,6 +154,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>  {
>      ovs_be16 raw_flags;
>      enum ofperr error;
> +    struct match match;
>      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
>      enum ofpraw raw = ofpraw_pull_assert(&b);
>      if (raw == OFPRAW_OFPT11_FLOW_MOD) {
> @@ -160,7 +163,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>
>          ofm = ofpbuf_pull(&b, sizeof *ofm);
>
> -        error = ofputil_pull_ofp11_match(&b, tun_table, vl_mff_map,
> &fm->match,
> +        error = ofputil_pull_ofp11_match(&b, tun_table, vl_mff_map,
> &match,
>                                           NULL);
>          if (error) {
>              return error;
> @@ -228,8 +231,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>              ofm = ofpbuf_pull(&b, sizeof *ofm);
>
>              /* Translate the rule. */
> -            ofputil_match_from_ofp10_match(&ofm->match, &fm->match);
> -            ofputil_normalize_match(&fm->match);
> +            ofputil_match_from_ofp10_match(&ofm->match, &match);
> +            ofputil_normalize_match(&match);
>
>              /* OpenFlow 1.0 says that exact-match rules have to have the
>               * highest possible priority. */
> @@ -256,7 +259,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>              /* Dissect the message. */
>              nfm = ofpbuf_pull(&b, sizeof *nfm);
>              error = nx_pull_match(&b, ntohs(nfm->match_len),
> -                                  &fm->match, &fm->cookie,
> &fm->cookie_mask,
> +                                  &match, &fm->cookie, &fm->cookie_mask,
>                                    false, tun_table, vl_mff_map);
>              if (error) {
>                  return error;
> @@ -269,6 +272,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>                   * existing cookie. */
>                  return OFPERR_NXBRC_NXM_INVALID;
>              }
> +            minimatch_init(&fm->match, &match);
>              fm->priority = ntohs(nfm->priority);
>              fm->new_cookie = nfm->cookie;
>              fm->idle_timeout = ntohs(nfm->idle_timeout);
> @@ -298,11 +302,11 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>
>      /* Check for mismatched conntrack original direction tuple address
> fields
>       * w.r.t. the IP version of the match. */
> -    if (((fm->match.wc.masks.ct_nw_src || fm->match.wc.masks.ct_nw_dst)
> -         && fm->match.flow.dl_type != htons(ETH_TYPE_IP))
> -        || ((ipv6_addr_is_set(&fm->match.wc.masks.ct_ipv6_src)
> -             || ipv6_addr_is_set(&fm->match.wc.masks.ct_ipv6_dst))
> -            && fm->match.flow.dl_type != htons(ETH_TYPE_IPV6))) {
> +    if (((match.wc.masks.ct_nw_src || match.wc.masks.ct_nw_dst)
> +         && match.flow.dl_type != htons(ETH_TYPE_IP))
> +        || ((ipv6_addr_is_set(&match.wc.masks.ct_ipv6_src)
> +             || ipv6_addr_is_set(&match.wc.masks.ct_ipv6_dst))
> +            && match.flow.dl_type != htons(ETH_TYPE_IPV6))) {
>          return OFPERR_OFPBAC_MATCH_INCONSISTENT;
>      }
>
> @@ -339,9 +343,13 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>                  : OFPERR_OFPFMFC_TABLE_FULL);
>      }
>
> -    return ofpacts_check_consistency(fm->ofpacts, fm->ofpacts_len,
> -                                     &fm->match, max_port,
> -                                     fm->table_id, max_table, protocol);
> +    error = ofpacts_check_consistency(fm->ofpacts, fm->ofpacts_len,
> +                                      &match, max_port,
> +                                      fm->table_id, max_table, protocol);
> +    if (!error) {
> +        minimatch_init(&fm->match, &match);
> +    }
> +    return error;
>  }
>
>  static ovs_be16
> @@ -363,6 +371,9 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod
> *fm,
>      ovs_be16 raw_flags = ofputil_encode_flow_mod_flags(fm->flags,
> version);
>      struct ofpbuf *msg;
>
> +    struct match match;
> +    minimatch_expand(&fm->match, &match);
> +
>      switch (protocol) {
>      case OFPUTIL_P_OF11_STD:
>      case OFPUTIL_P_OF12_OXM:
> @@ -407,7 +418,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod
> *fm,
>          } else {
>              ofm->importance = 0;
>          }
> -        ofputil_put_ofp11_match(msg, &fm->match, protocol);
> +        ofputil_put_ofp11_match(msg, &match, protocol);
>          ofpacts_put_openflow_instructions(fm->ofpacts, fm->ofpacts_len,
> msg,
>                                            version);
>          break;
> @@ -420,7 +431,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod
> *fm,
>          msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION,
>                             fm->ofpacts_len);
>          ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
> -        ofputil_match_to_ofp10_match(&fm->match, &ofm->match);
> +        ofputil_match_to_ofp10_match(&match, &ofm->match);
>          ofm->cookie = fm->new_cookie;
>          ofm->command = ofputil_tid_command(fm, protocol);
>          ofm->idle_timeout = htons(fm->idle_timeout);
> @@ -444,7 +455,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod
> *fm,
>          nfm = ofpbuf_put_zeros(msg, sizeof *nfm);
>          nfm->command = ofputil_tid_command(fm, protocol);
>          nfm->cookie = fm->new_cookie;
> -        match_len = nx_put_match(msg, &fm->match, fm->cookie,
> fm->cookie_mask);
> +        match_len = nx_put_match(msg, &match, fm->cookie,
> fm->cookie_mask);
>          nfm = msg->msg;
>          nfm->idle_timeout = htons(fm->idle_timeout);
>          nfm->hard_timeout = htons(fm->hard_timeout);
> @@ -536,7 +547,9 @@ ofputil_flow_mod_format(struct ds *s, const struct
> ofp_header *oh,
>          /* nx_match_to_string() doesn't print priority. */
>          need_priority = true;
>      } else {
> -        match_format(&fm.match, port_map, s, fm.priority);
> +        struct match match;
> +        minimatch_expand(&fm.match, &match);
> +        match_format(&match, port_map, s, fm.priority);
>
>          /* match_format() does print priority. */
>          need_priority = false;
> @@ -589,6 +602,7 @@ ofputil_flow_mod_format(struct ds *s, const struct
> ofp_header *oh,
>      };
>      ofpacts_format(fm.ofpacts, fm.ofpacts_len, &fp);
>      ofpbuf_uninit(&ofpacts);
> +    minimatch_destroy(&fm.match);
>
>      return 0;
>  }
> @@ -831,10 +845,13 @@ parse_ofp_flow_stats_request_str(struct
> ofputil_flow_stats_request *fsr,
>      fsr->aggregate = aggregate;
>      fsr->cookie = fm.cookie;
>      fsr->cookie_mask = fm.cookie_mask;
> -    fsr->match = fm.match;
> +    minimatch_expand(&fm.match, &fsr->match);
>      fsr->out_port = fm.out_port;
>      fsr->out_group = fm.out_group;
>      fsr->table_id = fm.table_id;
> +
> +    minimatch_destroy(&fm.match);
> +
>      return NULL;
>  }
>
> @@ -1393,7 +1410,6 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> command, char *string,
>      }
>
>      *fm = (struct ofputil_flow_mod) {
> -        .match = MATCH_CATCHALL_INITIALIZER,
>          .priority = OFP_DEFAULT_PRIORITY,
>          .table_id = 0xff,
>          .command = command,
> @@ -1413,19 +1429,20 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> command, char *string,
>          }
>      }
>
> +    struct match match = MATCH_CATCHALL_INITIALIZER;
>      while (ofputil_parse_key_value(&string, &name, &value)) {
>          const struct ofp_protocol *p;
>          const struct mf_field *mf;
>          char *error = NULL;
>
>          if (ofp_parse_protocol(name, &p)) {
> -            match_set_dl_type(&fm->match, htons(p->dl_type));
> +            match_set_dl_type(&match, htons(p->dl_type));
>              if (p->nw_proto) {
> -                match_set_nw_proto(&fm->match, p->nw_proto);
> +                match_set_nw_proto(&match, p->nw_proto);
>              }
> -            match_set_default_packet_type(&fm->match);
> +            match_set_default_packet_type(&match);
>          } else if (!strcmp(name, "eth")) {
> -            match_set_packet_type(&fm->match, htonl(PT_ETH));
> +            match_set_packet_type(&match, htonl(PT_ETH));
>          } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) {
>              fm->flags |= OFPUTIL_FF_SEND_FLOW_REM;
>          } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) {
> @@ -1444,9 +1461,9 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> command, char *string,
>               /* ignore these fields. */
>          } else if ((mf = mf_from_name(name)) != NULL) {
>              error = ofp_parse_field(mf, value, port_map,
> -                                    &fm->match, usable_protocols);
> +                                    &match, usable_protocols);
>          } else if (strchr(name, '[')) {
> -            error = parse_subfield(name, value, &fm->match,
> usable_protocols);
> +            error = parse_subfield(name, value, &match, usable_protocols);
>          } else {
>              if (!*value) {
>                  return xasprintf("field %s missing value", name);
> @@ -1532,13 +1549,13 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> command, char *string,
>      }
>      /* Copy ethertype to flow->dl_type for matches on packet_type
>       * (OFPHTN_ETHERTYPE, ethertype). */
> -    if (fm->match.wc.masks.packet_type == OVS_BE32_MAX &&
> -            pt_ns(fm->match.flow.packet_type) == OFPHTN_ETHERTYPE) {
> -        fm->match.flow.dl_type = pt_ns_type_be(fm->match.flow.p
> acket_type);
> +    if (match.wc.masks.packet_type == OVS_BE32_MAX &&
> +            pt_ns(match.flow.packet_type) == OFPHTN_ETHERTYPE) {
> +        match.flow.dl_type = pt_ns_type_be(match.flow.packet_type);
>      }
>      /* Check for usable protocol interdependencies between match fields.
> */
> -    if (fm->match.flow.dl_type == htons(ETH_TYPE_IPV6)) {
> -        const struct flow_wildcards *wc = &fm->match.wc;
> +    if (match.flow.dl_type == htons(ETH_TYPE_IPV6)) {
> +        const struct flow_wildcards *wc = &match.wc;
>          /* Only NXM and OXM support matching L3 and L4 fields within IPv6.
>           *
>           * (IPv6 specific fields as well as arp_sha, arp_tha, nw_frag, and
> @@ -1574,7 +1591,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> command, char *string,
>          if (!error) {
>              enum ofperr err;
>
> -            err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match,
> +            err = ofpacts_check(ofpacts.data, ofpacts.size, &match,
>                                  OFPP_MAX, fm->table_id, 255,
> usable_protocols);
>              if (!err && !*usable_protocols) {
>                  err = OFPERR_OFPBAC_MATCH_INCONSISTENT;
> @@ -1596,6 +1613,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> command, char *string,
>          fm->ofpacts_len = 0;
>          fm->ofpacts = NULL;
>      }
> +    minimatch_init(&fm->match, &match);
>
>      return NULL;
>  }
> @@ -1613,7 +1631,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> command, char *string,
>   * name is treated as "add".
>   *
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
> - * error.  The caller is responsible for freeing the returned string. */
> + * error.  The caller is responsible for freeing the returned string.
> + *
> + * On success, the caller is responsible for freeing fm->ofpacts and
> + * fm->match. */
>  char * OVS_WARN_UNUSED_RESULT
>  parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
>                const struct ofputil_port_map *port_map,
> @@ -1657,8 +1678,9 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm,
> const char *string,
>          /* Normalize a copy of the match.  This ensures that
> non-normalized
>           * flows get logged but doesn't affect what gets sent to the
> switch, so
>           * that the switch can do whatever it likes with the flow. */
> -        struct match match_copy = fm->match;
> -        ofputil_normalize_match(&match_copy);
> +        struct match match;
> +        minimatch_expand(&fm->match, &match);
> +        ofputil_normalize_match(&match);
>      }
>
>      return error;
> @@ -1716,6 +1738,7 @@ parse_ofp_flow_mod_file(const char *file_name,
>
>              for (i = 0; i < *n_fms; i++) {
>                  free(CONST_CAST(struct ofpact *, (*fms)[i].ofpacts));
> +                minimatch_destroy(&(*fms)[i].match);
>              }
>              free(*fms);
>              *fms = NULL;
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index bc6429c25346..a00bed704806 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -5019,7 +5019,9 @@ xlate_learn_action(struct xlate_ctx *ctx, const
> struct ofpact_learn *learn)
>          if (OVS_UNLIKELY(ctx->xin->trace)) {
>              struct ds s = DS_EMPTY_INITIALIZER;
>              ds_put_format(&s, "table=%"PRIu8" ", fm.table_id);
> -            match_format(&fm.match, NULL, &s, OFP_DEFAULT_PRIORITY);
> +            minimatch_format(&fm.match,
> +                             ofproto_get_tun_tab(&ctx->xin->ofproto->up),
> +                             NULL, &s, OFP_DEFAULT_PRIORITY);
>              ds_chomp(&s, ' ');
>              ds_put_format(&s, " priority=%d", fm.priority);
>              if (fm.new_cookie) {
> @@ -5090,6 +5092,8 @@ xlate_learn_action(struct xlate_ctx *ctx, const
> struct ofpact_learn *learn)
>              xlate_report_error(ctx, "LEARN action execution failed (%s).",
>                                 ofperr_to_string(error));
>          }
> +
> +        minimatch_destroy(&fm.match);
>      } else {
>          xlate_report(ctx, OFT_WARN,
>                       "suppressing side effects, so learn action ignored");
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index c92c5bea1725..1ed82d036528 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -5582,9 +5582,11 @@ odp_port_to_ofp_port(const struct ofproto_dpif
> *ofproto, odp_port_t odp_port)
>      }
>  }
>
> +/* 'match' is non-const to allow for temporary modifications.  Any
> changes are
> + * restored before returning. */
>  int
>  ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto,
> -                               const struct match *match, int priority,
> +                               struct match *match, int priority,
>                                 uint16_t idle_timeout,
>                                 const struct ofpbuf *ofpacts,
>                                 struct rule **rulep)
> @@ -5595,7 +5597,6 @@ ofproto_dpif_add_internal_flow(struct ofproto_dpif
> *ofproto,
>
>      fm = (struct ofputil_flow_mod) {
>          .buffer_id = UINT32_MAX,
> -        .match = *match,
>          .priority = priority,
>          .table_id = TBL_INTERNAL,
>          .command = OFPFC_ADD,
> @@ -5604,8 +5605,10 @@ ofproto_dpif_add_internal_flow(struct ofproto_dpif
> *ofproto,
>          .ofpacts = ofpacts->data,
>          .ofpacts_len = ofpacts->size,
>      };
> -
> +    minimatch_init(&fm.match, match);
>      error = ofproto_flow_mod(&ofproto->up, &fm);
> +    minimatch_destroy(&fm.match);
> +
>      if (error) {
>          VLOG_ERR_RL(&rl, "failed to add internal flow (%s)",
>                      ofperr_to_string(error));
> @@ -5615,8 +5618,7 @@ ofproto_dpif_add_internal_flow(struct ofproto_dpif
> *ofproto,
>
>      rule = rule_dpif_lookup_in_table(ofproto,
>                                       ofproto_dpif_get_tables_versio
> n(ofproto),
> -                                     TBL_INTERNAL, &fm.match.flow,
> -                                     &fm.match.wc);
> +                                     TBL_INTERNAL, &match->flow,
> &match->wc);
>      if (rule) {
>          *rulep = &rule->up;
>      } else {
> @@ -5634,7 +5636,6 @@ ofproto_dpif_delete_internal_flow(struct
> ofproto_dpif *ofproto,
>
>      fm = (struct ofputil_flow_mod) {
>          .buffer_id = UINT32_MAX,
> -        .match = *match,
>          .priority = priority,
>          .table_id = TBL_INTERNAL,
>          .out_port = OFPP_ANY,
> @@ -5642,8 +5643,10 @@ ofproto_dpif_delete_internal_flow(struct
> ofproto_dpif *ofproto,
>          .flags = OFPUTIL_FF_HIDDEN_FIELDS | OFPUTIL_FF_NO_READONLY,
>          .command = OFPFC_DELETE_STRICT,
>      };
> -
> +    minimatch_init(&fm.match, match);
>      error = ofproto_flow_mod(&ofproto->up, &fm);
> +    minimatch_destroy(&fm.match);
> +
>      if (error) {
>          VLOG_ERR_RL(&rl, "failed to delete internal flow (%s)",
>                      ofperr_to_string(error));
> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> index 90432fa2678b..47bf7f9f0ac2 100644
> --- a/ofproto/ofproto-dpif.h
> +++ b/ofproto/ofproto-dpif.h
> @@ -335,7 +335,7 @@ struct ofport_dpif *ofp_port_to_ofport(const struct
> ofproto_dpif *,
>                                         ofp_port_t);
>
>  int ofproto_dpif_add_internal_flow(struct ofproto_dpif *,
> -                                   const struct match *, int priority,
> +                                   struct match *, int priority,
>                                     uint16_t idle_timeout,
>                                     const struct ofpbuf *ofpacts,
>                                     struct rule **rulep);
> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
> index 92d4622b3290..d636fb35c4f9 100644
> --- a/ofproto/ofproto-provider.h
> +++ b/ofproto/ofproto-provider.h
> @@ -1199,7 +1199,7 @@ struct ofproto_class {
>       *
>       * If this function is NULL then table 0 is always chosen. */
>      enum ofperr (*rule_choose_table)(const struct ofproto *ofproto,
> -                                     const struct match *match,
> +                                     const struct minimatch *match,
>                                       uint8_t *table_idp);
>
>      /* Life-cycle functions for a "struct rule".
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index 57ce3c81fd22..36f4c0b16d48 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -133,7 +133,7 @@ static void eviction_group_remove_rule(struct rule *)
>      OVS_REQUIRES(ofproto_mutex);
>
>  static void rule_criteria_init(struct rule_criteria *, uint8_t table_id,
> -                               const struct match *match, int priority,
> +                               const struct minimatch *match, int
> priority,
>                                 ovs_version_t version,
>                                 ovs_be64 cookie, ovs_be64 cookie_mask,
>                                 ofp_port_t out_port, uint32_t out_group);
> @@ -2104,7 +2104,6 @@ flow_mod_init(struct ofputil_flow_mod *fm,
>                enum ofp_flow_mod_command command)
>  {
>      *fm = (struct ofputil_flow_mod) {
> -        .match = *match,
>          .priority = priority,
>          .table_id = 0,
>          .command = command,
> @@ -2114,6 +2113,7 @@ flow_mod_init(struct ofputil_flow_mod *fm,
>          .ofpacts = CONST_CAST(struct ofpact *, ofpacts),
>          .ofpacts_len = ofpacts_len,
>      };
> +    minimatch_init(&fm->match, match);
>  }
>
>  static int
> @@ -2123,10 +2123,10 @@ simple_flow_mod(struct ofproto *ofproto,
>                  enum ofp_flow_mod_command command)
>  {
>      struct ofputil_flow_mod fm;
> -
>      flow_mod_init(&fm, match, priority, ofpacts, ofpacts_len, command);
> -
> -    return handle_flow_mod__(ofproto, &fm, NULL);
> +    enum ofperr error = handle_flow_mod__(ofproto, &fm, NULL);
> +    minimatch_destroy(&fm.match);
> +    return error;
>  }
>
>  /* Adds a flow to OpenFlow flow table 0 in 'p' that matches 'cls_rule' and
> @@ -3173,12 +3173,12 @@ learned_cookies_flush(struct ofproto *ofproto,
> struct ovs_list *dead_cookies)
>  {
>      struct learned_cookie *c;
>
> +    struct minimatch match;
> +
> +    minimatch_init_catchall(&match);
>      LIST_FOR_EACH_POP (c, u.list_node, dead_cookies) {
>          struct rule_criteria criteria;
>          struct rule_collection rules;
> -        struct match match;
> -
> -        match_init_catchall(&match);
>          rule_criteria_init(&criteria, c->table_id, &match, 0,
> OVS_VERSION_MAX,
>                             c->cookie, OVS_BE64_MAX, OFPP_ANY, OFPG_ANY);
>          rule_criteria_require_rw(&criteria, false);
> @@ -3188,6 +3188,7 @@ learned_cookies_flush(struct ofproto *ofproto,
> struct ovs_list *dead_cookies)
>
>          free(c);
>      }
> +    minimatch_destroy(&match);
>  }
>
>  static enum ofperr
> @@ -4051,13 +4052,13 @@ next_matching_table(const struct ofproto *ofproto,
>   * supplied as 0. */
>  static void
>  rule_criteria_init(struct rule_criteria *criteria, uint8_t table_id,
> -                   const struct match *match, int priority,
> +                   const struct minimatch *match, int priority,
>                     ovs_version_t version, ovs_be64 cookie,
>                     ovs_be64 cookie_mask, ofp_port_t out_port,
>                     uint32_t out_group)
>  {
>      criteria->table_id = table_id;
> -    cls_rule_init(&criteria->cr, match, priority);
> +    cls_rule_init_from_minimatch(&criteria->cr, match, priority);
>      criteria->version = version;
>      criteria->cookie = cookie;
>      criteria->cookie_mask = cookie_mask;
> @@ -4303,9 +4304,12 @@ handle_flow_stats_request(struct ofconn *ofconn,
>          return error;
>      }
>
> -    rule_criteria_init(&criteria, fsr.table_id, &fsr.match, 0,
> OVS_VERSION_MAX,
> +    struct minimatch match;
> +    minimatch_init(&match, &fsr.match);
> +    rule_criteria_init(&criteria, fsr.table_id, &match, 0,
> OVS_VERSION_MAX,
>                         fsr.cookie, fsr.cookie_mask, fsr.out_port,
>                         fsr.out_group);
> +    minimatch_destroy(&match);
>
>      ovs_mutex_lock(&ofproto_mutex);
>      error = collect_rules_loose(ofproto, &criteria, &rules);
> @@ -4470,9 +4474,12 @@ handle_aggregate_stats_request(struct ofconn
> *ofconn,
>          return error;
>      }
>
> -    rule_criteria_init(&criteria, request.table_id, &request.match, 0,
> +    struct minimatch match;
> +    minimatch_init(&match, &request.match);
> +    rule_criteria_init(&criteria, request.table_id, &match, 0,
>                         OVS_VERSION_MAX, request.cookie,
> request.cookie_mask,
>                         request.out_port, request.out_group);
> +    minimatch_destroy(&match);
>
>      ovs_mutex_lock(&ofproto_mutex);
>      error = collect_rules_loose(ofproto, &criteria, &rules);
> @@ -4746,22 +4753,22 @@ add_flow_init(struct ofproto *ofproto, struct
> ofproto_flow_mod *ofm,
>      }
>
>      if (!(fm->flags & OFPUTIL_FF_HIDDEN_FIELDS)
> -        && !match_has_default_hidden_fields(&fm->match)) {
> +        && !minimatch_has_default_hidden_fields(&fm->match)) {
>          VLOG_WARN_RL(&rl, "%s: (add_flow) only internal flows can set "
>                       "non-default values to hidden fields",
> ofproto->name);
>          return OFPERR_OFPBRC_EPERM;
>      }
>
>      if (!ofm->temp_rule) {
> -        cls_rule_init(&cr, &fm->match, fm->priority);
> +        cls_rule_init_from_minimatch(&cr, &fm->match, fm->priority);
>
>          /* Allocate new rule.  Destroys 'cr'. */
> +        uint64_t map = miniflow_get_tun_metadata_pres
> ent_map(fm->match.flow);
>          error = ofproto_rule_create(ofproto, &cr, table - ofproto->tables,
>                                      fm->new_cookie, fm->idle_timeout,
>                                      fm->hard_timeout, fm->flags,
>                                      fm->importance, fm->ofpacts,
> -                                    fm->ofpacts_len,
> -                                    fm->match.flow.tunnel.metadata
> .present.map,
> +                                    fm->ofpacts_len, map,
>                                      fm->ofpacts_tlv_bitmap,
> &ofm->temp_rule);
>          if (error) {
>              return error;
> @@ -4958,7 +4965,7 @@ ofproto_flow_mod_init_for_learn(struct ofproto
> *ofproto,
>      struct oftable *table = &ofproto->tables[fm->table_id];
>      struct rule *rule;
>
> -    rule = rule_from_cls_rule(classifier_find_match_exactly(
> +    rule = rule_from_cls_rule(classifier_find_minimatch_exactly(
>                                    &table->cls, &fm->match, fm->priority,
>                                    OVS_VERSION_MAX));
>      if (rule) {
> @@ -5108,12 +5115,14 @@ ofproto_flow_mod_learn(struct ofproto_flow_mod
> *ofm, bool keep_ref,
>          if (limit) {
>              struct rule_criteria criteria;
>              struct rule_collection rules;
> -            struct match match;
> +            struct minimatch match;
>
> -            match_init_catchall(&match);
> +            minimatch_init_catchall(&match);
>              rule_criteria_init(&criteria, rule->table_id, &match, 0,
>                                 OVS_VERSION_MAX, rule->flow_cookie,
>                                 OVS_BE64_MAX, OFPP_ANY, OFPG_ANY);
> +            minimatch_destroy(&match);
> +
>              rule_criteria_require_rw(&criteria, false);
>              collect_rules_loose(rule->ofproto, &criteria, &rules);
>              if (rule_collection_n(&rules) >= limit) {
> @@ -5797,6 +5806,7 @@ handle_flow_mod(struct ofconn *ofconn, const struct
> ofp_header *oh)
>      if (!error) {
>          struct openflow_mod_requester req = { ofconn, oh };
>          error = handle_flow_mod__(ofproto, &fm, &req);
> +        minimatch_destroy(&fm.match);
>      }
>
>      ofpbuf_uninit(&ofpacts);
> @@ -7921,6 +7931,7 @@ handle_bundle_add(struct ofconn *ofconn, const
> struct ofp_header *oh)
>                                          ofproto->n_tables);
>          if (!error) {
>              error = ofproto_flow_mod_init(ofproto, &bmsg->ofm, &fm, NULL);
> +            minimatch_destroy(&fm.match);
>          }
>      } else if (type == OFPTYPE_GROUP_MOD) {
>          error = ofputil_decode_group_mod(badd.msg, &bmsg->ogm.gm);
> diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
> index 8d6d1b6ae8c0..83340bbf2b30 100644
> --- a/ovn/controller/ofctrl.c
> +++ b/ovn/controller/ofctrl.c
> @@ -58,7 +58,7 @@ struct ovn_flow {
>      /* Key. */
>      uint8_t table_id;
>      uint16_t priority;
> -    struct match match;
> +    struct minimatch match;
>
>      /* Data. */
>      struct ofpact *ofpacts;
> @@ -373,14 +373,16 @@ error:
>  static void
>  run_S_CLEAR_FLOWS(void)
>  {
> +    VLOG_DBG("clearing all flows");
> +
>      /* Send a flow_mod to delete all flows. */
>      struct ofputil_flow_mod fm = {
> -        .match = MATCH_CATCHALL_INITIALIZER,
>          .table_id = OFPTT_ALL,
>          .command = OFPFC_DELETE,
>      };
> +    minimatch_init_catchall(&fm.match);
>      queue_msg(encode_flow_mod(&fm));
> -    VLOG_DBG("clearing all flows");
> +    minimatch_destroy(&fm.match);
>
>      /* Clear installed_flows, to match the state of the switch. */
>      ovn_flow_table_clear(&installed_flows);
> @@ -630,13 +632,12 @@ ofctrl_recv(const struct ofp_header *oh, enum
> ofptype type)
>  void
>  ofctrl_add_flow(struct hmap *desired_flows,
>                  uint8_t table_id, uint16_t priority, uint64_t cookie,
> -                const struct match *match,
> -                const struct ofpbuf *actions)
> +                const struct match *match, const struct ofpbuf *actions)
>  {
>      struct ovn_flow *f = xmalloc(sizeof *f);
>      f->table_id = table_id;
>      f->priority = priority;
> -    f->match = *match;
> +    minimatch_init(&f->match, match);
>      f->ofpacts = xmemdup(actions->data, actions->size);
>      f->ofpacts_len = actions->size;
>      f->hmap_node.hash = ovn_flow_hash(f);
> @@ -665,7 +666,7 @@ static uint32_t
>  ovn_flow_hash(const struct ovn_flow *f)
>  {
>      return hash_2words((f->table_id << 16) | f->priority,
> -                       match_hash(&f->match, 0));
> +                       minimatch_hash(&f->match, 0));
>
>  }
>
> @@ -676,7 +677,7 @@ ofctrl_dup_flow(struct ovn_flow *src)
>      struct ovn_flow *dst = xmalloc(sizeof *dst);
>      dst->table_id = src->table_id;
>      dst->priority = src->priority;
> -    dst->match = src->match;
> +    minimatch_clone(&dst->match, &src->match);
>      dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len);
>      dst->ofpacts_len = src->ofpacts_len;
>      dst->hmap_node.hash = ovn_flow_hash(dst);
> @@ -694,7 +695,7 @@ ovn_flow_lookup(struct hmap *flow_table, const struct
> ovn_flow *target)
>                               flow_table) {
>          if (f->table_id == target->table_id
>              && f->priority == target->priority
> -            && match_equal(&f->match, &target->match)) {
> +            && minimatch_equal(&f->match, &target->match)) {
>              return f;
>          }
>      }
> @@ -707,7 +708,7 @@ ovn_flow_to_string(const struct ovn_flow *f)
>      struct ds s = DS_EMPTY_INITIALIZER;
>      ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id);
>      ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
> -    match_format(&f->match, NULL, &s, OFP_DEFAULT_PRIORITY);
> +    minimatch_format(&f->match, NULL, NULL, &s, OFP_DEFAULT_PRIORITY);
>      ds_put_cstr(&s, ", actions=");
>      struct ofpact_format_params fp = { .s = &s };
>      ofpacts_format(f->ofpacts, f->ofpacts_len, &fp);
> @@ -728,6 +729,7 @@ static void
>  ovn_flow_destroy(struct ovn_flow *f)
>  {
>      if (f) {
> +        minimatch_destroy(&f->match);
>          free(f->ofpacts);
>          free(f);
>      }
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index b5e2b409328e..4d9e8743e885 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -1747,6 +1747,7 @@ ofctl_flow_mod__(const char *remote, struct
> ofputil_flow_mod *fms,
>
>          transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
>          free(CONST_CAST(struct ofpact *, fm->ofpacts));
> +        minimatch_destroy(&fm->match);
>      }
>      vconn_close(vconn);
>  }
> @@ -3309,7 +3310,7 @@ struct fte_version {
>  /* A FTE entry that has been queued for later insertion after all
>   * flows have been scanned to correctly allocation tunnel metadata. */
>  struct fte_pending {
> -    struct match *match;
> +    struct minimatch match;
>      int priority;
>      struct fte_version *version;
>      int index;
> @@ -3442,14 +3443,14 @@ fte_free_all(struct flow_tables *tables)
>   *
>   * Takes ownership of 'version'. */
>  static void
> -fte_insert(struct flow_tables *tables, const struct match *match,
> +fte_insert(struct flow_tables *tables, const struct minimatch *match,
>             int priority, struct fte_version *version, int index)
>  {
>      struct classifier *cls = &tables->tables[version->table_id];
>      struct fte *old, *fte;
>
>      fte = xzalloc(sizeof *fte);
> -    cls_rule_init(&fte->rule, match, priority);
> +    cls_rule_init_from_minimatch(&fte->rule, match, priority);
>      fte->versions[index] = version;
>
>      old = fte_from_cls_rule(classifier_replace(cls, &fte->rule,
> @@ -3498,40 +3499,45 @@ generate_tun_metadata(struct fte_state *state)
>   * can just read the data from the match and rewrite it. On rewrite, it
>   * will use the new table. */
>  static void
> -remap_match(struct fte_state *state, struct match *match)
> +remap_match(struct fte_state *state, struct minimatch *minimatch)
>  {
>      int i;
>
> -    if (!match->tun_md.valid) {
> +    if (!minimatch->tun_md || !minimatch->tun_md->valid) {
>          return;
>      }
>
> -    struct tun_metadata flow = match->flow.tunnel.metadata;
> -    struct tun_metadata flow_mask = match->wc.masks.tunnel.metadata;
> -    memset(&match->flow.tunnel.metadata, 0, sizeof
> match->flow.tunnel.metadata);
> -    memset(&match->wc.masks.tunnel.metadata, 0,
> -           sizeof match->wc.masks.tunnel.metadata);
> -    match->tun_md.valid = false;
> +    struct match match;
> +    minimatch_expand(minimatch, &match);
> +
> +    struct tun_metadata flow = match.flow.tunnel.metadata;
> +    struct tun_metadata flow_mask = match.wc.masks.tunnel.metadata;
> +    memset(&match.flow.tunnel.metadata, 0, sizeof
> match.flow.tunnel.metadata);
> +    memset(&match.wc.masks.tunnel.metadata, 0,
> +           sizeof match.wc.masks.tunnel.metadata);
> +    match.tun_md.valid = false;
>
> -    match->flow.tunnel.metadata.tab = state->tun_tab;
> -    match->wc.masks.tunnel.metadata.tab = match->flow.tunnel.metadata.ta
> b;
> +    match.flow.tunnel.metadata.tab = state->tun_tab;
> +    match.wc.masks.tunnel.metadata.tab = match.flow.tunnel.metadata.tab;
>
>      ULLONG_FOR_EACH_1 (i, flow_mask.present.map) {
>          const struct mf_field *field = mf_from_id(MFF_TUN_METADATA0 + i);
> -        int offset = match->tun_md.entry[i].loc.c.offset;
> -        int len = match->tun_md.entry[i].loc.len;
> +        int offset = match.tun_md.entry[i].loc.c.offset;
> +        int len = match.tun_md.entry[i].loc.len;
>          union mf_value value, mask;
>
>          memset(&value, 0, field->n_bytes - len);
> -        memset(&mask, match->tun_md.entry[i].masked ? 0 : 0xff,
> +        memset(&mask, match.tun_md.entry[i].masked ? 0 : 0xff,
>                 field->n_bytes - len);
>
>          memcpy(value.tun_metadata + field->n_bytes - len,
>                 flow.opts.u8 + offset, len);
>          memcpy(mask.tun_metadata + field->n_bytes - len,
>                 flow_mask.opts.u8 + offset, len);
> -        mf_set(field, &value, &mask, match, NULL);
> +        mf_set(field, &value, &mask, &match, NULL);
>      }
> +    minimatch_destroy(minimatch);
> +    minimatch_init(minimatch, &match);
>  }
>
>  /* In order to correctly handle tunnel metadata, we need to have
> @@ -3578,25 +3584,26 @@ fte_state_destroy(struct fte_state *state)
>   * fte_state_init(). fte_queue() is the first pass to be called as each
>   * flow is read from its source. */
>  static void
> -fte_queue(struct fte_state *state, const struct match *match,
> +fte_queue(struct fte_state *state, const struct minimatch *match,
>            int priority, struct fte_version *version, int index)
>  {
>      struct fte_pending *pending = xmalloc(sizeof *pending);
>      int i;
>
> -    pending->match = xmemdup(match, sizeof *match);
> +    minimatch_clone(&pending->match, match);
>      pending->priority = priority;
>      pending->version = version;
>      pending->index = index;
>      ovs_list_push_back(&state->fte_pending_list, &pending->list_node);
>
> -    if (!match->tun_md.valid) {
> +    if (!match->tun_md || !match->tun_md->valid) {
>          return;
>      }
>
> -    ULLONG_FOR_EACH_1 (i, match->wc.masks.tunnel.metadata.present.map) {
> -        if (match->tun_md.entry[i].loc.len >
> state->tun_metadata_size[i]) {
> -            state->tun_metadata_size[i] = match->tun_md.entry[i].loc.len;
> +    uint64_t map = miniflow_get_tun_metadata_pres
> ent_map(&match->mask->masks);
> +    ULLONG_FOR_EACH_1 (i, map) {
> +        if (match->tun_md->entry[i].loc.len >
> state->tun_metadata_size[i]) {
> +            state->tun_metadata_size[i] = match->tun_md->entry[i].loc.le
> n;
>          }
>      }
>  }
> @@ -3615,10 +3622,10 @@ fte_fill(struct fte_state *state, struct
> flow_tables *tables)
>      flow_tables_defer(tables);
>
>      LIST_FOR_EACH_POP(pending, list_node, &state->fte_pending_list) {
> -        remap_match(state, pending->match);
> -        fte_insert(tables, pending->match, pending->priority,
> pending->version,
> -                   pending->index);
> -        free(pending->match);
> +        remap_match(state, &pending->match);
> +        fte_insert(tables, &pending->match, pending->priority,
> +                   pending->version, pending->index);
> +        minimatch_destroy(&pending->match);
>          free(pending);
>      }
>
> @@ -3669,6 +3676,8 @@ read_flows_from_file(const char *filename, struct
> fte_state *state, int index)
>          version->table_id = fm.table_id != OFPTT_ALL ? fm.table_id : 0;
>
>          fte_queue(state, &fm.match, fm.priority, version, index);
> +
> +        minimatch_destroy(&fm.match);
>      }
>      ds_destroy(&s);
>
> @@ -3714,7 +3723,10 @@ read_flows_from_switch(struct vconn *vconn,
>          version->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len);
>          version->table_id = fs->table_id;
>
> -        fte_queue(state, &fs->match, fs->priority, version, index);
> +        struct minimatch match;
> +        minimatch_init(&match, &fs->match);
> +        fte_queue(state, &match, fs->priority, version, index);
> +        minimatch_destroy(&match);
>      }
>
>      for (size_t i = 0; i < n_fses; i++) {
> @@ -3744,7 +3756,7 @@ fte_make_flow_mod(const struct fte *fte, int index,
> uint16_t command,
>          .out_group = OFPG_ANY,
>          .flags = version->flags,
>      };
> -    minimatch_expand(&fte->rule.match, &fm.match);
> +    minimatch_clone(&fm.match, &fte->rule.match);
>      if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
>          command == OFPFC_MODIFY_STRICT) {
>          fm.ofpacts = version->ofpacts;
> @@ -3753,8 +3765,9 @@ fte_make_flow_mod(const struct fte *fte, int index,
> uint16_t command,
>          fm.ofpacts = NULL;
>          fm.ofpacts_len = 0;
>      }
> -
>      ofm = ofputil_encode_flow_mod(&fm, protocol);
> +    minimatch_destroy(&fm.match);
> +
>      ovs_list_push_back(packets, &ofm->list_node);
>  }
>
> @@ -4028,6 +4041,7 @@ ofctl_parse_flows__(struct ofputil_flow_mod *fms,
> size_t n_fms,
>          ofpbuf_delete(msg);
>
>          free(CONST_CAST(struct ofpact *, fm->ofpacts));
> +        minimatch_destroy(&fm->match);
>      }
>  }
>
> @@ -4504,10 +4518,13 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
>      if (error_s) {
>          ovs_fatal(0, "%s", error_s);
>      }
> +    struct match fm_match;
> +    minimatch_expand(&fm.match, &fm_match);
>      printf("%04"PRIx16"/%04"PRIx16"\n",
> -           ntohs(fm.match.flow.vlans[0].tci),
> -           ntohs(fm.match.wc.masks.vlans[0].tci));
> +           ntohs(fm_match.flow.vlans[0].tci),
> +           ntohs(fm_match.wc.masks.vlans[0].tci));
>      free(string_s);
> +    minimatch_destroy(&fm.match);
>
>      /* Convert to and from NXM. */
>      ofpbuf_init(&nxm, 0);
> --
> 2.16.1
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

I can't claim that my review would be enough to spot any potential issue,
but for what it's worth I did test the patches: all 150K+ flows were
processed correctly, and the memory savings were substantial. I did test
against master and 2.7.3.

Thanks,
Armando

Reviewed-by: Armando Migliaccio <armamig at gmail.com>
Tested-by: Armando Migliaccio <armamig at gmail.com>


More information about the dev mailing list