[ovs-dev] [PATCH ovn v3 5/6] northd: Add support for Logical Datapath Groups.

Ilya Maximets i.maximets at ovn.org
Fri Dec 4 17:54:13 UTC 2020


northd will create only one logical flow with a single logical
datapath group instead of several lflows that differs only by a
datapath column.  We're avoiding creation of datapath groups with
only one datapath to save space.  Old 'logical_datapath' column
used instead.

Feature is disabled.  Configuration option to enable it will be
added in the next commit.

Note: ovn-controller highly depnds on fact that logical flow that
uses multicast group is always received after or in the same update
with the corresponding multicast group.  That will not be the case
with datapath groups.  For that reason northd will not use datapath
groups for such logical flows.

Co-authored-by: Dumitru Ceara <dceara at redhat.com>
Signed-off-by: Dumitru Ceara <dceara at redhat.com>
Signed-off-by: Ilya Maximets <i.maximets at ovn.org>
Acked-by: Mark Michelson <mmichels at redhat.com>
---
 lib/ovn-util.c      |  14 +--
 lib/ovn-util.h      |   3 +-
 northd/ovn-northd.c | 300 ++++++++++++++++++++++++++++++++++++--------
 3 files changed, 253 insertions(+), 64 deletions(-)

diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index 97d27efa3..f62c97e96 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -505,24 +505,16 @@ ovn_is_known_nb_lsp_type(const char *type)
 uint32_t
 sbrec_logical_flow_hash(const struct sbrec_logical_flow *lf)
 {
-    const struct sbrec_datapath_binding *ld = lf->logical_datapath;
-    if (!ld) {
-        return 0;
-    }
-
-    return ovn_logical_flow_hash(&ld->header_.uuid,
-                                 lf->table_id, lf->pipeline,
+    return ovn_logical_flow_hash(lf->table_id, lf->pipeline,
                                  lf->priority, lf->match, lf->actions);
 }
 
 uint32_t
-ovn_logical_flow_hash(const struct uuid *logical_datapath,
-                      uint8_t table_id, const char *pipeline,
+ovn_logical_flow_hash(uint8_t table_id, const char *pipeline,
                       uint16_t priority,
                       const char *match, const char *actions)
 {
-    size_t hash = uuid_hash(logical_datapath);
-    hash = hash_2words((table_id << 16) | priority, hash);
+    size_t hash = hash_2words((table_id << 16) | priority, 0);
     hash = hash_string(pipeline, hash);
     hash = hash_string(match, hash);
     return hash_string(actions, hash);
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
index 280bcc3e4..1d2f7a9c5 100644
--- a/lib/ovn-util.h
+++ b/lib/ovn-util.h
@@ -102,8 +102,7 @@ const char *db_table_usage(struct ds *tables,
 bool ovn_is_known_nb_lsp_type(const char *type);
 
 uint32_t sbrec_logical_flow_hash(const struct sbrec_logical_flow *);
-uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
-                               uint8_t table_id, const char *pipeline,
+uint32_t ovn_logical_flow_hash(uint8_t table_id, const char *pipeline,
                                uint16_t priority,
                                const char *match, const char *actions);
 bool datapath_is_switch(const struct sbrec_datapath_binding *);
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 4ca027165..be6cb4201 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -25,6 +25,7 @@
 #include "openvswitch/dynamic-string.h"
 #include "fatal-signal.h"
 #include "hash.h"
+#include "hmapx.h"
 #include "openvswitch/hmap.h"
 #include "openvswitch/json.h"
 #include "ovn/lex.h"
@@ -4093,7 +4094,7 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups,
 struct ovn_lflow {
     struct hmap_node hmap_node;
 
-    struct ovn_datapath *od;
+    struct hmapx od;           /* Hash map of 'struct ovn_datapath *'. */
     enum ovn_stage stage;
     uint16_t priority;
     char *match;
@@ -4102,11 +4103,15 @@ struct ovn_lflow {
     const char *where;
 };
 
+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);
+
 static size_t
 ovn_lflow_hash(const struct ovn_lflow *lflow)
 {
-    return ovn_logical_flow_hash(&lflow->od->sb->header_.uuid,
-                                 ovn_stage_get_table(lflow->stage),
+    return ovn_logical_flow_hash(ovn_stage_get_table(lflow->stage),
                                  ovn_stage_get_pipeline_name(lflow->stage),
                                  lflow->priority, lflow->match,
                                  lflow->actions);
@@ -4124,20 +4129,19 @@ 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->od == b->od
-            && a->stage == b->stage
+    return (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, struct ovn_datapath *od,
+ovn_lflow_init(struct ovn_lflow *lflow,
                enum ovn_stage stage, uint16_t priority,
                char *match, char *actions, char *stage_hint,
                const char *where)
 {
-    lflow->od = od;
+    hmapx_init(&lflow->od);
     lflow->stage = stage;
     lflow->priority = priority;
     lflow->match = match;
@@ -4146,56 +4150,81 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
     lflow->where = where;
 }
 
+/* If this option is 'true' northd will combine logical flows that differs by
+ * logical datapath only by creating a datapah group. */
+static bool use_logical_dp_groups = false;
+
 /* Adds a row with the specified contents to the Logical_Flow table. */
 static void
 ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
                  enum ovn_stage stage, uint16_t priority,
-                 const char *match, const char *actions,
+                 const char *match, const char *actions, bool shared,
                  const struct ovsdb_idl_row *stage_hint, const char *where)
 {
     ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od));
 
-    struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
-    ovn_lflow_init(lflow, od, stage, priority,
+    struct ovn_lflow *old_lflow, *lflow;
+    size_t hash;
+
+    lflow = xmalloc(sizeof *lflow);
+    ovn_lflow_init(lflow, stage, priority,
                    xstrdup(match), xstrdup(actions),
                    ovn_lflow_hint(stage_hint), where);
-    hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow));
+
+    hash = ovn_lflow_hash(lflow);
+    if (shared && use_logical_dp_groups) {
+        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);
+            return;
+        }
+    }
+
+    hmapx_add(&lflow->od, od);
+    hmap_insert(lflow_map, &lflow->hmap_node, hash);
 }
 
 /* Adds a row with the specified contents to the Logical_Flow table. */
 #define ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \
                                 ACTIONS, STAGE_HINT) \
