[ovs-dev] [RFC 6/8] ovn-controller: Incremental logical flow processing

Han Zhou zhouhan at gmail.com
Tue Feb 20 19:04:42 UTC 2018


Persistents flow-table and implements change handler of flow_output
for SB lflow changes.

(TODO: test case 2338 failed)

Signed-off-by: Han Zhou <hzhou8 at ebay.com>
---
 include/ovn/actions.h           |   3 +
 ovn/controller/lflow.c          | 104 ++++++++++++++++----
 ovn/controller/lflow.h          |  10 +-
 ovn/controller/ofctrl.c         | 205 ++++++++++++++++++++++++++++------------
 ovn/controller/ofctrl.h         |  11 ++-
 ovn/controller/ovn-controller.c |  79 +++++++++++-----
 ovn/controller/physical.c       | 100 +++++++++++---------
 ovn/controller/physical.h       |   2 +-
 ovn/lib/actions.c               |   6 +-
 ovn/lib/extend-table.c          |  31 ++++--
 ovn/lib/extend-table.h          |   9 +-
 11 files changed, 391 insertions(+), 169 deletions(-)

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 9554a39..0f40199 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -501,6 +501,9 @@ struct ovnact_encode_params {
     /* A struct to figure out the meter_id for meter actions. */
     struct ovn_extend_table *meter_table;
 
+    /* The logical flow uuid that drove this action. */
+    struct uuid lflow_uuid;
+
     /* OVN maps each logical flow table (ltable), one-to-one, onto a physical
      * OpenFlow flow table (ptable).  A number of parameters describe this
      * mapping and data related to flow tables:
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index df125b1..3d465a5 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -70,7 +70,6 @@ static void consider_logical_flow(struct controller_ctx *ctx,
                                   struct hmap *nd_ra_opts,
                                   uint32_t *conj_id_ofs,
                                   const struct shash *addr_sets,
-                                  struct hmap *flow_table,
                                   struct sset *active_tunnels,
                                   struct sset *local_lport_ids);
 
@@ -149,7 +148,6 @@ add_logical_flows(struct controller_ctx *ctx,
                   struct ovn_extend_table *meter_table,
                   const struct sbrec_chassis *chassis,
                   const struct shash *addr_sets,
-                  struct hmap *flow_table,
                   struct sset *active_tunnels,
                   struct sset *local_lport_ids)
 {
@@ -179,7 +177,7 @@ add_logical_flows(struct controller_ctx *ctx,
                               lflow, local_datapaths,
                               group_table, meter_table, chassis,
                               &dhcp_opts, &dhcpv6_opts, &nd_ra_opts,
-                              &conj_id_ofs, addr_sets, flow_table,
+                              &conj_id_ofs, addr_sets,
                               active_tunnels, local_lport_ids);
     }
 
@@ -188,6 +186,69 @@ add_logical_flows(struct controller_ctx *ctx,
     nd_ra_opts_destroy(&nd_ra_opts);
 }
 
+void
+lflow_handle_changed_flows(struct controller_ctx *ctx,
+                  const struct sbrec_chassis *chassis,
+                  const struct chassis_index *chassis_index,
+                  const struct hmap *local_datapaths,
+                  struct ovn_extend_table *group_table,
+                  struct ovn_extend_table *meter_table,
+                  const struct shash *addr_sets,
+                  struct sset *active_tunnels,
+                  struct sset *local_lport_ids)
+{
+    // TODO: how to set conj_id_ofs in incremental processing?
+    uint32_t conj_id_ofs = 1;
+    const struct sbrec_logical_flow *lflow;
+
+    struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
+    struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
+    const struct sbrec_dhcp_options *dhcp_opt_row;
+    SBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opt_row, ctx->ovnsb_idl) {
+        dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
+                     dhcp_opt_row->type);
+    }
+
+
+    const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
+    SBREC_DHCPV6_OPTIONS_FOR_EACH(dhcpv6_opt_row, ctx->ovnsb_idl) {
+       dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
+                    dhcpv6_opt_row->type);
+    }
+
+    struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
+    nd_ra_opts_init(&nd_ra_opts);
+
+    SBREC_LOGICAL_FLOW_FOR_EACH_TRACKED (lflow, ctx->ovnsb_idl) {
+        /* Remove any flows that should be removed. */
+        if (sbrec_logical_flow_is_deleted(lflow)) {
+            VLOG_DBG("handle deleted lflow "UUID_FMT,
+                     UUID_ARGS(&lflow->header_.uuid));
+            ofctrl_remove_flows(&lflow->header_.uuid);
+        } else {
+            /* Now, add/modify existing flows. If the logical
+             * flow is a modification, just remove the flows
+             * for this row, and then add new flows. */
+            if (!sbrec_logical_flow_is_new(lflow)) {
+                VLOG_DBG("handle updated lflow "UUID_FMT,
+                         UUID_ARGS(&lflow->header_.uuid));
+                ofctrl_remove_flows(&lflow->header_.uuid);
+            }
+            VLOG_DBG("handle new lflow "UUID_FMT,
+                     UUID_ARGS(&lflow->header_.uuid));
+            consider_logical_flow(ctx, chassis_index,
+                                  lflow, local_datapaths,
+                                  group_table, meter_table, chassis,
+                                  &dhcp_opts, &dhcpv6_opts, &nd_ra_opts,
+                                  &conj_id_ofs, addr_sets,
+                                  active_tunnels, local_lport_ids);
+        }
+    }
+    dhcp_opts_destroy(&dhcp_opts);
+    dhcp_opts_destroy(&dhcpv6_opts);
+    nd_ra_opts_destroy(&nd_ra_opts);
+}
+
 static void
 consider_logical_flow(struct controller_ctx *ctx,
                       const struct chassis_index *chassis_index,
@@ -201,7 +262,6 @@ consider_logical_flow(struct controller_ctx *ctx,
                       struct hmap *nd_ra_opts,
                       uint32_t *conj_id_ofs,
                       const struct shash *addr_sets,
-                      struct hmap *flow_table,
                       struct sset *active_tunnels,
                       struct sset *local_lport_ids)
 {
@@ -210,9 +270,13 @@ consider_logical_flow(struct controller_ctx *ctx,
 
     const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
     if (!ldp) {
+        VLOG_DBG("lflow "UUID_FMT" has no datapath binding, skip",
+                 UUID_ARGS(&lflow->header_.uuid));
         return;
     }
     if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) {
+        VLOG_DBG("lflow "UUID_FMT" is not for local datapath, skip",
+                 UUID_ARGS(&lflow->header_.uuid));
         return;
     }
 
@@ -290,6 +354,8 @@ consider_logical_flow(struct controller_ctx *ctx,
     expr_destroy(expr);
 
     if (hmap_is_empty(&matches)) {
+        VLOG_DBG("lflow "UUID_FMT" matches are empty, skip",
+                 UUID_ARGS(&lflow->header_.uuid));
         ovnacts_free(ovnacts.data, ovnacts.size);
         ofpbuf_uninit(&ovnacts);
         expr_matches_destroy(&matches);
@@ -306,6 +372,7 @@ consider_logical_flow(struct controller_ctx *ctx,
         .is_gateway_router = is_gateway_router(ldp, local_datapaths),
         .group_table = group_table,
         .meter_table = meter_table,
+        .lflow_uuid = lflow->header_.uuid,
 
         .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
         .ingress_ptable = OFTABLE_LOG_INGRESS_PIPELINE,
@@ -334,13 +401,18 @@ consider_logical_flow(struct controller_ctx *ctx,
                 char buf[16];
                 snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64, dp_id, port_id);
                 if (!sset_contains(local_lport_ids, buf)) {
+                    VLOG_DBG("lflow "UUID_FMT
+                             " port %s in match is not local, skip",
+                             UUID_ARGS(&lflow->header_.uuid),
+                             buf);
                     continue;
                 }
             }
         }
         if (!m->n) {
-            ofctrl_add_flow(flow_table, ptable, lflow->priority,
-                            lflow->header_.uuid.parts[0], &m->match, &ofpacts);
+            ofctrl_add_flow(ptable, lflow->priority,
+                            lflow->header_.uuid.parts[0], &m->match, &ofpacts,
+                            &lflow->header_.uuid);
         } else {
             uint64_t conj_stubs[64 / 8];
             struct ofpbuf conj;
@@ -355,8 +427,8 @@ consider_logical_flow(struct controller_ctx *ctx,
                 dst->clause = src->clause;
                 dst->n_clauses = src->n_clauses;
             }
-            ofctrl_add_flow(flow_table, ptable, lflow->priority, 0, &m->match,
-                            &conj);
+            ofctrl_add_flow(ptable, lflow->priority, 0, &m->match,
+                            &conj, &lflow->header_.uuid);
             ofpbuf_uninit(&conj);
         }
     }
@@ -381,8 +453,7 @@ put_load(const uint8_t *data, size_t len,
 
 static void
 consider_neighbor_flow(struct controller_ctx *ctx,
-                       const struct sbrec_mac_binding *b,
-                       struct hmap *flow_table)
+                       const struct sbrec_mac_binding *b)
 {
     const struct sbrec_port_binding *pb
         = lport_lookup_by_name(ctx->ovnsb_idl, b->logical_port);
@@ -424,19 +495,19 @@ consider_neighbor_flow(struct controller_ctx *ctx,
     uint64_t stub[1024 / 8];
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
     put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
-    ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts);
+    ofctrl_add_flow(OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts,
+                    &b->header_.uuid);
     ofpbuf_uninit(&ofpacts);
 }
 
 /* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
  * southbound database. */
 static void
-add_neighbor_flows(struct controller_ctx *ctx,
-                   struct hmap *flow_table)
+add_neighbor_flows(struct controller_ctx *ctx)
 {
     const struct sbrec_mac_binding *b;
     SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) {
-        consider_neighbor_flow(ctx, b, flow_table);
+        consider_neighbor_flow(ctx, b);
     }
 }
 
@@ -450,14 +521,13 @@ lflow_run(struct controller_ctx *ctx,
           struct ovn_extend_table *group_table,
           struct ovn_extend_table *meter_table,
           const struct shash *addr_sets,
-          struct hmap *flow_table,
           struct sset *active_tunnels,
           struct sset *local_lport_ids)
 {
     add_logical_flows(ctx, chassis_index, local_datapaths,
-                      group_table, meter_table, chassis, addr_sets, flow_table,
+                      group_table, meter_table, chassis, addr_sets,
                       active_tunnels, local_lport_ids);
-    add_neighbor_flows(ctx, flow_table);
+    add_neighbor_flows(ctx);
 }
 
 void
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
index 22bf534..ef4ed0e 100644
--- a/ovn/controller/lflow.h
+++ b/ovn/controller/lflow.h
@@ -69,9 +69,17 @@ void lflow_run(struct controller_ctx *,
                struct ovn_extend_table *group_table,
                struct ovn_extend_table *meter_table,
                const struct shash *addr_sets,
-               struct hmap *flow_table,
                struct sset *active_tunnels,
                struct sset *local_lport_ids);
+void lflow_handle_changed_flows(struct controller_ctx *ctx,
+                  const struct sbrec_chassis *chassis,
+                  const struct chassis_index *chassis_index,
+                  const struct hmap *local_datapaths,
+                  struct ovn_extend_table *group_table,
+                  struct ovn_extend_table *extend_table,
+                  const struct shash *addr_sets,
+                  struct sset *active_tunnels,
+                  struct sset *local_lport_ids);
 void lflow_destroy(void);
 
 #endif /* ovn/lflow.h */
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index e6b25af..723f9a7 100644
--- a/ovn/controller/ofctrl.c
+++ b/ovn/controller/ofctrl.c
@@ -20,6 +20,7 @@
 #include "dp-packet.h"
 #include "flow.h"
 #include "hash.h"
+#include "hindex.h"
 #include "lflow.h"
 #include "ofctrl.h"
 #include "openflow/openflow.h"
@@ -52,7 +53,8 @@ VLOG_DEFINE_THIS_MODULE(ofctrl);
 
 /* An OpenFlow flow. */
 struct ovn_flow {
-    struct hmap_node hmap_node; /* For match based hashing. */
+    struct hmap_node match_hmap_node; /* For match based hashing. */
+    struct hindex_node uuid_hindex_node; /* For uuid based hashing. */
     struct ovs_list list_node; /* For handling lists of flows. */
 
     /* Key. */
@@ -61,12 +63,13 @@ struct ovn_flow {
     struct match match;
 
     /* Data. */
+    struct uuid sb_uuid;
     struct ofpact *ofpacts;
     size_t ofpacts_len;
     uint64_t cookie;
 };
 
-static uint32_t ovn_flow_hash(const struct ovn_flow *);
+static uint32_t ovn_flow_match_hash(const struct ovn_flow *);
 static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table,
                                         const struct ovn_flow *target);
 static char *ovn_flow_to_string(const struct ovn_flow *);
@@ -154,11 +157,16 @@ static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *);
 
 static struct ofpbuf *encode_meter_mod(const struct ofputil_meter_mod *);
 
-static void ovn_flow_table_clear(struct hmap *flow_table);
-static void ovn_flow_table_destroy(struct hmap *flow_table);
+static void ovn_desired_flow_table_clear(void);
+static void ovn_installed_flow_table_clear(void);
+static void ovn_desired_flow_table_destroy(void);
+static void ovn_installed_flow_table_destroy(void);
 
 static void ofctrl_recv(const struct ofp_header *, enum ofptype);
 
+static struct hmap match_flow_table = HMAP_INITIALIZER(&match_flow_table);
+static struct hindex uuid_flow_table = HINDEX_INITIALIZER(&uuid_flow_table);
+
 void
 ofctrl_init(struct ovn_extend_table *group_table,
             struct ovn_extend_table *meter_table)
@@ -382,9 +390,6 @@ run_S_CLEAR_FLOWS(void)
     queue_msg(encode_flow_mod(&fm));
     VLOG_DBG("clearing all flows");
 
-    /* Clear installed_flows, to match the state of the switch. */
-    ovn_flow_table_clear(&installed_flows);
-
     /* Send a group_mod to delete all groups. */
     struct ofputil_group_mod gm;
     memset(&gm, 0, sizeof gm);
@@ -395,6 +400,9 @@ run_S_CLEAR_FLOWS(void)
     queue_msg(encode_group_mod(&gm));
     ofputil_uninit_group_mod(&gm);
 
