On Tue, Sep 16, 2025 at 1:55 PM Dumitru Ceara <dce...@redhat.com> wrote:
> Hi Ales, > > Thanks for the patch! > > On 9/11/25 1:44 PM, Ales Musil wrote: > > The function has only single call site and was used for modifying > > flows where the only actions in the list were conjunctions. By > > specializing we can make durther assumptions that allows us to > > Typo: durther > > > optimize as much as possible. The main issue with the function was > > very high number of allocations, 90% of them were actually to perform > > a duplication check when we try to append new conjnctions into > > Typo: conjnctions > > > already existing flow. > > > > The strategy to avoid those allocations is to allocate the array > > of references only once instead of going one by name effectively > > "one by name"? Did you mean to say "one by one"? > > > turning the allocation complexity of that function fron O(N), where > > Typo: fron > > > N is the number of already existing conjunctions into O(1) where we > > allocate constant number of times depending on the code path. > > > > This also improves the performance of operations directly related > > to conjunctions, mainly port group and address set modifications > > within ACLs. > > > > Below are numbers from a test run with 2000 LSPs, 1000 PGs, 1000 ACLs > > that resulted with flows that had 1000 conjunctions as an action. > > > > Without the change: > > ovn-controller(s) completion: 910ms > > node: lflow_output, handler for input port_groups took 427ms > > > > With the change: > > ovn-controller(s) completion: 407ms > > node: lflow_output, handler for input port_groups took 157ms > > > > So the speed up for the sync call is on avarage aroung 2.2x faster> and > the speedup of precessing those lflows due to port group change > > is on avarage 2.7x faster. This is also not limited only to port > > Typos: avarage (x2), aroung, precessing > > > groups, but the address set changes resulting in conjunctions will > > benefit too. > > > > Fixes: 5ad4e5395b9e ("ofctrl: Prevent conjunction duplication") > > Fixes: e659bab31a91 ("Combine conjunctions with identical matches into > one flow.") > > Signed-off-by: Ales Musil <amu...@redhat.com> > > --- > > controller/lflow.c | 23 ++------ > > controller/ofctrl.c | 136 +++++++++++++++++++++++++------------------- > > controller/ofctrl.h | 15 ++--- > > 3 files changed, 92 insertions(+), 82 deletions(-) > > > > diff --git a/controller/lflow.c b/controller/lflow.c > > index b75ae5c0d..11ec33faf 100644 > > --- a/controller/lflow.c > > +++ b/controller/lflow.c > > @@ -938,24 +938,11 @@ add_matches_to_flow_table(const struct > sbrec_logical_flow *lflow, > > if (vector_len(&m->conjunctions) > 1) { > > ovs_assert(!as_info.name); > > } > > - uint64_t conj_stubs[64 / 8]; > > - struct ofpbuf conj; > > - > > - ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs); > > - const struct cls_conjunction *src; > > - VECTOR_FOR_EACH_PTR (&m->conjunctions, src) { > > - struct ofpact_conjunction *dst = > ofpact_put_CONJUNCTION(&conj); > > - dst->id = src->id; > > - dst->clause = src->clause; > > - dst->n_clauses = src->n_clauses; > > - } > > - > > - ofctrl_add_or_append_flow(l_ctx_out->flow_table, ptable, > > - lflow->priority, 0, > > - &m->match, &conj, > &lflow->header_.uuid, > > - ctrl_meter_id, > > - as_info.name ? &as_info : NULL); > > - ofpbuf_uninit(&conj); > > + ofctrl_add_or_append_conj_flow(l_ctx_out->flow_table, > ptable, > > + lflow->priority, &m->match, > > + &m->conjunctions, > > + &lflow->header_.uuid, > ctrl_meter_id, > > + as_info.name ? &as_info : > NULL); > > } > > } > > > > diff --git a/controller/ofctrl.c b/controller/ofctrl.c > > index 2a518161f..20d4bed01 100644 > > --- a/controller/ofctrl.c > > +++ b/controller/ofctrl.c > > @@ -290,6 +290,11 @@ static void remove_flows_from_sb_to_flow(struct > ovn_desired_flow_table *, > > struct sb_to_flow *, > > const char *log_msg, > > struct uuidset > *flood_remove_nodes); > > +static void ovn_flow_uninit(struct ovn_flow *f); > > +static void ovn_flow_init(struct ovn_flow *f, uint8_t table_id, > > + uint16_t priority, uint64_t cookie, > > + const struct match *match, void *actions, > > + size_t action_len, uint32_t meter_id); > > > > /* OpenFlow connection to the switch. */ > > static struct rconn *swconn; > > @@ -1300,77 +1305,91 @@ ofpact_ref_find(const struct hmap *refs, const > struct ofpact *ofpact) > > return NULL; > > } > > > > -static void > > -ofpact_refs_destroy(struct hmap *refs) > > +static inline void > > +ofpacts_append(struct ovn_flow *flow, const struct ofpbuf *append) > > { > > - struct ofpact_ref *ref; > > - HMAP_FOR_EACH_POP (ref, hmap_node, refs) { > > - free(ref); > > + size_t new_len = flow->ofpacts_len + append->size; > > + > > + flow->ofpacts = xrealloc(flow->ofpacts, new_len); > > + memcpy((uint8_t *) flow->ofpacts + flow->ofpacts_len, > > + append->data, append->size); > > + flow->ofpacts_len = new_len; > > +} > > + > > +static inline struct ofpbuf * > > +create_conjuction_actions(const struct vector *conjunctions, > > + const struct hmap *existing) > > +{ > > + size_t len = vector_len(conjunctions); > > + struct ofpbuf *ofpbuf = > > + ofpbuf_new(sizeof(struct ofpact_conjunction) * len); > > + > > + const struct cls_conjunction *cls; > > + VECTOR_FOR_EACH_PTR (conjunctions, cls) { > > + struct ofpact_conjunction *conj = > ofpact_put_CONJUNCTION(ofpbuf); > > + conj->id = cls->id; > > + conj->clause = cls->clause; > > + conj->n_clauses = cls->n_clauses; > > + > > + if (existing && ofpact_ref_find(existing, &conj->ofpact)) { > > + ofpbuf_pull(ofpbuf, sizeof *conj); > > + } > > } > > - hmap_destroy(refs); > > + > > + return ofpbuf; > > } > > > > /* Either add a new flow, or append actions on an existing flow. If the > > * flow existed, a new link will also be created between the new sb_uuid > > * and the existing flow. */ > > void > > -ofctrl_add_or_append_flow(struct ovn_desired_flow_table *desired_flows, > > - uint8_t table_id, uint16_t priority, uint64_t > cookie, > > - const struct match *match, > > - const struct ofpbuf *actions, > > - const struct uuid *sb_uuid, > > - uint32_t meter_id, > > - const struct addrset_info *as_info) > > +ofctrl_add_or_append_conj_flow(struct ovn_desired_flow_table > *desired_flows, > > + uint8_t table_id, uint16_t priority, > > + const struct match *match, > > + const struct vector *conjunctions, > > + const struct uuid *sb_uuid, > > + uint32_t meter_id, > > + const struct addrset_info *as_info) > > { > > struct desired_flow *existing; > > struct desired_flow *f; > > - > > - f = desired_flow_alloc(table_id, priority, cookie, match, actions, > > - meter_id); > > - existing = desired_flow_lookup_conjunctive(desired_flows, &f->flow); > > + struct ofpbuf *actions; > > + > > + /* Create flow just to search for existing one, this avoids the > allocation > > + * of ofpacts buffer if it's not needed yet. */ > > + struct ovn_flow search_flow; > > + ovn_flow_init(&search_flow, table_id, priority, 0, > > + match, NULL, 0, meter_id); > > + existing = desired_flow_lookup_conjunctive(desired_flows, > &search_flow); > > if (existing) { > > struct hmap existing_conj = HMAP_INITIALIZER(&existing_conj); > > - > > + /* We can calculate the length directly because the flow actions > > + * consist only of conjunctions. Make a rough assert to ensure > > + * that is that case in the future. */ > > + size_t conj_size = sizeof (struct ofpact_conjunction); > > Nit: I think it should be "sizeof(...)". > > > + ovs_assert(!(existing->flow.ofpacts_len % conj_size)); > > + struct ofpact_ref *refs = > > + xmalloc(sizeof *refs * existing->flow.ofpacts_len / > conj_size); > > + > > + size_t index = 0; > > struct ofpact *ofpact; > > OFPACT_FOR_EACH (ofpact, existing->flow.ofpacts, > > existing->flow.ofpacts_len) { > > - if (ofpact->type != OFPACT_CONJUNCTION) { > > - continue; > > - } > > Just to be extra sure we don't miss a case could we also add an > assertion here? E.g.: > > ovs_assert(ofpact->type == OFPACT_CONJUNCTION); > > Or a VLOG_WARN_RL(), I'm on the fence whether we should crash or not if > we determine there's a bug here. > > I see that all flows here must be "pure" conjunctive flows but in theory > flow_action_has_conj() could return true also if the flow has a mix of > conj and non-conjunction actions. > > > - > > - struct ofpact_ref *ref = xmalloc(sizeof *ref); > > + struct ofpact_ref *ref = &refs[index++]; > > ref->ofpact = ofpact; > > uint32_t hash = hash_bytes(ofpact, ofpact->len, 0); > > hmap_insert(&existing_conj, &ref->hmap_node, hash); > > } > > > > - /* There's already a flow with this particular match and action > > - * 'conjunction'. Append the action to that flow rather than > > - * adding a new flow. > > - */ > > - uint64_t compound_stub[64 / 8]; > > - struct ofpbuf compound; > > - ofpbuf_use_stub(&compound, compound_stub, > sizeof(compound_stub)); > > - ofpbuf_put(&compound, existing->flow.ofpacts, > > - existing->flow.ofpacts_len); > > - > > - OFPACT_FOR_EACH (ofpact, f->flow.ofpacts, f->flow.ofpacts_len) { > > - if (ofpact->type != OFPACT_CONJUNCTION || > > - !ofpact_ref_find(&existing_conj, ofpact)) { > > - ofpbuf_put(&compound, ofpact, > OFPACT_ALIGN(ofpact->len)); > > - } > > - } > > - > > - ofpact_refs_destroy(&existing_conj); > > - > > + actions = create_conjuction_actions(conjunctions, > &existing_conj); > > mem_stats.desired_flow_usage -= desired_flow_size(existing); > > - free(existing->flow.ofpacts); > > - existing->flow.ofpacts = xmemdup(compound.data, compound.size); > > - existing->flow.ofpacts_len = compound.size; > > + ofpacts_append(&existing->flow, actions); > > mem_stats.desired_flow_usage += desired_flow_size(existing); > > > > - ofpbuf_uninit(&compound); > > - desired_flow_destroy(f); > > + > > + free(refs); > > + hmap_destroy(&existing_conj); > > + > > f = existing; > > > > /* Since the flow now shared by more than one SB lflows, don't > track > > @@ -1388,17 +1407,20 @@ ofctrl_add_or_append_flow(struct > ovn_desired_flow_table *desired_flows, > > } > > link_flow_to_sb(desired_flows, f, sb_uuid, NULL); > > } else { > > + actions = create_conjuction_actions(conjunctions, NULL); > > + f = desired_flow_alloc(table_id, priority, 0, match, actions, > > + meter_id); > > hmap_insert(&desired_flows->match_flow_table, > &f->match_hmap_node, > > f->flow.hash); > > link_flow_to_sb(desired_flows, f, sb_uuid, as_info); > > } > > track_flow_add_or_modify(desired_flows, f); > > > > - if (existing) { > > - ovn_flow_log(&f->flow, "ofctrl_add_or_append_flow (append)"); > > - } else { > > - ovn_flow_log(&f->flow, "ofctrl_add_or_append_flow (add)"); > > - } > > + ovn_flow_log(&f->flow, existing > > + ? "ofctrl_add_or_append_conj_flow (append)" > > + : "ofctrl_add_or_append_conj_flow (add)"); > > + ovn_flow_uninit(&search_flow); > > + ofpbuf_delete(actions); > > } > > > > void > > @@ -1621,13 +1643,13 @@ remove_flows_from_sb_to_flow(struct > ovn_desired_flow_table *flow_table, > > static void > > ovn_flow_init(struct ovn_flow *f, uint8_t table_id, uint16_t priority, > > uint64_t cookie, const struct match *match, > > - const struct ofpbuf *actions, uint32_t meter_id) > > + void *actions, size_t action_len, uint32_t meter_id) > > { > > f->table_id = table_id; > > f->priority = priority; > > minimatch_init(&f->match, match); > > - f->ofpacts = xmemdup(actions->data, actions->size); > > - f->ofpacts_len = actions->size; > > + f->ofpacts = actions ? xmemdup(actions, action_len) : NULL; > > + f->ofpacts_len = action_len; > > f->hash = ovn_flow_match_hash(f); > > f->cookie = cookie; > > f->ctrl_meter_id = meter_id; > > @@ -1651,8 +1673,8 @@ desired_flow_alloc(uint8_t table_id, uint16_t > priority, uint64_t cookie, > > ovs_list_init(&f->track_list_node); > > f->installed_flow = NULL; > > f->is_deleted = false; > > - ovn_flow_init(&f->flow, table_id, priority, cookie, match, actions, > > - meter_id); > > + ovn_flow_init(&f->flow, table_id, priority, cookie, match, > actions->data, > > + actions->size, meter_id); > > > > mem_stats.desired_flow_usage += desired_flow_size(f); > > return f; > > diff --git a/controller/ofctrl.h b/controller/ofctrl.h > > index 76e2fbece..abd2ff1c9 100644 > > --- a/controller/ofctrl.h > > +++ b/controller/ofctrl.h > > @@ -34,6 +34,7 @@ struct sbrec_meter_table; > > struct sbrec_ecmp_nexthop_table; > > struct shash; > > struct tracked_acl_ids; > > +struct vector; > > > > struct ovn_desired_flow_table { > > /* Hash map flow table using flow match conditions as hash key.*/ > > @@ -96,13 +97,13 @@ void ofctrl_add_flow(struct ovn_desired_flow_table > *, uint8_t table_id, > > const struct match *, const struct ofpbuf *ofpacts, > > const struct uuid *); > > > > -void ofctrl_add_or_append_flow(struct ovn_desired_flow_table *, > > - uint8_t table_id, uint16_t priority, > > - uint64_t cookie, const struct match *, > > - const struct ofpbuf *actions, > > - const struct uuid *sb_uuid, > > - uint32_t meter_id, > > - const struct addrset_info *); > > +void ofctrl_add_or_append_conj_flow(struct ovn_desired_flow_table *, > > + uint8_t table_id, uint16_t priority, > > + const struct match *, > > + const struct vector *conjunctions, > > + const struct uuid *sb_uuid, > > + uint32_t meter_id, > > + const struct addrset_info *); > > > > void ofctrl_add_flow_metered(struct ovn_desired_flow_table > *desired_flows, > > uint8_t table_id, uint16_t priority, > > The rest looks good to me. With the small typos/nits addressed and > optionally with the assert/log added: > > Acked-by: Dumitru Ceara <dce...@redhat.com> > > Thanks, > Dumitru > > Thank you Mark and Dumitru, I have addressed all nits, added an assert and merged this into main. Regards, Ales _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev