On Fri, Dec 11, 2020 at 11:31 PM Ilya Maximets <i.maxim...@ovn.org> wrote: > > Commit that introduced support for logical datapath groups removed > the 'logical_datapath' column from the lflow hash. If datapath groups > disabled, there will be many flows that are same except for the > 'logical_datapath' column, so they will have exactly same hash. > Since 'logical_datapath' is not involved into comparison inside > ovn_lflow_equal(), all these flows will be considered equal. > > While iterating over Southbound DB records, ovn_lflow_found() will > return first of many "equal" flows and, likely, not the right one. > Comparison of logical datapaths will say that we need to update > the logical datapath, so it will be updated with the value from > the flow that we just found. Flow that we found will be removed from > the hash map. Similar procedure for the next DB record will give > us different "equal" flow leading to the update of the > 'logical_datapath' column for the next record. And so on. This > way we will swap 'logical_datapath' column for almost all logical > flows. Since we're not using same lflow more than once, resulted > database will still be correct, but all these unnecessary steps will > generate huge database transaction if we'll have at least one new > or modified logical flow, because that will cause different order > in which lflows will be found in a hash map. > > Fix that by re-adding 'logical_datapath' column back to hash and to > the check for equality. This way correct lflows could be found, so > there will be no unnecessary updates. > > Some functions and variables renamed to better describe their meaning. > Also size_t replaced with uint32_t to avoid confusion. lib/hash.c > always returns uint32_t as a hash value and that is important because > we will want to update current hash with the datapath value and not to > re-calculate it for all lflow columns. > > Fixes: bfed22400675 ("northd: Add support for Logical Datapath Groups.") > Reported-by: Dumitru Ceara <dce...@redhat.com> > Acked-by: Dumitru Ceara <dce...@redhat.com> > Signed-off-by: Ilya Maximets <i.maxim...@ovn.org>
Thanks. I applied this patch to master and branch-20.12. Numan > --- > > Version 3: > * Fixed grammar and a smal style nits from Dumitru. > * Added ACK from Dumitru. > > Version 2: > * Removed unused incorrect condition from the ovn_logical_flow_hash_datapath > function. It should not return 0, but should return 'hash' instead. > Anyway, there is no code that uses this branch, so I just removed it to > make the code cleaner: > - return logical_datapath ? hash_add(hash, > uuid_hash(logical_datapath)) : 0; > + return hash_add(hash, uuid_hash(logical_datapath)); > > lib/ovn-util.c | 15 ++++- > lib/ovn-util.h | 2 + > northd/ovn-northd.c | 134 ++++++++++++++++++++++++++++---------------- > 3 files changed, 101 insertions(+), 50 deletions(-) > > diff --git a/lib/ovn-util.c b/lib/ovn-util.c > index f62c97e96..2136f90fe 100644 > --- a/lib/ovn-util.c > +++ b/lib/ovn-util.c > @@ -505,8 +505,12 @@ ovn_is_known_nb_lsp_type(const char *type) > uint32_t > sbrec_logical_flow_hash(const struct sbrec_logical_flow *lf) > { > - return ovn_logical_flow_hash(lf->table_id, lf->pipeline, > - lf->priority, lf->match, lf->actions); > + const struct sbrec_datapath_binding *ld = lf->logical_datapath; > + uint32_t hash = ovn_logical_flow_hash(lf->table_id, lf->pipeline, > + lf->priority, lf->match, > + lf->actions); > + > + return ld ? ovn_logical_flow_hash_datapath(&ld->header_.uuid, hash) : > hash; > } > > uint32_t > @@ -520,6 +524,13 @@ ovn_logical_flow_hash(uint8_t table_id, const char > *pipeline, > return hash_string(actions, hash); > } > > +uint32_t > +ovn_logical_flow_hash_datapath(const struct uuid *logical_datapath, > + uint32_t hash) > +{ > + return hash_add(hash, uuid_hash(logical_datapath)); > +} > + > bool > datapath_is_switch(const struct sbrec_datapath_binding *ldp) > { > diff --git a/lib/ovn-util.h b/lib/ovn-util.h > index 1d2f7a9c5..679f47a97 100644 > --- a/lib/ovn-util.h > +++ b/lib/ovn-util.h > @@ -105,6 +105,8 @@ uint32_t sbrec_logical_flow_hash(const struct > sbrec_logical_flow *); > uint32_t ovn_logical_flow_hash(uint8_t table_id, const char *pipeline, > uint16_t priority, > const char *match, const char *actions); > +uint32_t ovn_logical_flow_hash_datapath(const struct uuid *logical_datapath, > + uint32_t hash); > bool datapath_is_switch(const struct sbrec_datapath_binding *); > int datapath_snat_ct_zone(const struct sbrec_datapath_binding *ldp); > void ovn_conn_show(struct unixctl_conn *conn, int argc OVS_UNUSED, > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c > index 957242367..d94c735ee 100644 > --- a/northd/ovn-northd.c > +++ b/northd/ovn-northd.c > @@ -4097,7 +4097,8 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups, > struct ovn_lflow { > struct hmap_node hmap_node; > > - struct hmapx od; /* Hash map of 'struct ovn_datapath *'. */ > + struct ovn_datapath *od; /* 'logical_datapath' in SB schema. */ > + struct hmapx od_group; /* Hash map of 'struct ovn_datapath *'. */ > enum ovn_stage stage; > uint16_t priority; > char *match; > @@ -4109,9 +4110,9 @@ struct ovn_lflow { > static void ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow); > static struct ovn_lflow * ovn_lflow_find_by_lflow(const struct hmap *, > const struct ovn_lflow *, > - size_t hash); > + uint32_t hash); > > -static size_t > +static uint32_t > ovn_lflow_hash(const struct ovn_lflow *lflow) > { > return ovn_logical_flow_hash(ovn_stage_get_table(lflow->stage), > @@ -4132,19 +4133,21 @@ ovn_lflow_hint(const struct ovsdb_idl_row *row) > static bool > ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b) > { > - return (a->stage == b->stage > + return (a->od == b->od > + && a->stage == b->stage > && a->priority == b->priority > && !strcmp(a->match, b->match) > && !strcmp(a->actions, b->actions)); > } > > static void > -ovn_lflow_init(struct ovn_lflow *lflow, > +ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od, > enum ovn_stage stage, uint16_t priority, > char *match, char *actions, char *stage_hint, > const char *where) > { > - hmapx_init(&lflow->od); > + hmapx_init(&lflow->od_group); > + lflow->od = od; > lflow->stage = stage; > lflow->priority = priority; > lflow->match = match; > @@ -4167,10 +4170,13 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct > ovn_datapath *od, > ovs_assert(ovn_stage_to_datapath_type(stage) == > ovn_datapath_get_type(od)); > > struct ovn_lflow *old_lflow, *lflow; > - size_t hash; > + uint32_t hash; > > lflow = xmalloc(sizeof *lflow); > - ovn_lflow_init(lflow, stage, priority, > + /* While adding new logical flows we're not setting single datapath, but > + * collecting a group. 'od' will be updated later for all flows with > only > + * one datapath in a group, so it could be hashed correctly. */ > + ovn_lflow_init(lflow, NULL, stage, priority, > xstrdup(match), xstrdup(actions), > ovn_lflow_hint(stage_hint), where); > > @@ -4179,12 +4185,12 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct > ovn_datapath *od, > old_lflow = ovn_lflow_find_by_lflow(lflow_map, lflow, hash); > if (old_lflow) { > ovn_lflow_destroy(NULL, lflow); > - hmapx_add(&old_lflow->od, od); > + hmapx_add(&old_lflow->od_group, od); > return; > } > } > > - hmapx_add(&lflow->od, od); > + hmapx_add(&lflow->od_group, od); > hmap_insert(lflow_map, &lflow->hmap_node, hash); > } > > @@ -4218,12 +4224,12 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct > ovn_datapath *od, > NULL, OVS_SOURCE_LOCATOR) > > static struct ovn_lflow * > -ovn_lflow_find(struct hmap *lflows, > +ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od, > enum ovn_stage stage, uint16_t priority, > const char *match, const char *actions, uint32_t hash) > { > struct ovn_lflow target; > - ovn_lflow_init(&target, stage, priority, > + ovn_lflow_init(&target, od, stage, priority, > CONST_CAST(char *, match), CONST_CAST(char *, actions), > NULL, NULL); > > @@ -4237,7 +4243,7 @@ ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow > *lflow) > if (lflows) { > hmap_remove(lflows, &lflow->hmap_node); > } > - hmapx_destroy(&lflow->od); > + hmapx_destroy(&lflow->od_group); > free(lflow->match); > free(lflow->actions); > free(lflow->stage_hint); > @@ -4247,7 +4253,7 @@ ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow > *lflow) > > static struct ovn_lflow * > ovn_lflow_find_by_lflow(const struct hmap *lflows, > - const struct ovn_lflow *target, size_t hash) > + const struct ovn_lflow *target, uint32_t hash) > { > struct ovn_lflow *lflow; > HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) { > @@ -11318,33 +11324,28 @@ ovn_sb_insert_logical_dp_group(struct > northd_context *ctx, > } > > static void > -ovn_sb_set_lflow_logical_datapaths( > +ovn_sb_set_lflow_logical_dp_group( > struct northd_context *ctx, > struct hmap *dp_groups, > const struct sbrec_logical_flow *sbflow, > - const struct hmapx *od) > + const struct hmapx *od_group) > { > - const struct hmapx_node *node; > struct ovn_dp_group *dpg; > > - if (hmapx_count(od) == 1) { > - HMAPX_FOR_EACH (node, od) { > - struct ovn_datapath *dp = node->data; > - > - sbrec_logical_flow_set_logical_datapath(sbflow, dp->sb); > - sbrec_logical_flow_set_logical_dp_group(sbflow, NULL); > - break; > - } > + if (!hmapx_count(od_group)) { > + sbrec_logical_flow_set_logical_dp_group(sbflow, NULL); > return; > } > > - dpg = ovn_dp_group_find(dp_groups, od, hash_int(hmapx_count(od), 0)); > + ovs_assert(hmapx_count(od_group) != 1); > + > + dpg = ovn_dp_group_find(dp_groups, od_group, > + hash_int(hmapx_count(od_group), 0)); > ovs_assert(dpg != NULL); > > if (!dpg->dp_group) { > dpg->dp_group = ovn_sb_insert_logical_dp_group(ctx, &dpg->map); > } > - sbrec_logical_flow_set_logical_datapath(sbflow, NULL); > sbrec_logical_flow_set_logical_dp_group(sbflow, dpg->dp_group); > } > > @@ -11365,28 +11366,55 @@ build_lflows(struct northd_context *ctx, struct > hmap *datapaths, > > /* Collecting all unique datapath groups. */ > struct hmap dp_groups = HMAP_INITIALIZER(&dp_groups); > + struct hmapx single_dp_lflows = HMAPX_INITIALIZER(&single_dp_lflows); > struct ovn_lflow *lflow; > HMAP_FOR_EACH (lflow, hmap_node, &lflows) { > - uint32_t hash = hash_int(hmapx_count(&lflow->od), 0); > + uint32_t hash = hash_int(hmapx_count(&lflow->od_group), 0); > struct ovn_dp_group *dpg; > > - if (hmapx_count(&lflow->od) == 1) { > + ovs_assert(hmapx_count(&lflow->od_group)); > + > + if (hmapx_count(&lflow->od_group) == 1) { > + /* There is only one datapath, so it should be moved out of the > + * group to a single 'od'. */ > + const struct hmapx_node *node; > + HMAPX_FOR_EACH (node, &lflow->od_group) { > + lflow->od = node->data; > + break; > + } > + hmapx_clear(&lflow->od_group); > + /* Logical flow should be re-hashed later to allow lookups. */ > + hmapx_add(&single_dp_lflows, lflow); > continue; > } > > - dpg = ovn_dp_group_find(&dp_groups, &lflow->od, hash); > + dpg = ovn_dp_group_find(&dp_groups, &lflow->od_group, hash); > if (!dpg) { > dpg = xzalloc(sizeof *dpg); > - hmapx_clone(&dpg->map, &lflow->od); > + hmapx_clone(&dpg->map, &lflow->od_group); > hmap_insert(&dp_groups, &dpg->node, hash); > } > } > > + /* Adding datapath to the flow hash for logical flows that have only one, > + * so they could be found by the southbound db record. */ > + const struct hmapx_node *node; > + uint32_t hash; > + HMAPX_FOR_EACH (node, &single_dp_lflows) { > + lflow = node->data; > + hash = hmap_node_hash(&lflow->hmap_node); > + hmap_remove(&lflows, &lflow->hmap_node); > + hash = ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid, > + hash); > + hmap_insert(&lflows, &lflow->hmap_node, hash); > + } > + hmapx_destroy(&single_dp_lflows); > + > /* Push changes to the Logical_Flow table to database. */ > const struct sbrec_logical_flow *sbflow, *next_sbflow; > SBREC_LOGICAL_FLOW_FOR_EACH_SAFE (sbflow, next_sbflow, ctx->ovnsb_idl) { > struct sbrec_logical_dp_group *dp_group = sbflow->logical_dp_group; > - struct ovn_datapath **od; > + struct ovn_datapath **od, *logical_datapath_od = NULL; > int n_datapaths = 0; > size_t i; > > @@ -11403,45 +11431,52 @@ build_lflows(struct northd_context *ctx, struct > hmap *datapaths, > > struct sbrec_datapath_binding *dp = sbflow->logical_datapath; > if (dp) { > - od[n_datapaths] = ovn_datapath_from_sbrec(datapaths, dp); > - if (od[n_datapaths] && !ovn_datapath_is_stale(od[n_datapaths])) { > - n_datapaths++; > + logical_datapath_od = ovn_datapath_from_sbrec(datapaths, dp); > + if (logical_datapath_od > + && ovn_datapath_is_stale(logical_datapath_od)) { > + logical_datapath_od = NULL; > } > } > > - if (!n_datapaths) { > + if (!n_datapaths && !logical_datapath_od) { > /* This lflow has no valid logical datapaths. */ > sbrec_logical_flow_delete(sbflow); > free(od); > continue; > } > > - enum ovn_datapath_type dp_type = od[0]->nbs ? DP_SWITCH : DP_ROUTER; > enum ovn_pipeline pipeline > = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT; > + enum ovn_datapath_type dp_type; > > + if (n_datapaths) { > + dp_type = od[0]->nbs ? DP_SWITCH : DP_ROUTER; > + } else { > + dp_type = logical_datapath_od->nbs ? DP_SWITCH : DP_ROUTER; > + } > lflow = ovn_lflow_find( > - &lflows, ovn_stage_build(dp_type, pipeline, sbflow->table_id), > + &lflows, logical_datapath_od, > + ovn_stage_build(dp_type, pipeline, sbflow->table_id), > sbflow->priority, sbflow->match, sbflow->actions, sbflow->hash); > if (lflow) { > /* This is a valid lflow. Checking if the datapath group needs > * updates. */ > - bool update_datapaths = false; > + bool update_dp_group = false; > > - if (n_datapaths != hmapx_count(&lflow->od)) { > - update_datapaths = true; > + if (n_datapaths != hmapx_count(&lflow->od_group)) { > + update_dp_group = true; > } else { > for (i = 0; i < n_datapaths; i++) { > - if (od[i] && !hmapx_contains(&lflow->od, od[i])) { > - update_datapaths = true; > + if (od[i] && !hmapx_contains(&lflow->od_group, od[i])) { > + update_dp_group = true; > break; > } > } > } > > - if (update_datapaths) { > - ovn_sb_set_lflow_logical_datapaths(ctx, &dp_groups, > - sbflow, &lflow->od); > + if (update_dp_group) { > + ovn_sb_set_lflow_logical_dp_group(ctx, &dp_groups, > + sbflow, &lflow->od_group); > } > /* This lflow updated. Not needed anymore. */ > ovn_lflow_destroy(&lflows, lflow); > @@ -11457,8 +11492,11 @@ build_lflows(struct northd_context *ctx, struct hmap > *datapaths, > uint8_t table = ovn_stage_get_table(lflow->stage); > > sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn); > - ovn_sb_set_lflow_logical_datapaths(ctx, &dp_groups, > - sbflow, &lflow->od); > + if (lflow->od) { > + sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb); > + } > + ovn_sb_set_lflow_logical_dp_group(ctx, &dp_groups, > + sbflow, &lflow->od_group); > sbrec_logical_flow_set_pipeline(sbflow, pipeline); > sbrec_logical_flow_set_table_id(sbflow, table); > sbrec_logical_flow_set_priority(sbflow, lflow->priority); > -- > 2.25.4 > > _______________________________________________ > 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