On 11 May 2025, at 15:47, Roi Dayan via dev wrote:
> 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? Hi Roi, It’s on Ilya’s queue, see here; https://patchwork.ozlabs.org/project/openvswitch/list/ >> 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 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev