On 27/04/2025 10:51, Roi Dayan wrote:
> From: Eli Britstein <el...@nvidia.com>
> 
> According to [1], transport port masks are optimized using a prefix tree,
> to get less specific datapath flows. However, this might be not optimal
> for some cases. HW offload that is based on templates and has a table per
> template (all the fields that are matched) will create many such tables
> and yield poor performance. Furthermore, such templates are limited resources.
> 
> In such cases, it is preferred to have more specific datapath flows that
> share the same match template rather then less flows distributed across
> multiple templates.
> 
> To achieve that, introduce other_config:disable-ports-trie (true/false)
> configuration (default as false).
> 
> [1] 69d6040e861a ("lib/classifier: Use a prefix tree to optimize ports 
> wildcarding.")
> 
> Acked-by: Roi Dayan <r...@nvidia.com>
> Signed-off-by: Eli Britstein <el...@nvidia.com>
> ---

Hi,

Any feedkback about this optional config?

Thanks,
Roi


>  lib/classifier.c           | 21 +++++++++++++--------
>  lib/classifier.h           |  3 ++-
>  lib/ovs-router.c           |  4 ++--
>  lib/tnl-ports.c            |  7 ++++---
>  ofproto/ofproto-dpif.c     |  9 ++++++---
>  ofproto/ofproto-provider.h |  2 ++
>  ofproto/ofproto.c          |  5 ++++-
>  ofproto/ofproto.h          |  2 +-
>  tests/classifier.at        | 25 +++++++++++++++++++++++++
>  tests/test-classifier.c    |  8 ++++----
>  vswitchd/bridge.c          |  3 ++-
>  vswitchd/vswitch.xml       | 18 ++++++++++++++++++
>  12 files changed, 83 insertions(+), 24 deletions(-)
> 
> diff --git a/lib/classifier.c b/lib/classifier.c
> index 55f23b976f30..37684a5aa026 100644
> --- a/lib/classifier.c
> +++ b/lib/classifier.c
> @@ -116,7 +116,8 @@ static const struct cls_match *find_match_wc(const struct 
> cls_subtable *,
>                                               const struct flow *,
>                                               struct trie_ctx *,
>                                               unsigned int n_tries,
> -                                             struct flow_wildcards *);
> +                                             struct flow_wildcards *,
> +                                             bool disable_ports_trie);
>  static struct cls_match *find_equal(const struct cls_subtable *,
>                                      const struct miniflow *, uint32_t hash);
>  
> @@ -971,7 +972,8 @@ static const struct cls_rule *
>  classifier_lookup__(const struct classifier *cls, ovs_version_t version,
>                      struct flow *flow, struct flow_wildcards *wc,
>                      bool allow_conjunctive_matches,
> -                    struct hmapx *conj_flows)
> +                    struct hmapx *conj_flows,
> +                    bool disable_ports_trie)
>  {
>      struct trie_ctx trie_ctx[CLS_MAX_TRIES];
>      const struct cls_match *match;
> @@ -1007,7 +1009,7 @@ classifier_lookup__(const struct classifier *cls, 
> ovs_version_t version,
>          /* Skip subtables with no match, or where the match is lower-priority
>           * than some certain match we've already found. */
>          match = find_match_wc(subtable, version, flow, trie_ctx, 
> cls->n_tries,
> -                              wc);
> +                              wc, disable_ports_trie);
>          if (!match || match->priority <= hard_pri) {
>              continue;
>          }
> @@ -1132,7 +1134,7 @@ classifier_lookup__(const struct classifier *cls, 
> ovs_version_t version,
>  
>                  flow->conj_id = id;
>                  rule = classifier_lookup__(cls, version, flow, wc, false,
> -                                           NULL);
> +                                           NULL, disable_ports_trie);
>                  flow->conj_id = saved_conj_id;
>  
>                  if (rule) {
> @@ -1207,9 +1209,11 @@ classifier_lookup__(const struct classifier *cls, 
> ovs_version_t version,
>  const struct cls_rule *
>  classifier_lookup(const struct classifier *cls, ovs_version_t version,
>                    struct flow *flow, struct flow_wildcards *wc,
> -                  struct hmapx *conj_flows)
> +                  struct hmapx *conj_flows,
> +                  bool disable_ports_trie)
>  {
> -    return classifier_lookup__(cls, version, flow, wc, true, conj_flows);
> +    return classifier_lookup__(cls, version, flow, wc, true, conj_flows,
> +                               disable_ports_trie);
>  }
>  
>  /* Finds and returns a rule in 'cls' with exactly the same priority and
> @@ -1727,7 +1731,8 @@ find_match(const struct cls_subtable *subtable, 
> ovs_version_t version,
>  static const struct cls_match *
>  find_match_wc(const struct cls_subtable *subtable, ovs_version_t version,
>                const struct flow *flow, struct trie_ctx *trie_ctx,
> -              unsigned int n_tries, struct flow_wildcards *wc)
> +              unsigned int n_tries, struct flow_wildcards *wc,
> +              bool disable_ports_trie)
>  {
>      if (OVS_UNLIKELY(!wc)) {
>          return find_match(subtable, version, flow,
> @@ -1774,7 +1779,7 @@ find_match_wc(const struct cls_subtable *subtable, 
> ovs_version_t version,
>                                         subtable->index_maps[i],
>                                         &mask_offset, &basis);
>      rule = find_match(subtable, version, flow, hash);
> -    if (!rule && subtable->ports_mask_len) {
> +    if (!rule && subtable->ports_mask_len && !disable_ports_trie) {
>          /* The final stage had ports, but there was no match.  Instead of
>           * unwildcarding all the ports bits, use the ports trie to figure 
> out a
>           * smaller set of bits to unwildcard. */
> diff --git a/lib/classifier.h b/lib/classifier.h
> index 7928601e0f59..38eb92f45267 100644
> --- a/lib/classifier.h
> +++ b/lib/classifier.h
> @@ -399,7 +399,8 @@ static inline void classifier_publish(struct classifier 
> *);
>  const struct cls_rule *classifier_lookup(const struct classifier *,
>                                           ovs_version_t, struct flow *,
>                                           struct flow_wildcards *,
> -                                         struct hmapx *conj_flows);
> +                                         struct hmapx *conj_flows,
> +                                         bool disable_ports_trie);
>  bool classifier_rule_overlaps(const struct classifier *,
>                                const struct cls_rule *, ovs_version_t);
>  const struct cls_rule *classifier_find_rule_exactly(const struct classifier 
> *,
> diff --git a/lib/ovs-router.c b/lib/ovs-router.c
> index d955a3a543b8..81fe3382a287 100644
> --- a/lib/ovs-router.c
> +++ b/lib/ovs-router.c
> @@ -117,7 +117,7 @@ ovs_router_lookup(uint32_t mark, const struct in6_addr 
> *ip6_dst,
>          struct flow flow_src = {.ipv6_dst = *src, .pkt_mark = mark};
>  
>          cr_src = classifier_lookup(&cls, OVS_VERSION_MAX, &flow_src, NULL,
> -                                   NULL);
> +                                   NULL, false);
>          if (cr_src) {
>              struct ovs_router_entry *p_src = ovs_router_entry_cast(cr_src);
>              if (!p_src->local) {
> @@ -128,7 +128,7 @@ ovs_router_lookup(uint32_t mark, const struct in6_addr 
> *ip6_dst,
>          }
>      }
>  
> -    cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL, NULL);
> +    cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL, NULL, false);
>      if (cr) {
>          struct ovs_router_entry *p = ovs_router_entry_cast(cr);
>  
> diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
> index 56119b30010e..3b3545a11194 100644
> --- a/lib/tnl-ports.c
> +++ b/lib/tnl-ports.c
> @@ -112,7 +112,8 @@ map_insert(odp_port_t port, struct eth_addr mac, struct 
> in6_addr *addr,
>      tnl_port_init_flow(&match.flow, mac, addr, nw_proto, tp_port);
>  
>      do {
> -        cr = classifier_lookup(&cls, OVS_VERSION_MAX, &match.flow, NULL, 
> NULL);
> +        cr = classifier_lookup(&cls, OVS_VERSION_MAX, &match.flow, NULL, 
> NULL,
> +                               false);
>          p = tnl_port_cast(cr);
>          /* Try again if the rule was released before we get the reference. */
>      } while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt));
> @@ -245,7 +246,7 @@ map_delete(struct eth_addr mac, struct in6_addr *addr,
>  
>      tnl_port_init_flow(&flow, mac, addr, nw_proto, tp_port);
>  
> -    cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL, NULL);
> +    cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL, NULL, false);
>      tnl_port_unref(cr);
>  }
>  
> @@ -303,7 +304,7 @@ odp_port_t
>  tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
>  {
>      const struct cls_rule *cr = classifier_lookup(&cls, OVS_VERSION_MAX, 
> flow,
> -                                                  wc, NULL);
> +                                                  wc, NULL, false);
>  
>      return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE;
>  }
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index ed9e44ce2b03..5b9b1e70ba92 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -4587,9 +4587,12 @@ rule_dpif_lookup_in_table(struct ofproto_dpif 
> *ofproto, ovs_version_t version,
>                            struct hmapx *conj_flows)
>  {
>      struct classifier *cls = &ofproto->up.tables[table_id].cls;
> -    return rule_dpif_cast(rule_from_cls_rule(classifier_lookup(cls, version,
> -                                                               flow, wc,
> -                                                               conj_flows)));
> +    bool disable_ports_trie = ofproto->up.disable_ports_trie;
> +    const struct cls_rule *cr;
> +
> +    cr = classifier_lookup(cls, version, flow, wc, conj_flows,
> +                           disable_ports_trie);
> +    return rule_dpif_cast(rule_from_cls_rule(cr));
>  }
>  
>  void
> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
> index 7df3f5246912..4527100c2d65 100644
> --- a/ofproto/ofproto-provider.h
> +++ b/ofproto/ofproto-provider.h
> @@ -146,6 +146,8 @@ struct ofproto {
>      struct vl_mff_map vl_mff_map;
>      /* refcount to this ofproto, held by rule/group/xlate_caches */
>      struct ovs_refcount refcount;
> +
> +    bool disable_ports_trie;
>  };
>  
>  void ofproto_init_tables(struct ofproto *, int n_tables);
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index ef615e59c354..8bd47c68c16b 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -481,7 +481,7 @@ ofproto_bump_tables_version(struct ofproto *ofproto)
>  
>  int
>  ofproto_create(const char *datapath_name, const char *datapath_type,
> -               struct ofproto **ofprotop)
> +               const struct smap *other_config, struct ofproto **ofprotop)
>      OVS_EXCLUDED(ofproto_mutex)
>  {
>      const struct ofproto_class *class;
> @@ -589,6 +589,9 @@ ofproto_create(const char *datapath_name, const char 
> *datapath_type,
>      ofproto->slowpath_meter_id = UINT32_MAX;
>      ofproto->controller_meter_id = UINT32_MAX;
>  
> +    ofproto->disable_ports_trie = smap_get_bool(other_config,
> +                                                "disable-ports-trie", false);
> +
>      /* Set the initial tables version. */
>      ofproto_bump_tables_version(ofproto);
>  
> diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
> index 3f85509a1add..ea0be0a4a19c 100644
> --- a/ofproto/ofproto.h
> +++ b/ofproto/ofproto.h
> @@ -275,7 +275,7 @@ int ofproto_type_run(const char *datapath_type);
>  void ofproto_type_wait(const char *datapath_type);
>  
>  int ofproto_create(const char *datapath, const char *datapath_type,
> -                   struct ofproto **ofprotop)
> +                   const struct smap *other_config, struct ofproto 
> **ofprotop)
>      OVS_EXCLUDED(ofproto_mutex);
>  void ofproto_destroy(struct ofproto *, bool del);
>  int ofproto_delete(const char *name, const char *type);
> diff --git a/tests/classifier.at b/tests/classifier.at
> index dfadf5e5a9ce..b83b76b3eacf 100644
> --- a/tests/classifier.at
> +++ b/tests/classifier.at
> @@ -282,6 +282,31 @@ Datapath actions: 3
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
>  
> +AT_SETUP([flow classifier - disable ports trie])
> +OVS_VSWITCHD_START([set Open_vSwitch . other_config:disable-ports-trie=true])
> +add_of_ports br0 1 2
> +
> +AT_DATA([flows.txt], [dnl
> + priority=1,ip,tcp,tp_dst=80 action=drop
> + priority=0 actions=2
> +])
> +AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
> +
> +AT_CHECK([ovs-appctl ofproto/trace br0 
> 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'],
>  [0], [stdout])
> +AT_CHECK([tail -2 stdout], [0],
> +  [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80
> +Datapath actions: drop
> +])
> +
> +AT_CHECK([ovs-appctl ofproto/trace br0 
> 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'],
>  [0], [stdout])
> +AT_CHECK([tail -2 stdout], [0],
> +  [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=79
> +Datapath actions: 2
> +])
> +
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
>  AT_SETUP([flow classifier - ipv6 ND dependency])
>  OVS_VSWITCHD_START
>  add_of_ports br0 1 2
> diff --git a/tests/test-classifier.c b/tests/test-classifier.c
> index 2c1604a01e2e..445b6206697f 100644
> --- a/tests/test-classifier.c
> +++ b/tests/test-classifier.c
> @@ -441,7 +441,7 @@ compare_classifiers(struct classifier *cls, size_t 
> n_invisible_rules,
>          /* This assertion is here to suppress a GCC 4.9 array-bounds warning 
> */
>          ovs_assert(cls->n_tries <= CLS_MAX_TRIES);
>  
> -        cr0 = classifier_lookup(cls, version, &flow, &wc, NULL);
> +        cr0 = classifier_lookup(cls, version, &flow, &wc, NULL, false);
>          cr1 = tcls_lookup(tcls, &flow);
>          assert((cr0 == NULL) == (cr1 == NULL));
>          if (cr0 != NULL) {
> @@ -454,7 +454,7 @@ compare_classifiers(struct classifier *cls, size_t 
> n_invisible_rules,
>              /* Make sure the rule should have been visible. */
>              assert(cls_rule_visible_in_version(cr0, version));
>          }
> -        cr2 = classifier_lookup(cls, version, &flow, NULL, NULL);
> +        cr2 = classifier_lookup(cls, version, &flow, NULL, NULL, false);
>          assert(cr2 == cr0);
>      }
>  }
> @@ -1370,10 +1370,10 @@ lookup_classifier(void *aux_)
>          if (aux->use_wc) {
>              flow_wildcards_init_catchall(&wc);
>              cr = classifier_lookup(aux->cls, version, &aux->lookup_flows[x],
> -                                   &wc, NULL);
> +                                   &wc, NULL, false);
>          } else {
>              cr = classifier_lookup(aux->cls, version, &aux->lookup_flows[x],
> -                                   NULL, NULL);
> +                                   NULL, NULL, false);
>          }
>          if (cr) {
>              hits++;
> diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
> index 456b784c01d0..1a2236c77719 100644
> --- a/vswitchd/bridge.c
> +++ b/vswitchd/bridge.c
> @@ -933,7 +933,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch 
> *ovs_cfg)
>          if (!br->ofproto) {
>              int error;
>  
> -            error = ofproto_create(br->name, br->type, &br->ofproto);
> +            error = ofproto_create(br->name, br->type, 
> &ovs_cfg->other_config,
> +                                   &br->ofproto);
>              if (error) {
>                  VLOG_ERR("failed to create bridge %s: %s", br->name,
>                           ovs_strerror(error));
> diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
> index 76df9aab055a..737e4c07adf5 100644
> --- a/vswitchd/vswitch.xml
> +++ b/vswitchd/vswitch.xml
> @@ -176,6 +176,24 @@
>          </p>
>        </column>
>  
> +      <column name="other_config" key="disable-ports-trie"
> +              type='{"type": "boolean"}'>
> +        <p>
> +          Set this value to <code>true</code> to disable transport ports 
> prefix
> +          tree optimization.
> +        </p>
> +        <p>
> +          The default value is <code>false</code>. Changing this value 
> requires
> +          restarting the daemon.
> +        </p>
> +        <p>
> +          The default behavior tries to optimize transport ports wildcards,
> +          to get less datapath flows. The down side of it that those flows
> +          are distributed accross multiple match templates, which is
> +          problematic for HW offloads.
> +        </p>
> +    </column>
> +
>        <column name="other_config" key="max-idle"
>                type='{"type": "integer", "minInteger": 500}'>
>          <p>

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

Reply via email to