-    ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \
+    ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, true, \
                      STAGE_HINT, OVS_SOURCE_LOCATOR)
 
 #define ovn_lflow_add(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS) \
-    ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \
-                            ACTIONS, NULL)
+    ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, true, \
+                     NULL, OVS_SOURCE_LOCATOR)
+
+#define ovn_lflow_add_unique_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \
+                                       ACTIONS, STAGE_HINT) \
+    ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, false, \
+                     STAGE_HINT, OVS_SOURCE_LOCATOR)
+
+#define ovn_lflow_add_unique(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS) \
+    ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, false, \
+                     NULL, OVS_SOURCE_LOCATOR)
 
 static struct ovn_lflow *
-ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
+ovn_lflow_find(struct hmap *lflows,
                enum ovn_stage stage, uint16_t priority,
                const char *match, const char *actions, uint32_t hash)
 {
     struct ovn_lflow target;
-    ovn_lflow_init(&target, od, stage, priority,
+    ovn_lflow_init(&target, stage, priority,
                    CONST_CAST(char *, match), CONST_CAST(char *, actions),
                    NULL, NULL);
 
-    struct ovn_lflow *lflow;
-    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) {
-        if (ovn_lflow_equal(lflow, &target)) {
-            return lflow;
-        }
-    }
-    return NULL;
+    return ovn_lflow_find_by_lflow(lflows, &target, hash);
 }
 
 static void
 ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow)
 {
     if (lflow) {
-        hmap_remove(lflows, &lflow->hmap_node);
+        if (lflows) {
+            hmap_remove(lflows, &lflow->hmap_node);
+        }
+        hmapx_destroy(&lflow->od);
         free(lflow->match);
         free(lflow->actions);
         free(lflow->stage_hint);
@@ -4203,6 +4232,19 @@ 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)
+{
+    struct ovn_lflow *lflow;
+    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) {
+        if (ovn_lflow_equal(lflow, target)) {
+            return lflow;
+        }
+    }
+    return NULL;
+}
+
 /* Appends port security constraints on L2 address field 'eth_addr_field'
  * (e.g. "eth.src" or "eth.dst") to 'match'.  'ps_addrs', with 'n_ps_addrs'
  * elements, is the collection of port_security constraints from an
@@ -6301,9 +6343,9 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op,
 
     ds_put_format(&match, "eth.src == %s && (arp.op == 1 || nd_ns)",
                   ds_cstr(&eth_src));
-    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
-                  ds_cstr(&match),
-                  "outport = \""MC_FLOOD_L2"\"; output;");
+    ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
+                         ds_cstr(&match),
+                         "outport = \""MC_FLOOD_L2"\"; output;");
 
     sset_destroy(&all_eth_addrs);
     ds_destroy(&eth_src);
@@ -6356,11 +6398,15 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
         ds_put_format(&actions, "clone {outport = %s; output; }; "
                                 "outport = \""MC_FLOOD_L2"\"; output;",
                       patch_op->json_key);
+        ovn_lflow_add_unique_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP,
+                                       priority, ds_cstr(&match),
+                                       ds_cstr(&actions), stage_hint);
     } else {
         ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
+        ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
+                                ds_cstr(&match), ds_cstr(&actions),
+                                stage_hint);
     }
-    ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
-                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
 
     ds_destroy(&match);
     ds_destroy(&actions);
@@ -7075,16 +7121,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
             /* Flood all IP multicast traffic destined to 224.0.0.X to all
              * ports - RFC 4541, section 2.1.2, item 2.
              */
-            ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 85,
-                          "ip4.mcast && ip4.dst == 224.0.0.0/24",
-                          "outport = \""MC_FLOOD"\"; output;");
+            ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 85,
+                                 "ip4.mcast && ip4.dst == 224.0.0.0/24",
+                                 "outport = \""MC_FLOOD"\"; output;");
 
             /* Flood all IPv6 multicast traffic destined to reserved
              * multicast IPs (RFC 4291, 2.7.1).
              */
-            ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 85,
-                          "ip6.mcast_flood",
-                          "outport = \""MC_FLOOD"\"; output;");
+            ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 85,
+                                 "ip6.mcast_flood",
+                                 "outport = \""MC_FLOOD"\"; output;");
 
             /* Forward uregistered IP multicast to routers with relay enabled
              * and to any ports configured to flood IP multicast traffic.
@@ -7114,13 +7160,14 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_put_cstr(&actions, "drop;");
                 }
 
-                ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
-                              "ip4.mcast || ip6.mcast", ds_cstr(&actions));
+                ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
+                                     "ip4.mcast || ip6.mcast",
+                                     ds_cstr(&actions));
             }
         }
 
-        ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 70, "eth.mcast",
-                      "outport = \""MC_FLOOD"\"; output;");
+        ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 70, "eth.mcast",
+                             "outport = \""MC_FLOOD"\"; output;");
     }
 
     /* Ingress table 19: Add IP multicast flows learnt from IGMP/MLD
@@ -7187,8 +7234,8 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
         ds_put_format(&actions, "outport = \"%s\"; output; ",
                       igmp_group->mcgroup.name);
 
-        ovn_lflow_add(lflows, igmp_group->datapath, S_SWITCH_IN_L2_LKUP, 90,
-                      ds_cstr(&match), ds_cstr(&actions));
+        ovn_lflow_add_unique(lflows, igmp_group->datapath, S_SWITCH_IN_L2_LKUP,
+                             90, ds_cstr(&match), ds_cstr(&actions));
     }
 
     /* Ingress table 19: Destination lookup, unicast handling (priority 50), */
@@ -10181,8 +10228,8 @@ build_mcast_lookup_flows_for_lrouter(
             }
             ds_put_format(actions, "outport = \"%s\"; ip.ttl--; next;",
                           igmp_group->mcgroup.name);
-            ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 500,
-                          ds_cstr(match), ds_cstr(actions));
+            ovn_lflow_add_unique(lflows, od, S_ROUTER_IN_IP_ROUTING, 500,
+                                 ds_cstr(match), ds_cstr(actions));
         }
 
         /* If needed, flood unregistered multicast on statically configured
@@ -11216,6 +11263,77 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
     build_lrouter_flows(datapaths, ports, lflows, meter_groups, lbs);
 }
 
+struct ovn_dp_group {
+    struct hmapx map;
+    struct sbrec_logical_dp_group *dp_group;
+    struct hmap_node node;
+};
+
+static struct ovn_dp_group *
+ovn_dp_group_find(const struct hmap *dp_groups,
+                  const struct hmapx *od, uint32_t hash)
+{
+    struct ovn_dp_group *dpg;
+
+    HMAP_FOR_EACH_WITH_HASH (dpg, node, hash, dp_groups) {
+        if (hmapx_equals(&dpg->map, od)) {
+            return dpg;
+        }
+    }
+    return NULL;
+}
+
+static struct sbrec_logical_dp_group *
+ovn_sb_insert_logical_dp_group(struct northd_context *ctx,
+                                     const struct hmapx *od)
+{
+    struct sbrec_logical_dp_group *dp_group;
+    const struct sbrec_datapath_binding **sb;
+    const struct hmapx_node *node;
+    int n = 0;
+
+    sb = xmalloc(hmapx_count(od) * sizeof *sb);
+    HMAPX_FOR_EACH (node, od) {
+        sb[n++] = ((struct ovn_datapath *) node->data)->sb;
+    }
+    dp_group = sbrec_logical_dp_group_insert(ctx->ovnsb_txn);
+    sbrec_logical_dp_group_set_datapaths(
+        dp_group, (struct sbrec_datapath_binding **) sb, n);
+    free(sb);
+
+    return dp_group;
+}
+
+static void
+ovn_sb_set_lflow_logical_datapaths(
+    struct northd_context *ctx,
+    struct hmap *dp_groups,
+    const struct sbrec_logical_flow *sbflow,
+    const struct hmapx *od)
+{
+    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;
+        }
+        return;
+    }
+
+    dpg = ovn_dp_group_find(dp_groups, od, hash_int(hmapx_count(od), 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);
+}
 
 /* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB database,
  * constructing their contents based on the OVN_NB database. */
@@ -11232,36 +11350,102 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
                                     port_groups, &lflows, mcgroups,
                                     igmp_groups, meter_groups, lbs);
 
+    /* Collecting all unique datapath groups. */
+    struct hmap dp_groups = HMAP_INITIALIZER(&dp_groups);
+    struct ovn_lflow *lflow;
+    HMAP_FOR_EACH (lflow, hmap_node, &lflows) {
+        uint32_t hash = hash_int(hmapx_count(&lflow->od), 0);
+        struct ovn_dp_group *dpg;
+
+        if (hmapx_count(&lflow->od) == 1) {
+            continue;
+        }
+
+        dpg = ovn_dp_group_find(&dp_groups, &lflow->od, hash);
+        if (!dpg) {
+            dpg = xzalloc(sizeof *dpg);
+            hmapx_clone(&dpg->map, &lflow->od);
+            hmap_insert(&dp_groups, &dpg->node, hash);
+        }
+    }
+
     /* 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 ovn_datapath *od
-            = ovn_datapath_from_sbrec(datapaths, sbflow->logical_datapath);
+        struct sbrec_logical_dp_group *dp_group = sbflow->logical_dp_group;
+        struct ovn_datapath **od;
+        int n_datapaths = 0;
+        size_t i;
+
+        od = xmalloc((dp_group ? dp_group->n_datapaths + 1 : 1) * sizeof *od);
+        /* Check all logical datapaths from the group. */
+        for (i = 0; dp_group && i < dp_group->n_datapaths; i++) {
+            od[n_datapaths] = ovn_datapath_from_sbrec(datapaths,
+                                                      dp_group->datapaths[i]);
+            if (!od[n_datapaths] || ovn_datapath_is_stale(od[n_datapaths])) {
+                continue;
+            }
+            n_datapaths++;
+        }
 