+    /* Clear installed_flows, to match the state of the switch. */
+    ovn_installed_flow_table_clear();
+
     /* Clear existing groups, to match the state of the switch. */
     if (groups) {
         ovn_extend_table_clear(groups, true);
@@ -578,7 +586,8 @@ void
 ofctrl_destroy(void)
 {
     rconn_destroy(swconn);
-    ovn_flow_table_destroy(&installed_flows);
+    ovn_desired_flow_table_destroy();
+    ovn_installed_flow_table_destroy();
     rconn_packet_counter_destroy(tx_counter);
     expr_symtab_destroy(&symtab);
     shash_destroy(&symtab);
@@ -630,15 +639,17 @@ ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
  * the OpenFlow table numbered 'table_id' with the given 'priority' and
  * OpenFlow 'cookie'.  The caller retains ownership of 'match' and 'actions'.
  *
+ * The flow is also added to a hash index based on sb_uuid.
+ *
  * This just assembles the desired flow table in memory.  Nothing is actually
  * sent to the switch until a later call to ofctrl_put().
  *
  * The caller should initialize its own hmap to hold the flows. */
 void
-ofctrl_add_flow(struct hmap *desired_flows,
-                uint8_t table_id, uint16_t priority, uint64_t cookie,
+ofctrl_add_flow(uint8_t table_id, uint16_t priority, uint64_t cookie,
                 const struct match *match,
-                const struct ofpbuf *actions)
+                const struct ofpbuf *actions,
+                const struct uuid *sb_uuid)
 {
     struct ovn_flow *f = xmalloc(sizeof *f);
     f->table_id = table_id;
@@ -646,10 +657,14 @@ ofctrl_add_flow(struct hmap *desired_flows,
     f->match = *match;
     f->ofpacts = xmemdup(actions->data, actions->size);
     f->ofpacts_len = actions->size;
-    f->hmap_node.hash = ovn_flow_hash(f);
+    f->sb_uuid = *sb_uuid;
+    f->match_hmap_node.hash = ovn_flow_match_hash(f);
+    f->uuid_hindex_node.hash = uuid_hash(&f->sb_uuid);
     f->cookie = cookie;
 
-    if (ovn_flow_lookup(desired_flows, f)) {
+    ovn_flow_log(f, "ofctrl_add_flow");
+
+    if (ovn_flow_lookup(&match_flow_table, f)) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
         if (!VLOG_DROP_DBG(&rl)) {
             char *s = ovn_flow_to_string(f);
@@ -661,19 +676,65 @@ ofctrl_add_flow(struct hmap *desired_flows,
         return;
     }
 
-    hmap_insert(desired_flows, &f->hmap_node, f->hmap_node.hash);
+    hmap_insert(&match_flow_table, &f->match_hmap_node,
+                f->match_hmap_node.hash);
+    hindex_insert(&uuid_flow_table, &f->uuid_hindex_node,
+                  f->uuid_hindex_node.hash);
+}
+
+void
+ofctrl_flow_table_clear(void)
+{
+    ovn_desired_flow_table_clear();
+}
+
+/* Removes a bundles of flows from the flow table. */
+void
+ofctrl_remove_flows(const struct uuid *sb_uuid)
+{
+    struct ovn_flow *f, *next;
+    HINDEX_FOR_EACH_WITH_HASH_SAFE (f, next, uuid_hindex_node,
+                                    uuid_hash(sb_uuid),
+                                    &uuid_flow_table) {
+        if (uuid_equals(&f->sb_uuid, sb_uuid)) {
+            ovn_flow_log(f, "ofctrl_remove_flow");
+            hmap_remove(&match_flow_table, &f->match_hmap_node);
+            hindex_remove(&uuid_flow_table, &f->uuid_hindex_node);
+            ovn_flow_destroy(f);
+        }
+    }
+
+    // TODO: refactor to a function
+    /* Remove any group information created by this logical flow. */
+    struct ovn_extend_table_info *g, *next_g;
+    HMAP_FOR_EACH_SAFE (g, next_g, hmap_node, &groups->desired) {
+        if (uuid_equals(&g->lflow_uuid, sb_uuid)) {
+            hmap_remove(&groups->desired, &g->hmap_node);
+            ds_destroy(&g->info);
+            free(g);
+        }
+    }
+
+    /* Remove any meter information created by this logical flow. */
+    struct ovn_extend_table_info *m, *next_m;
+    HMAP_FOR_EACH_SAFE (m, next_m, hmap_node, &meters->desired) {
+        if (uuid_equals(&m->lflow_uuid, sb_uuid)) {
+            hmap_remove(&meters->desired, &m->hmap_node);
+            ds_destroy(&m->info);
+            free(m);
+        }
+    }
 }
 
 
 /* ovn_flow. */
 
-/* Returns a hash of the key in 'f'. */
+/* Returns a hash of the match key in 'f'. */
 static uint32_t
-ovn_flow_hash(const struct ovn_flow *f)
+ovn_flow_match_hash(const struct ovn_flow *f)
 {
     return hash_2words((f->table_id << 16) | f->priority,
                        match_hash(&f->match, 0));
-
 }
 
 /* Duplicate an ovn_flow structure. */
@@ -686,7 +747,9 @@ ofctrl_dup_flow(struct ovn_flow *src)
     dst->match = src->match;
     dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len);
     dst->ofpacts_len = src->ofpacts_len;
-    dst->hmap_node.hash = ovn_flow_hash(dst);
+    dst->sb_uuid = src->sb_uuid;
+    dst->match_hmap_node.hash = ovn_flow_match_hash(dst);
+    dst->uuid_hindex_node.hash = uuid_hash(&src->sb_uuid);
     return dst;
 }
 
@@ -697,7 +760,7 @@ ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target)
 {
     struct ovn_flow *f;
 
-    HMAP_FOR_EACH_WITH_HASH (f, hmap_node, target->hmap_node.hash,
+    HMAP_FOR_EACH_WITH_HASH (f, match_hmap_node, target->match_hmap_node.hash,
                              flow_table) {
         if (f->table_id == target->table_id
             && f->priority == target->priority
@@ -712,6 +775,7 @@ static char *
 ovn_flow_to_string(const struct ovn_flow *f)
 {
     struct ds s = DS_EMPTY_INITIALIZER;
+    ds_put_format(&s, "sb_uuid="UUID_FMT", ", UUID_ARGS(&f->sb_uuid));
     ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id);
     ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
     match_format(&f->match, NULL, &s, OFP_DEFAULT_PRIORITY);
@@ -743,20 +807,39 @@ ovn_flow_destroy(struct ovn_flow *f)
 /* Flow tables of struct ovn_flow. */
 
 static void
-ovn_flow_table_clear(struct hmap *flow_table)
+ovn_desired_flow_table_clear(void)
 {
     struct ovn_flow *f, *next;
-    HMAP_FOR_EACH_SAFE (f, next, hmap_node, flow_table) {
-        hmap_remove(flow_table, &f->hmap_node);
+    HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &match_flow_table) {
+        hmap_remove(&match_flow_table, &f->match_hmap_node);
+        hindex_remove(&uuid_flow_table, &f->uuid_hindex_node);
         ovn_flow_destroy(f);
     }
 }
 
 static void
-ovn_flow_table_destroy(struct hmap *flow_table)
+ovn_desired_flow_table_destroy(void)
 {
-    ovn_flow_table_clear(flow_table);
-    hmap_destroy(flow_table);
+    ovn_desired_flow_table_clear();
+    hmap_destroy(&match_flow_table);
+    hindex_destroy(&uuid_flow_table);
+}
+
+static void
+ovn_installed_flow_table_clear(void)
+{
+    struct ovn_flow *f, *next;
+    HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &installed_flows) {
+        hmap_remove(&installed_flows, &f->match_hmap_node);
+        ovn_flow_destroy(f);
+    }
+}
+
+static void
+ovn_installed_flow_table_destroy(void)
+{
+    ovn_installed_flow_table_clear();
+    hmap_destroy(&installed_flows);
 }
 
 /* Flow table update. */
@@ -836,9 +919,7 @@ ofctrl_can_put(void)
  * with ofctrl_add_flow().
  *
  * Replaces the group table and meter table on the switch, if possible, by the
- *  contents of 'groups->desired'.  Regardless of whether the group table
- * is updated, this deletes all the groups from the 'groups->desired' and frees
- * them. (The hmap itself isn't destroyed.)
+ * contents of 'desired'.
  *
  * Sends conntrack flush messages to each zone in 'pending_ct_zones' that
  * is in the CT_ZONE_OF_QUEUED state and then moves the zone into the
@@ -846,16 +927,14 @@ ofctrl_can_put(void)
  *
  * This should be called after ofctrl_run() within the main loop. */
 void
-ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
+ofctrl_put(struct shash *pending_ct_zones,
            int64_t nb_cfg)
 {
     if (!ofctrl_can_put()) {
-        ovn_flow_table_clear(flow_table);
-        ovn_extend_table_clear(groups, false);
-        ovn_extend_table_clear(meters, false);
         return;
     }
 
+    VLOG_DBG("ofctrl can put");
     /* OpenFlow messages to send to the switch to bring it up-to-date. */
     struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs);
 
@@ -919,8 +998,8 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
      * longer desired, delete them; if any of them should have different
      * actions, update them. */
     struct ovn_flow *i, *next;
-    HMAP_FOR_EACH_SAFE (i, next, hmap_node, &installed_flows) {
-        struct ovn_flow *d = ovn_flow_lookup(flow_table, i);
+    HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) {
+        struct ovn_flow *d = ovn_flow_lookup(&match_flow_table, i);
         if (!d) {
             /* Installed flow is no longer desirable.  Delete it from the
              * switch and from installed_flows. */
@@ -933,9 +1012,13 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
             add_flow_mod(&fm, &msgs);
             ovn_flow_log(i, "removing installed");
 
-            hmap_remove(&installed_flows, &i->hmap_node);
+            hmap_remove(&installed_flows, &i->match_hmap_node);
             ovn_flow_destroy(i);
         } else {
+            if (!uuid_equals(&i->sb_uuid, &d->sb_uuid)) {
+                /* Update installed flow's UUID. */
+                i->sb_uuid = d->sb_uuid;
+            }
             if (!ofpacts_equal(i->ofpacts, i->ofpacts_len,
                                d->ofpacts, d->ofpacts_len)) {
                 /* Update actions in installed flow. */
@@ -952,38 +1035,36 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
 
                 /* Replace 'i''s actions by 'd''s. */
                 free(i->ofpacts);
-                i->ofpacts = d->ofpacts;
+                i->ofpacts = xmemdup(d->ofpacts, d->ofpacts_len);
                 i->ofpacts_len = d->ofpacts_len;
-                d->ofpacts = NULL;
-                d->ofpacts_len = 0;
             }
 
-            hmap_remove(flow_table, &d->hmap_node);
-            ovn_flow_destroy(d);
         }
     }
 
-    /* The previous loop removed from 'flow_table' all of the flows that are
-     * already installed.  Thus, any flows remaining in 'flow_table' need to
-     * be added to the flow table. */
+    /* Iterate through the desired flows and add those that aren't found
+     * in the installed flow table. */
     struct ovn_flow *d;
-    HMAP_FOR_EACH_SAFE (d, next, hmap_node, flow_table) {
-        /* Send flow_mod to add flow. */
-        struct ofputil_flow_mod fm = {
-            .match = d->match,
-            .priority = d->priority,
-            .table_id = d->table_id,
-            .ofpacts = d->ofpacts,
-            .ofpacts_len = d->ofpacts_len,
-            .new_cookie = htonll(d->cookie),
-            .command = OFPFC_ADD,
-        };
-        add_flow_mod(&fm, &msgs);
-        ovn_flow_log(d, "adding installed");
-
-        /* Move 'd' from 'flow_table' to installed_flows. */
-        hmap_remove(flow_table, &d->hmap_node);
-        hmap_insert(&installed_flows, &d->hmap_node, d->hmap_node.hash);
+    HMAP_FOR_EACH (d, match_hmap_node, &match_flow_table) {
+        struct ovn_flow *i = ovn_flow_lookup(&installed_flows, d);
+        if (!i) {
+            /* Send flow_mod to add flow. */
+            struct ofputil_flow_mod fm = {
+                .match = d->match,
+                .priority = d->priority,
+                .table_id = d->table_id,
+                .ofpacts = d->ofpacts,
+                .ofpacts_len = d->ofpacts_len,
+                .command = OFPFC_ADD,
+            };
+            add_flow_mod(&fm, &msgs);
+            ovn_flow_log(d, "adding installed");
+
+            /* Copy 'd' from 'flow_table' to installed_flows. */
+            struct ovn_flow *new_node = ofctrl_dup_flow(d);
+            hmap_insert(&installed_flows, &new_node->match_hmap_node,
+                        new_node->match_hmap_node.hash);
+        }
     }
 
     /* Iterate through the installed groups from previous runs. If they
@@ -1012,7 +1093,7 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
     }
 
     /* Move the contents of groups->desired to groups->existing. */
-    ovn_extend_table_move(groups);
+    ovn_extend_table_sync(groups);
 
     /* Iterate through the installed meters from previous runs. If they
      * are not needed delete them. */
@@ -1039,7 +1120,7 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
     }
 
     /* Move the contents of meters->desired to meters->existing. */
-    ovn_extend_table_move(meters);
+    ovn_extend_table_sync(meters);
 
     if (!ovs_list_is_empty(&msgs)) {
         /* Add a barrier to the list of messages. */
diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
index e25f7d1..3ba5e61 100644
--- a/ovn/controller/ofctrl.h
+++ b/ovn/controller/ofctrl.h
@@ -37,7 +37,7 @@ void ofctrl_run(const struct ovsrec_bridge *br_int,
                 struct shash *pending_ct_zones);
 enum mf_field_id ofctrl_get_mf_field_id(void);
 bool ofctrl_can_put(void);
-void ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
+void ofctrl_put(struct shash *pending_ct_zones,
                 int64_t nb_cfg);
 void ofctrl_wait(void);
 void ofctrl_destroy(void);
@@ -51,8 +51,13 @@ char *ofctrl_inject_pkt(const struct ovsrec_bridge *br_int,
                         const char *flow_s, const struct shash *addr_sets);
 
 /* Flow table interfaces to the rest of ovn-controller. */
-void ofctrl_add_flow(struct hmap *desired_flows, uint8_t table_id,
+void ofctrl_add_flow(uint8_t table_id,
                      uint16_t priority, uint64_t cookie,
-                     const struct match *, const struct ofpbuf *ofpacts);
+                     const struct match *, const struct ofpbuf *ofpacts,
+                     const struct uuid *lflow_uuid);
+
+void ofctrl_remove_flows(const struct uuid *lflow_uuid);
+
+void ofctrl_flow_table_clear(void);
 
 #endif /* ovn/ofctrl.h */
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 38432ac..8a7d2c6 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -659,7 +659,6 @@ runtime_data_run(struct engine_node *node)
 }
 
 struct ed_type_flow_output {
-    struct hmap *flow_table;
     struct ovn_extend_table *group_table;
     struct ovn_extend_table *meter_table;
 };
@@ -688,26 +687,23 @@ flow_output_run(struct engine_node *node)
 
     ovs_assert(br_int && chassis);
 
-    struct hmap *flow_table =
-        ((struct ed_type_flow_output *)node->data)->flow_table;
-    struct ovn_extend_table *group_table =
-        ((struct ed_type_flow_output *)node->data)->group_table;
-    struct ovn_extend_table *meter_table =
-        ((struct ed_type_flow_output *)node->data)->meter_table;
-
     if (ctx->ovs_idl_txn) {
         static bool first_run = true;
         if (first_run) {
             first_run = false;
         } else {
-            hmap_clear(flow_table);
+            ofctrl_flow_table_clear();
         }
+        struct ovn_extend_table *group_table =
+            ((struct ed_type_flow_output *)node->data)->group_table;
+        struct ovn_extend_table *meter_table =
+            ((struct ed_type_flow_output *)node->data)->meter_table;
         /* requires ctx->ovs_idl_txn */
         commit_ct_zones(br_int, ctx->pending_ct_zones);
 
         lflow_run(ctx, chassis,
                   chassis_index, local_datapaths, group_table,
-                  meter_table, addr_sets, flow_table, active_tunnels,
+                  meter_table, addr_sets, active_tunnels,
                   local_lport_ids);
 
         bfd_run(ctx, br_int, chassis, local_datapaths,
@@ -716,12 +712,49 @@ flow_output_run(struct engine_node *node)
 
         physical_run(ctx, mff_ovn_geneve,
                      br_int, chassis, ctx->ct_zones,
-                     flow_table, local_datapaths, local_lports,
+                     local_datapaths, local_lports,
                      chassis_index, active_tunnels);
     }
     node->changed = true;
 }
 
+static bool
+flow_output_sb_logical_flow_handler(struct engine_node *node)
+{
+    struct controller_ctx *ctx = (struct controller_ctx *)node->context;
+    struct ed_type_runtime_data *data =
+        (struct ed_type_runtime_data *)engine_get_input(
+                "runtime_data", node)->data;
+    struct hmap *local_datapaths = data->local_datapaths;
+    struct sset *local_lport_ids = data->local_lport_ids;
+    struct sset *active_tunnels = data->active_tunnels;
+    struct chassis_index *chassis_index = data->chassis_index;
+    struct shash *addr_sets = data->addr_sets;
+    const struct ovsrec_bridge *br_int = get_br_int(ctx);
+
+    const char *chassis_id = get_chassis_id(ctx->ovs_idl);
+
+
+    const struct sbrec_chassis *chassis = NULL;
+    if (chassis_id) {
+        chassis = get_chassis(ctx->ovnsb_idl, chassis_id);
+    }
+
+    ovs_assert(br_int && chassis);
+
+    // TODO: move group_table from node data to ofctrl module data
+    struct ovn_extend_table *group_table =
+        ((struct ed_type_flow_output *)node->data)->group_table;
+    struct ovn_extend_table *meter_table =
+        ((struct ed_type_flow_output *)node->data)->meter_table;
+    lflow_handle_changed_flows(ctx, chassis,
+              chassis_index, local_datapaths, group_table, meter_table,
+              addr_sets, active_tunnels, local_lport_ids);
+
+    node->changed = true;
+    return true;
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -822,10 +855,7 @@ main(int argc, char *argv[])
         .addr_sets = &addr_sets
     };
 
-    struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
-
     struct ed_type_flow_output ed_flow_output = {
-        .flow_table = &flow_table,
         .group_table = &group_table,
         .meter_table = &meter_table
     };
@@ -858,7 +888,7 @@ main(int argc, char *argv[])
     engine_add_input(&en_flow_output, &en_sb_datapath_binding, NULL);
     engine_add_input(&en_flow_output, &en_sb_port_binding, NULL);
     engine_add_input(&en_flow_output, &en_sb_mac_binding, NULL);
-    engine_add_input(&en_flow_output, &en_sb_logical_flow, NULL);
+    engine_add_input(&en_flow_output, &en_sb_logical_flow, flow_output_sb_logical_flow_handler);
     engine_add_input(&en_flow_output, &en_sb_dhcp_options, NULL);
     engine_add_input(&en_flow_output, &en_sb_dhcpv6_options, NULL);
     engine_add_input(&en_flow_output, &en_sb_dns, NULL);
@@ -875,6 +905,8 @@ main(int argc, char *argv[])
 
     uint64_t engine_run_id = 0;
     uint64_t old_engine_run_id = 0;
+    bool ofctrl_put_skipped = false;
+
     /* Main loop. */
     exiting = false;
     while (!exiting) {
@@ -911,13 +943,16 @@ main(int argc, char *argv[])
             patch_run(&ctx, br_int, chassis);
             encaps_run(&ctx, br_int, chassis_id);
 
-            if (ofctrl_can_put()) {
-                engine_run(&en_flow_output, ++engine_run_id);
-                if (en_flow_output.changed) {
-                    ofctrl_put(&flow_table, &pending_ct_zones,
-                               get_nb_cfg(ctx.ovnsb_idl));
-                }
+            engine_run(&en_flow_output, ++engine_run_id);
+            if (en_flow_output.changed || ofctrl_put_skipped) {
+                VLOG_DBG("before ofctrl_put: skipped = %s",
+                         ofctrl_put_skipped ? "true" : "false");
+                ofctrl_put(&pending_ct_zones,
+                           get_nb_cfg(ctx.ovnsb_idl));
             }
+            ofctrl_put_skipped = !ofctrl_can_put();
+            VLOG_DBG("after ofctrl_put: skipped = %s",
+                     ofctrl_put_skipped ? "true" : "false");
 
             pinctrl_run(&ctx, br_int, chassis, &chassis_index,
                         &local_datapaths, &active_tunnels);
@@ -1012,8 +1047,6 @@ main(int argc, char *argv[])
     }
     hmap_destroy(&local_datapaths);
 
-    hmap_destroy(&flow_table);
-
     /* It's time to exit.  Clean up the databases. */
     bool done = false;
     while (!done) {
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index aa6f973..41ea9fe 100644
--- a/ovn/controller/physical.c
+++ b/ovn/controller/physical.c
@@ -44,6 +44,9 @@
 
 VLOG_DEFINE_THIS_MODULE(physical);
 
+/* UUID to identify OF flows not associated with ovsdb rows. */
+static struct uuid *hc_uuid = NULL;
+
 void
 physical_register_ovs_idl(struct ovsdb_idl *ovs_idl)
 {
@@ -190,7 +193,7 @@ get_zone_ids(const struct sbrec_port_binding *binding,
 static void
 put_local_common_flows(uint32_t dp_key, uint32_t port_key,
                        bool nested_container, const struct zone_ids *zone_ids,
-                       struct ofpbuf *ofpacts_p, struct hmap *flow_table)
+                       struct ofpbuf *ofpacts_p)
 {
     struct match match;
 
@@ -223,8 +226,8 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
 
     /* Resubmit to table 34. */
     put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
-    ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
-                    &match, ofpacts_p);
+    ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, 0,
+                    &match, ofpacts_p, hc_uuid);
 
     /* Table 34, Priority 100.
      * =======================
@@ -238,8 +241,8 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
                          0, MLF_ALLOW_LOOPBACK);
     match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key);
     match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
-    ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 100, 0,
-                    &match, ofpacts_p);
+    ofctrl_add_flow(OFTABLE_CHECK_LOOPBACK, 100, 0,
+                    &match, ofpacts_p, hc_uuid);
 
     /* Table 64, Priority 100.
      * =======================
@@ -262,8 +265,8 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
     put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
     put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
     put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
-    ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
-                    &match, ofpacts_p);
+    ofctrl_add_flow(OFTABLE_SAVE_INPORT, 100, 0,
+                    &match, ofpacts_p, hc_uuid);
 }
 
 static void
@@ -299,8 +302,7 @@ consider_port_binding(struct controller_ctx *ctx,
                       struct hmap *local_datapaths,
                       const struct sbrec_port_binding *binding,
                       const struct sbrec_chassis *chassis,
-                      struct ofpbuf *ofpacts_p,
-                      struct hmap *flow_table)
+                      struct ofpbuf *ofpacts_p)
 {
     uint32_t dp_key = binding->datapath->tunnel_key;
     uint32_t port_key = binding->tunnel_key;
@@ -329,7 +331,7 @@ consider_port_binding(struct controller_ctx *ctx,
 
         struct zone_ids binding_zones = get_zone_ids(binding, ct_zones);
         put_local_common_flows(dp_key, port_key, false, &binding_zones,
-                               ofpacts_p, flow_table);
+                               ofpacts_p);
 
         match_init_catchall(&match);
         ofpbuf_clear(ofpacts_p);
@@ -355,8 +357,8 @@ consider_port_binding(struct controller_ctx *ctx,
         ofpacts_p->header = clone;
         ofpact_finish_CLONE(ofpacts_p, &clone);
 
-        ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
-                        &match, ofpacts_p);
+        ofctrl_add_flow(OFTABLE_LOG_TO_PHY, 100, 0,
+                        &match, ofpacts_p, hc_uuid);
         return;
     }
 
@@ -423,8 +425,8 @@ consider_port_binding(struct controller_ctx *ctx,
             put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
         }
 
-        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
-                        &match, ofpacts_p);
+        ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, 0,
+                        &match, ofpacts_p, hc_uuid);
 
         goto out;
     }
@@ -512,7 +514,7 @@ consider_port_binding(struct controller_ctx *ctx,
 
         struct zone_ids zone_ids = get_zone_ids(binding, ct_zones);
         put_local_common_flows(dp_key, port_key, nested_container, &zone_ids,
-                               ofpacts_p, flow_table);
+                               ofpacts_p);
 
         /* Table 0, Priority 150 and 100.
          * ==============================
@@ -555,8 +557,8 @@ consider_port_binding(struct controller_ctx *ctx,
 
         /* Resubmit to first logical ingress pipeline table. */
         put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
-        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
-                        tag ? 150 : 100, 0, &match, ofpacts_p);
+        ofctrl_add_flow(OFTABLE_PHY_TO_LOG,
+                        tag ? 150 : 100, 0, &match, ofpacts_p, hc_uuid);
 
         if (!tag && (!strcmp(binding->type, "localnet")
                      || !strcmp(binding->type, "l2gateway"))) {
@@ -566,7 +568,7 @@ consider_port_binding(struct controller_ctx *ctx,
              * action. */
             ofpbuf_pull(ofpacts_p, ofpacts_orig_size);
             match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
-            ofctrl_add_flow(flow_table, 0, 100, 0, &match, ofpacts_p);
+            ofctrl_add_flow(0, 100, 0, &match, ofpacts_p, hc_uuid);
         }
 
         /* Table 65, Priority 100.
@@ -593,8 +595,8 @@ consider_port_binding(struct controller_ctx *ctx,
              * switch will also contain the tag. */
             ofpact_put_STRIP_VLAN(ofpacts_p);
         }
-        ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
-                        &match, ofpacts_p);
+        ofctrl_add_flow(OFTABLE_LOG_TO_PHY, 100, 0,
+                        &match, ofpacts_p, hc_uuid);
     } else if (!tun && !is_ha_remote) {
         /* Remote port connected by localnet port */
         /* Table 33, priority 100.
@@ -616,8 +618,8 @@ consider_port_binding(struct controller_ctx *ctx,
 
         /* Resubmit to table 33. */
         put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
-        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
-                        &match, ofpacts_p);
+        ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, 0,
+                        &match, ofpacts_p, hc_uuid);
     } else {
         /* Remote port connected by tunnel */
 
@@ -707,8 +709,8 @@ consider_port_binding(struct controller_ctx *ctx,
             bundle->fields = NX_HASH_FIELDS_ETH_SRC;
             ofpact_finish_BUNDLE(ofpacts_p, &bundle);
         }
-        ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
-                        &match, ofpacts_p);
+        ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100, 0,
+                        &match, ofpacts_p, hc_uuid);
     }
 out:
     if (gateway_chassis) {
@@ -723,8 +725,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
                   const struct sbrec_chassis *chassis,
                   const struct sbrec_multicast_group *mc,
                   struct ofpbuf *ofpacts_p,
-                  struct ofpbuf *remote_ofpacts_p,
-                  struct hmap *flow_table)
+                  struct ofpbuf *remote_ofpacts_p)
 {
     uint32_t dp_key = mc->datapath->tunnel_key;
     if (!get_local_datapath(local_datapaths, dp_key)) {
@@ -799,8 +800,8 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
          * group as the logical output port. */
         put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
 
-        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
-                        &match, ofpacts_p);
+        ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, 0,
+                        &match, ofpacts_p, hc_uuid);
     }
 
     /* Table 32, priority 100.
@@ -837,8 +838,8 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
             if (local_ports) {
                 put_resubmit(OFTABLE_LOCAL_OUTPUT, remote_ofpacts_p);
             }
-            ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
-                            &match, remote_ofpacts_p);
+            ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100, 0,
+                            &match, remote_ofpacts_p, hc_uuid);
         }
     }
     sset_destroy(&remote_chassis);
@@ -860,12 +861,17 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
              const struct ovsrec_bridge *br_int,
              const struct sbrec_chassis *chassis,
              const struct simap *ct_zones,
-             struct hmap *flow_table, struct hmap *local_datapaths,
+             struct hmap *local_datapaths,
              const struct sset *local_lports,
              struct chassis_index *chassis_index,
              struct sset *active_tunnels)
 {
 
+    if (!hc_uuid) {
+        hc_uuid = xmalloc(sizeof(struct uuid));
+        uuid_generate(hc_uuid);
+    }
+
     /* This bool tracks physical mapping changes. */
     bool physical_map_changed = false;
 
@@ -987,7 +993,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
         consider_port_binding(ctx, mff_ovn_geneve, ct_zones,
                               chassis_index, active_tunnels,
                               local_datapaths, binding, chassis,
-                              &ofpacts, flow_table);
+                              &ofpacts);
     }
 
     /* Handle output to multicast groups, in tables 32 and 33. */
@@ -1007,11 +1013,11 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
                              MLF_LOCAL_ONLY, MLF_LOCAL_ONLY);
         ofpbuf_clear(&ofpacts);
         put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
-        ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0, &match,
-                        &ofpacts);
+        ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 150, 0, &match,
+                        &ofpacts, hc_uuid);
 
         consider_mc_group(mff_ovn_geneve, ct_zones, local_datapaths, chassis,
-                          mc, &ofpacts, &remote_ofpacts, flow_table);
+                          mc, &ofpacts, &remote_ofpacts);
     }
 
     ofpbuf_uninit(&remote_ofpacts);
@@ -1051,8 +1057,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
 
         put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
 
-        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
-                        &ofpacts);
+        ofctrl_add_flow(OFTABLE_PHY_TO_LOG, 100, 0, &match,
+                        &ofpacts, hc_uuid);
     }
 
     /* Add flows for VXLAN encapsulations.  Due to the limited amount of
@@ -1084,8 +1090,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
             put_load(1, MFF_LOG_FLAGS, MLF_RCV_FROM_VXLAN_BIT, 1, &ofpacts);
             put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
 
-            ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
-                            &ofpacts);
+            ofctrl_add_flow(OFTABLE_PHY_TO_LOG, 100, 0, &match,
+                            &ofpacts, hc_uuid);
         }
     }
 
@@ -1105,8 +1111,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
     /* Resubmit to table 33. */
     ofpbuf_clear(&ofpacts);
     put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
-    ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
-                    &match, &ofpacts);
+    ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 150, 0,
+                    &match, &ofpacts, hc_uuid);
 
     /* Table 32, priority 150.
      * =======================
@@ -1128,8 +1134,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
         if (pb && !strcmp(pb->type, "localport")) {
             match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, pb->tunnel_key);
             match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
-            ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
-                            &match, &ofpacts);
+            ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 150, 0,
+                            &match, &ofpacts, hc_uuid);
         }
     }
 
@@ -1141,7 +1147,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
     match_init_catchall(&match);
     ofpbuf_clear(&ofpacts);
     put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
-    ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, 0, &match, &ofpacts);
+    ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 0, 0, &match, &ofpacts, hc_uuid);
 
     /* Table 34, Priority 0.
      * =======================
@@ -1155,8 +1161,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
         put_load(0, MFF_REG0 + i, 0, 32, &ofpacts);
     }
     put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts);
-    ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, 0, &match,
-                    &ofpacts);
+    ofctrl_add_flow(OFTABLE_CHECK_LOOPBACK, 0, 0, &match,
+                    &ofpacts, hc_uuid);
 
     /* Table 64, Priority 0.
      * =======================
@@ -1166,7 +1172,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
     match_init_catchall(&match);
     ofpbuf_clear(&ofpacts);
     put_resubmit(OFTABLE_LOG_TO_PHY, &ofpacts);
-    ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, 0, &match, &ofpacts);
+    ofctrl_add_flow(OFTABLE_SAVE_INPORT, 0, 0, &match, &ofpacts, hc_uuid);
 
     ofpbuf_uninit(&ofpacts);
 
diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h
index f8dbb49..4aeec04 100644
--- a/ovn/controller/physical.h
+++ b/ovn/controller/physical.h
@@ -47,7 +47,7 @@ void physical_run(struct controller_ctx *, enum mf_field_id mff_ovn_geneve,
                   const struct ovsrec_bridge *br_int,
                   const struct sbrec_chassis *chassis,
                   const struct simap *ct_zones,
-                  struct hmap *flow_table, struct hmap *local_datapaths,
+                  struct hmap *local_datapaths,
                   const struct sset *local_lports,
                   struct chassis_index *chassis_index,
                   struct sset *active_tunnels);
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index fde3bff..fb13ed5 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -1059,7 +1059,8 @@ encode_CT_LB(const struct ovnact_ct_lb *cl,
                       recirc_table, zone_reg);
     }
 
-    table_id = ovn_extend_table_assign_id(ep->group_table, &ds);
+    table_id = ovn_extend_table_assign_id(ep->group_table, &ds,
+                                          ep->lflow_uuid);
     ds_destroy(&ds);
     if (table_id == EXT_TABLE_ID_INVALID) {
         return;
@@ -2158,7 +2159,8 @@ encode_SET_METER(const struct ovnact_set_meter *cl,
                       cl->rate);
     }
 
-    table_id = ovn_extend_table_assign_id(ep->meter_table, &ds);
+    table_id = ovn_extend_table_assign_id(ep->meter_table, &ds,
+                                          ep->lflow_uuid);
     if (table_id == EXT_TABLE_ID_INVALID) {
         ds_destroy(&ds);
         return;
diff --git a/ovn/lib/extend-table.c b/ovn/lib/extend-table.c
index e18713b..7e4f471 100644
--- a/ovn/lib/extend-table.c
+++ b/ovn/lib/extend-table.c
@@ -102,21 +102,30 @@ ovn_extend_table_remove(struct ovn_extend_table *table,
     free(existing);
 }
 
+static struct ovn_extend_table_info*
+ovn_extend_info_clone(struct ovn_extend_table_info *source)
+{
+    struct ovn_extend_table_info *clone = xmalloc(sizeof *clone);
+    ds_clone(&clone->info, &source->info);
+    clone->table_id = source->table_id;
+    clone->new_table_id = source->new_table_id;
+    clone->hmap_node.hash = source->hmap_node.hash;
+    clone->lflow_uuid = source->lflow_uuid;
+    return clone;
+}
+
 void
-ovn_extend_table_move(struct ovn_extend_table *table)
+ovn_extend_table_sync(struct ovn_extend_table *table)
 {
     struct ovn_extend_table_info *desired, *next;
 
-    /* Move the contents of desired to existing. */
+    /* Copy the contents of desired to existing. */
     HMAP_FOR_EACH_SAFE (desired, next, hmap_node, &table->desired) {
-        hmap_remove(&table->desired, &desired->hmap_node);
-
         if (!ovn_extend_table_lookup(&table->existing, desired)) {
-            hmap_insert(&table->existing, &desired->hmap_node,
-                        desired->hmap_node.hash);
-        } else {
-           ds_destroy(&desired->info);
-           free(desired);
+            struct ovn_extend_table_info *clone =
+                ovn_extend_info_clone(desired);
+            hmap_insert(&table->existing, &clone->hmap_node,
+                        clone->hmap_node.hash);
         }
     }
 }
@@ -124,7 +133,8 @@ ovn_extend_table_move(struct ovn_extend_table *table)
 /* Assign a new table ID for the table information from the bitmap.
  * If it already exists, return the old ID. */
 uint32_t
-ovn_extend_table_assign_id(struct ovn_extend_table *table, struct ds *ds)
+ovn_extend_table_assign_id(struct ovn_extend_table *table, struct ds *ds,
+                           struct uuid lflow_uuid)
 {
     uint32_t table_id = 0, hash;
     struct ovn_extend_table_info *table_info;
@@ -165,6 +175,7 @@ ovn_extend_table_assign_id(struct ovn_extend_table *table, struct ds *ds)
     table_info->table_id = table_id;
     table_info->hmap_node.hash = hash;
     table_info->new_table_id = new_table_id;
+    table_info->lflow_uuid = lflow_uuid;
 
     hmap_insert(&table->desired,
                 &table_info->hmap_node, table_info->hmap_node.hash);
diff --git a/ovn/lib/extend-table.h b/ovn/lib/extend-table.h
index d9ae549..6539127 100644
--- a/ovn/lib/extend-table.h
+++ b/ovn/lib/extend-table.h
@@ -23,6 +23,7 @@
 #include "openvswitch/dynamic-string.h"
 #include "openvswitch/hmap.h"
 #include "openvswitch/list.h"
+#include "openvswitch/uuid.h"
 
 /* Used to manage expansion tables associated with Flow table,
  * such as the Group Table or Meter Table. */
@@ -37,6 +38,7 @@ struct ovn_extend_table {
 struct ovn_extend_table_info {
     struct hmap_node hmap_node;
     struct ds info;     /* Details string for the table entity. */
+    struct uuid lflow_uuid;
     uint32_t table_id;
     bool new_table_id;  /* 'True' if 'table_id' was reserved from
                          * ovn_extend_table's 'table_ids' bitmap. */
@@ -54,11 +56,12 @@ void ovn_extend_table_clear(struct ovn_extend_table *, bool);
 void ovn_extend_table_remove(struct ovn_extend_table *,
                              struct ovn_extend_table_info *);
 
-/* Move the contents of desired to existing. */
-void ovn_extend_table_move(struct ovn_extend_table *);
+/* Copy the contents of desired to existing. */
+void ovn_extend_table_sync(struct ovn_extend_table *);
 
 uint32_t ovn_extend_table_assign_id(struct ovn_extend_table *,
-                                    struct ds *);
+                                    struct ds *,
+                                    struct uuid lflow_uuid);
 
 /* Iterates 'DESIRED' through all of the 'ovn_extend_table_info's in
  * 'TABLE'->desired that are not in 'TABLE'->existing.  (The loop body
-- 
2.1.0



More information about the dev mailing list