-        if (!od || ovn_datapath_is_stale(od)) {
+        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++;
+            }
+        }
+
+        if (!n_datapaths) {
+            /* This lflow has no valid logical datapaths. */
             sbrec_logical_flow_delete(sbflow);
+            free(od);
             continue;
         }
 
-        enum ovn_datapath_type dp_type = od->nbs ? DP_SWITCH : DP_ROUTER;
+        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;
-        struct ovn_lflow *lflow = ovn_lflow_find(
-            &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id),
+
+        lflow = ovn_lflow_find(
+            &lflows, 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;
+
+            if (n_datapaths != hmapx_count(&lflow->od)) {
+                update_datapaths = true;
+            } else {
+                for (i = 0; i < n_datapaths; i++) {
+                    if (od[i] && !hmapx_contains(&lflow->od, od[i])) {
+                        update_datapaths = true;
+                        break;
+                    }
+                }
+            }
+
+            if (update_datapaths) {
+                ovn_sb_set_lflow_logical_datapaths(ctx, &dp_groups,
+                                                   sbflow, &lflow->od);
+            }
+            /* This lflow updated.  Not needed anymore. */
             ovn_lflow_destroy(&lflows, lflow);
         } else {
             sbrec_logical_flow_delete(sbflow);
         }
+        free(od);
     }
-    struct ovn_lflow *lflow, *next_lflow;
+
+    struct ovn_lflow *next_lflow;
     HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
         const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
         uint8_t table = ovn_stage_get_table(lflow->stage);
 
         sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
-        sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
+        ovn_sb_set_lflow_logical_datapaths(ctx, &dp_groups,
+                                           sbflow, &lflow->od);
         sbrec_logical_flow_set_pipeline(sbflow, pipeline);
         sbrec_logical_flow_set_table_id(sbflow, table);
         sbrec_logical_flow_set_priority(sbflow, lflow->priority);
@@ -11293,6 +11477,13 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
     }
     hmap_destroy(&lflows);
 
+    struct ovn_dp_group *dpg;
+    HMAP_FOR_EACH_POP (dpg, node, &dp_groups) {
+        hmapx_destroy(&dpg->map);
+        free(dpg);
+    }
+    hmap_destroy(&dp_groups);
+
     /* Push changes to the Multicast_Group table to database. */
     const struct sbrec_multicast_group *sbmc, *next_sbmc;
     SBREC_MULTICAST_GROUP_FOR_EACH_SAFE (sbmc, next_sbmc, ctx->ovnsb_idl) {
@@ -12882,12 +13073,19 @@ main(int argc, char *argv[])
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);
     add_column_noalert(ovnsb_idl_loop.idl,
                        &sbrec_logical_flow_col_logical_datapath);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_logical_flow_col_logical_dp_group);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_pipeline);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_table_id);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions);
 
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl,
+                        &sbrec_table_logical_dp_group);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_logical_dp_group_col_datapaths);
+
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group);
     add_column_noalert(ovnsb_idl_loop.idl,
                        &sbrec_multicast_group_col_datapath);
-- 
2.25.4



More information about the dev mailing list