[ovs-dev] [PATCH v6 03/17] ovn-controller: Initial use of incremental engine - quiet mode.

Han Zhou zhouhan at gmail.com
Fri May 17 19:56:28 UTC 2019


From: Han Zhou <hzhou8 at ebay.com>

Incremental proccessing engine is used to compute flows. In this
patch we create below engine nodes:
    - Engine nodes for each OVSDB table in local OVS DB and SB DB.
    - runtime_data: compute and maintain intermediate result such
                    as local_datapath, etc.
    - mff_ovn_geneve: MFF_* field ID for our Geneve option, which
                      is provided by switch.
    - flow_output: compute and maintain computed flow table.

In this patch the ovn flow table is persistent across main loop
iterations, and a new index of SB uuid is maintained for the
desired flow table, which will be useful for next patches for
incremental processing.

This patch doesn't do any incremental processing yet, but it achieves
the "quiet mode": the flow computation won't be triggered by unrelated
events, such as pinctrl/ofctrl messages. The flow computation
(full compute) happens only when any of its related inputs are
changed.

Signed-off-by: Han Zhou <hzhou8 at ebay.com>
---
 include/ovn/actions.h           |   3 +
 ovn/controller/binding.c        |   1 +
 ovn/controller/lflow.c          |  94 +++--
 ovn/controller/lflow.h          |   7 +-
 ovn/controller/ofctrl.c         | 270 ++++++++----
 ovn/controller/ofctrl.h         |  41 +-
 ovn/controller/ovn-controller.c | 897 ++++++++++++++++++++++++++++++----------
 ovn/controller/physical.c       |  65 +--
 ovn/controller/physical.h       |   2 +-
 ovn/lib/actions.c               |   9 +-
 ovn/lib/extend-table.c          |  60 ++-
 ovn/lib/extend-table.h          |  16 +-
 12 files changed, 1080 insertions(+), 385 deletions(-)

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index e07ad9a..f42bbc2 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -561,6 +561,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/binding.c b/ovn/controller/binding.c
index 911c935..c8e737a 100644
--- a/ovn/controller/binding.c
+++ b/ovn/controller/binding.c
@@ -524,6 +524,7 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
         update_local_lport_ids(local_lport_ids, binding_rec);
     }
 
+    ovs_assert(ovnsb_idl_txn);
     if (ovnsb_idl_txn) {
         const char *vif_chassis = smap_get(&binding_rec->options,
                                            "requested-chassis");
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index 661407b..8712b6d 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -61,7 +61,7 @@ struct condition_aux {
     const struct sset *active_tunnels;
 };
 
-static void consider_logical_flow(
+static bool consider_logical_flow(
     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
     const struct sbrec_logical_flow *,
@@ -74,10 +74,10 @@ static void consider_logical_flow(
     const struct shash *port_groups,
     const struct sset *active_tunnels,
     const struct sset *local_lport_ids,
-    uint32_t *conj_id_ofs,
-    struct hmap *flow_table,
+    struct ovn_desired_flow_table *,
     struct ovn_extend_table *group_table,
-    struct ovn_extend_table *meter_table);
+    struct ovn_extend_table *meter_table,
+    uint32_t *conj_id_ofs);
 
 static bool
 lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
@@ -147,11 +147,11 @@ add_logical_flows(
     const struct shash *port_groups,
     const struct sset *active_tunnels,
     const struct sset *local_lport_ids,
-    struct hmap *flow_table,
+    struct ovn_desired_flow_table *flow_table,
     struct ovn_extend_table *group_table,
-    struct ovn_extend_table *meter_table)
+    struct ovn_extend_table *meter_table,
+    uint32_t *conj_id_ofs)
 {
-    uint32_t conj_id_ofs = 1;
     const struct sbrec_logical_flow *lflow;
 
     struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
@@ -174,13 +174,18 @@ add_logical_flows(
     nd_ra_opts_init(&nd_ra_opts);
 
     SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
-        consider_logical_flow(sbrec_multicast_group_by_name_datapath,
-                              sbrec_port_binding_by_name,
-                              lflow, local_datapaths,
-                              chassis, &dhcp_opts, &dhcpv6_opts, &nd_ra_opts,
-                              addr_sets, port_groups, active_tunnels,
-                              local_lport_ids, &conj_id_ofs,
-                              flow_table, group_table, meter_table);
+        if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
+                                   sbrec_port_binding_by_name,
+                                   lflow, local_datapaths,
+                                   chassis, &dhcp_opts, &dhcpv6_opts,
+                                   &nd_ra_opts, addr_sets, port_groups,
+                                   active_tunnels, local_lport_ids,
+                                   flow_table, group_table, meter_table,
+                                   conj_id_ofs)) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
+            VLOG_ERR_RL(&rl, "Conjunction id overflow when processing lflow "
+                        UUID_FMT, UUID_ARGS(&lflow->header_.uuid));
+        }
     }
 
     dhcp_opts_destroy(&dhcp_opts);
@@ -188,7 +193,18 @@ add_logical_flows(
     nd_ra_opts_destroy(&nd_ra_opts);
 }
 
-static void
+static bool
+update_conj_id_ofs(uint32_t *conj_id_ofs, uint32_t n_conjs)
+{
+    if (*conj_id_ofs + n_conjs < *conj_id_ofs) {
+        /* overflow */
+        return false;
+    }
+    *conj_id_ofs += n_conjs;
+    return true;
+}
+
+static bool
 consider_logical_flow(
     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
@@ -202,20 +218,24 @@ consider_logical_flow(
     const struct shash *port_groups,
     const struct sset *active_tunnels,
     const struct sset *local_lport_ids,
-    uint32_t *conj_id_ofs,
-    struct hmap *flow_table,
+    struct ovn_desired_flow_table *flow_table,
     struct ovn_extend_table *group_table,
-    struct ovn_extend_table *meter_table)
+    struct ovn_extend_table *meter_table,
+    uint32_t *conj_id_ofs)
 {
     /* Determine translation of logical table IDs to physical table IDs. */
     bool ingress = !strcmp(lflow->pipeline, "ingress");
 
     const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
     if (!ldp) {
-        return;
+        VLOG_DBG("lflow "UUID_FMT" has no datapath binding, skip",
+                 UUID_ARGS(&lflow->header_.uuid));
+        return true;
     }
     if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) {
-        return;
+        VLOG_DBG("lflow "UUID_FMT" is not for local datapath, skip",
+                 UUID_ARGS(&lflow->header_.uuid));
+        return true;
     }
 
     /* Determine translation of logical table IDs to physical table IDs. */
@@ -253,7 +273,7 @@ consider_logical_flow(
         free(error);
         ovnacts_free(ovnacts.data, ovnacts.size);
         ofpbuf_uninit(&ovnacts);
-        return;
+        return true;
     }
 
     /* Translate OVN match into table of OpenFlow matches. */
@@ -277,7 +297,7 @@ consider_logical_flow(
         free(error);
         ovnacts_free(ovnacts.data, ovnacts.size);
         ofpbuf_uninit(&ovnacts);
-        return;
+        return true;
     }
 
     struct lookup_port_aux aux = {
@@ -298,10 +318,12 @@ consider_logical_flow(
     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);
-        return;
+        return true;
     }
 
     /* Encode OVN logical actions into OpenFlow. */
@@ -313,6 +335,7 @@ consider_logical_flow(
         .is_switch = is_switch(ldp),
         .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,
@@ -341,13 +364,18 @@ consider_logical_flow(
                 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);
+                            lflow->header_.uuid.parts[0], &m->match, &ofpacts,
+                            &lflow->header_.uuid);
         } else {
             uint64_t conj_stubs[64 / 8];
             struct ofpbuf conj;
@@ -363,7 +391,7 @@ consider_logical_flow(
                 dst->n_clauses = src->n_clauses;
             }
             ofctrl_add_flow(flow_table, ptable, lflow->priority, 0, &m->match,
-                            &conj);
+                            &conj, &lflow->header_.uuid);
             ofpbuf_uninit(&conj);
         }
     }
@@ -371,7 +399,7 @@ consider_logical_flow(
     /* Clean up. */
     expr_matches_destroy(&matches);
     ofpbuf_uninit(&ofpacts);
-    *conj_id_ofs += n_conjs;
+    return update_conj_id_ofs(conj_id_ofs, n_conjs);
 }
 
 static void
@@ -389,7 +417,7 @@ put_load(const uint8_t *data, size_t len,
 static void
 consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                        const struct sbrec_mac_binding *b,
-                       struct hmap *flow_table)
+                       struct ovn_desired_flow_table *flow_table)
 {
     const struct sbrec_port_binding *pb
         = lport_lookup_by_name(sbrec_port_binding_by_name, b->logical_port);
@@ -431,7 +459,8 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
     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(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts,
+                    &b->header_.uuid);
     ofpbuf_uninit(&ofpacts);
 }
 
@@ -440,7 +469,7 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
 static void
 add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                    const struct sbrec_mac_binding_table *mac_binding_table,
-                   struct hmap *flow_table)
+                   struct ovn_desired_flow_table *flow_table)
 {
     const struct sbrec_mac_binding *b;
     SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
@@ -463,9 +492,10 @@ lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
           const struct shash *port_groups,
           const struct sset *active_tunnels,
           const struct sset *local_lport_ids,
-          struct hmap *flow_table,
+          struct ovn_desired_flow_table *flow_table,
           struct ovn_extend_table *group_table,
-          struct ovn_extend_table *meter_table)
+          struct ovn_extend_table *meter_table,
+          uint32_t *conj_id_ofs)
 {
     COVERAGE_INC(lflow_run);
 
@@ -474,7 +504,7 @@ lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
                       dhcpv6_options_table, logical_flow_table,
                       local_datapaths, chassis, addr_sets, port_groups,
                       active_tunnels, local_lport_ids, flow_table, group_table,
-                      meter_table);
+                      meter_table, conj_id_ofs);
     add_neighbor_flows(sbrec_port_binding_by_name, mac_binding_table,
                        flow_table);
 }
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
index 347da11..053c033 100644
--- a/ovn/controller/lflow.h
+++ b/ovn/controller/lflow.h
@@ -37,6 +37,7 @@
 
 struct ovn_extend_table;
 struct ovsdb_idl_index;
+struct ovn_desired_flow_table;
 struct hmap;
 struct sbrec_chassis;
 struct sbrec_dhcp_options_table;
@@ -77,9 +78,11 @@ void lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
                const struct shash *port_groups,
                const struct sset *active_tunnels,
                const struct sset *local_lport_ids,
-               struct hmap *flow_table,
+               struct ovn_desired_flow_table *,
                struct ovn_extend_table *group_table,
-               struct ovn_extend_table *meter_table);
+               struct ovn_extend_table *meter_table,
+               uint32_t *conj_id_ofs);
+
 void lflow_destroy(void);
 
 #endif /* ovn/lflow.h */
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index 95b95b6..47a915a 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,14 +63,16 @@ struct ovn_flow {
     struct minimatch 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);
+                                        const struct ovn_flow *target,
+                                        bool cmp_sb_uuid);
 static char *ovn_flow_to_string(const struct ovn_flow *);
 static void ovn_flow_log(const struct ovn_flow *, const char *action);
 static void ovn_flow_destroy(struct ovn_flow *);
@@ -146,6 +150,10 @@ static struct ovn_extend_table *meters;
  * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */
 static enum mf_field_id mff_ovn_geneve;
 
+/* Indicates if flows need to be reinstalled for scenarios when ovs
+ * is restarted, even if there is no change in the desired flow table. */
+static bool need_reinstall_flows;
+
 static ovs_be32 queue_msg(struct ofpbuf *);
 
 static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *);
@@ -154,8 +162,8 @@ 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_installed_flow_table_clear(void);
+static void ovn_installed_flow_table_destroy(void);
 
 static void ofctrl_recv(const struct ofp_header *, enum ofptype);
 
@@ -375,6 +383,7 @@ run_S_CLEAR_FLOWS(void)
 {
     VLOG_DBG("clearing all flows");
 
+    need_reinstall_flows = true;
     /* Send a flow_mod to delete all flows. */
     struct ofputil_flow_mod fm = {
         .table_id = OFPTT_ALL,
@@ -384,9 +393,6 @@ run_S_CLEAR_FLOWS(void)
     queue_msg(encode_flow_mod(&fm));
     minimatch_destroy(&fm.match);
 
-    /* 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);
@@ -397,6 +403,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);
@@ -477,11 +486,21 @@ recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type,
     }
 }
 
+
+enum mf_field_id
+ofctrl_get_mf_field_id(void)
+{
+    if (!rconn_is_connected(swconn)) {
+        return 0;
+    }
+    return (state == S_CLEAR_FLOWS || state == S_UPDATE_FLOWS
+            ? mff_ovn_geneve : 0);
+}
+
 /* Runs the OpenFlow state machine against 'br_int', which is local to the
  * hypervisor on which we are running.  Attempts to negotiate a Geneve option
- * field for class OVN_GENEVE_CLASS, type OVN_GENEVE_TYPE.  If successful,
- * returns the MFF_* field ID for the option, otherwise returns 0. */
-enum mf_field_id
+ * field for class OVN_GENEVE_CLASS, type OVN_GENEVE_TYPE. */
+void
 ofctrl_run(const struct ovsrec_bridge *br_int, struct shash *pending_ct_zones)
 {
     char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
@@ -494,7 +513,7 @@ ofctrl_run(const struct ovsrec_bridge *br_int, struct shash *pending_ct_zones)
     rconn_run(swconn);
 
     if (!rconn_is_connected(swconn)) {
-        return 0;
+        return;
     }
     if (seqno != rconn_get_connection_seqno(swconn)) {
         seqno = rconn_get_connection_seqno(swconn);
@@ -557,9 +576,6 @@ ofctrl_run(const struct ovsrec_bridge *br_int, struct shash *pending_ct_zones)
          * point, so ensure that we come back again without waiting. */
         poll_immediate_wake();
     }
-
-    return (state == S_CLEAR_FLOWS || state == S_UPDATE_FLOWS
-            ? mff_ovn_geneve : 0);
 }
 
 void
@@ -573,7 +589,7 @@ void
 ofctrl_destroy(void)
 {
     rconn_destroy(swconn);
-    ovn_flow_table_destroy(&installed_flows);
+    ovn_installed_flow_table_destroy();
     rconn_packet_counter_destroy(tx_counter);
     expr_symtab_destroy(&symtab);
     shash_destroy(&symtab);
@@ -625,15 +641,18 @@ 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_check_and_add_flow(struct hmap *desired_flows,
-                          uint8_t table_id, uint16_t priority, uint64_t cookie,
-                          const struct match *match,
+ofctrl_check_and_add_flow(struct ovn_desired_flow_table *flow_table,
+                          uint8_t table_id, uint16_t priority,
+                          uint64_t cookie, const struct match *match,
                           const struct ofpbuf *actions,
+                          const struct uuid *sb_uuid,
                           bool log_duplicate_flow)
 {
     struct ovn_flow *f = xmalloc(sizeof *f);
@@ -642,10 +661,14 @@ ofctrl_check_and_add_flow(struct hmap *desired_flows,
     minimatch_init(&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(&flow_table->match_flow_table, f, true)) {
         if (log_duplicate_flow) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
             if (!VLOG_DROP_DBG(&rl)) {
@@ -658,27 +681,53 @@ ofctrl_check_and_add_flow(struct hmap *desired_flows,
         return;
     }
 
-    hmap_insert(desired_flows, &f->hmap_node, f->hmap_node.hash);
+    hmap_insert(&flow_table->match_flow_table, &f->match_hmap_node,
+                f->match_hmap_node.hash);
+    hindex_insert(&flow_table->uuid_flow_table, &f->uuid_hindex_node,
+                  f->uuid_hindex_node.hash);
+}
+
+/* Removes a bundles of flows from the flow table. */
+void
+ofctrl_remove_flows(struct ovn_desired_flow_table *flow_table,
+                    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),
+                                    &flow_table->uuid_flow_table) {
+        if (uuid_equals(&f->sb_uuid, sb_uuid)) {
+            ovn_flow_log(f, "ofctrl_remove_flow");
+            hmap_remove(&flow_table->match_flow_table,
+                        &f->match_hmap_node);
+            hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node);
+            ovn_flow_destroy(f);
+        }
+    }
+
+    /* remove any related group and meter info */
+    ovn_extend_table_remove_desired(groups, sb_uuid);
+    ovn_extend_table_remove_desired(meters, sb_uuid);
 }
 
 void
-ofctrl_add_flow(struct hmap *desired_flows,
+ofctrl_add_flow(struct ovn_desired_flow_table *desired_flows,
                 uint8_t table_id, uint16_t priority, uint64_t cookie,
-                const struct match *match, const struct ofpbuf *actions)
+                const struct match *match, const struct ofpbuf *actions,
+                const struct uuid *sb_uuid)
 {
     ofctrl_check_and_add_flow(desired_flows, table_id, priority, cookie,
-                              match, actions, true);
+                              match, actions, sb_uuid, true);
 }
 
 /* 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,
                        minimatch_hash(&f->match, 0));
-
 }
 
 /* Duplicate an ovn_flow structure. */
@@ -691,23 +740,28 @@ ofctrl_dup_flow(struct ovn_flow *src)
     minimatch_clone(&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;
 }
 
 /* Finds and returns an ovn_flow in 'flow_table' whose key is identical to
  * 'target''s key, or NULL if there is none. */
 static struct ovn_flow *
-ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target)
+ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target,
+                bool cmp_sb_uuid)
 {
     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
             && minimatch_equal(&f->match, &target->match)) {
-            return f;
+            if (!cmp_sb_uuid || uuid_equals(&target->sb_uuid, &f->sb_uuid)) {
+                return f;
+            }
         }
     }
     return NULL;
@@ -717,6 +771,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);
     minimatch_format(&f->match, NULL, NULL, &s, OFP_DEFAULT_PRIORITY);
@@ -747,22 +802,48 @@ ovn_flow_destroy(struct ovn_flow *f)
 }
 
 /* Flow tables of struct ovn_flow. */
+void
+ovn_desired_flow_table_init(struct ovn_desired_flow_table *flow_table)
+{
+    hmap_init(&flow_table->match_flow_table);
+    hindex_init(&flow_table->uuid_flow_table);
+}
+
+void
+ovn_desired_flow_table_clear(struct ovn_desired_flow_table *flow_table)
+{
+    struct ovn_flow *f, *next;
+    HMAP_FOR_EACH_SAFE (f, next, match_hmap_node,
+                        &flow_table->match_flow_table) {
+        hmap_remove(&flow_table->match_flow_table, &f->match_hmap_node);
+        hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node);
+        ovn_flow_destroy(f);
+    }
+}
+
+void
+ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *flow_table)
+{
+    ovn_desired_flow_table_clear(flow_table);
+    hmap_destroy(&flow_table->match_flow_table);
+    hindex_destroy(&flow_table->uuid_flow_table);
+}
 
 static void
-ovn_flow_table_clear(struct hmap *flow_table)
+ovn_installed_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, &installed_flows) {
+        hmap_remove(&installed_flows, &f->match_hmap_node);
         ovn_flow_destroy(f);
     }
 }
 
 static void
-ovn_flow_table_destroy(struct hmap *flow_table)
+ovn_installed_flow_table_destroy(void)
 {
-    ovn_flow_table_clear(flow_table);
-    hmap_destroy(flow_table);
+    ovn_installed_flow_table_clear();
+    hmap_destroy(&installed_flows);
 }
 
 /* Flow table update. */
@@ -902,7 +983,7 @@ add_meter(struct ovn_extend_table_info *m_desired,
  * in the correct state and not backlogged with existing flow_mods.  (Our
  * criteria for being backlogged appear very conservative, but the socket
  * between ovn-controller and OVS provides some buffering.) */
-bool
+static bool
 ofctrl_can_put(void)
 {
     if (state != S_UPDATE_FLOWS
@@ -917,9 +998,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 '->desired'.  Regardless of whether the table is
- * updated, this deletes all the groups or meters from the '->desired'
- * and frees them. (The hmap itself isn't destroyed.)
+ * by the 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
@@ -927,16 +1006,43 @@ 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,
-           const struct sbrec_meter_table *meter_table, int64_t nb_cfg)
+ofctrl_put(struct ovn_desired_flow_table *flow_table,
+           struct shash *pending_ct_zones,
+           const struct sbrec_meter_table *meter_table,
+           int64_t nb_cfg,
+           bool flow_changed)
 {
+    static bool skipped_last_time = false;
+    static int64_t old_nb_cfg = 0;
+    bool need_put = false;
+    if (flow_changed || skipped_last_time || need_reinstall_flows) {
+        need_put = true;
+    } else if (nb_cfg != old_nb_cfg) {
+        /* nb_cfg changed since last ofctrl_put() call */
+        if (cur_cfg == old_nb_cfg) {
+            /* we were up-to-date already, so just update with the
+             * new nb_cfg */
+            cur_cfg = nb_cfg;
+        } else {
+            need_put = true;
+        }
+    }
+
+    old_nb_cfg = nb_cfg;
+
+    if (!need_put) {
+        VLOG_DBG("ofctrl_put not needed");
+        return;
+    }
     if (!ofctrl_can_put()) {
-        ovn_flow_table_clear(flow_table);
-        ovn_extend_table_clear(groups, false);
-        ovn_extend_table_clear(meters, false);
+        VLOG_DBG("ofctrl_put can't be performed");
+        skipped_last_time = true;
         return;
     }
 
+    skipped_last_time = false;
+    need_reinstall_flows = false;
+
     /* OpenFlow messages to send to the switch to bring it up-to-date. */
     struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs);
 
@@ -991,8 +1097,9 @@ 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(&flow_table->match_flow_table,
+                                             i, false);
         if (!d) {
             /* Installed flow is no longer desirable.  Delete it from the
              * switch and from installed_flows. */
@@ -1005,9 +1112,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. */
@@ -1024,38 +1135,37 @@ 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");
+    HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) {
+        i = ovn_flow_lookup(&installed_flows, d, false);
+        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,
+                .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);
+            /* 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
@@ -1080,11 +1190,11 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
         }
         free(group_string);
         ofputil_uninit_group_mod(&gm);
-        ovn_extend_table_remove(groups, installed);
+        ovn_extend_table_remove_existing(groups, installed);
     }
 
-    /* Move the contents of groups->desired to groups->existing. */
-    ovn_extend_table_move(groups);
+    /* Sync the contents of groups->desired to groups->existing. */
+    ovn_extend_table_sync(groups);
 
     /* Iterate through the installed meters from previous runs. If they
      * are not needed delete them. */
@@ -1097,11 +1207,11 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
         };
         add_meter_mod(&mm, &msgs);
 
-        ovn_extend_table_remove(meters, m_installed);
+        ovn_extend_table_remove_existing(meters, m_installed);
     }
 
-    /* Move the contents of meters->desired to meters->existing. */
-    ovn_extend_table_move(meters);
+    /* Sync the contents of meters->desired to meters->existing. */
+    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 f752180..b39cdf8 100644
--- a/ovn/controller/ofctrl.h
+++ b/ovn/controller/ofctrl.h
@@ -21,6 +21,7 @@
 
 #include "openvswitch/meta-flow.h"
 #include "ovsdb-idl.h"
+#include "hindex.h"
 
 struct ovn_extend_table;
 struct hmap;
@@ -30,14 +31,25 @@ struct ovsrec_bridge;
 struct sbrec_meter_table;
 struct shash;
 
+struct ovn_desired_flow_table {
+    /* Hash map flow table using flow match conditions as hash key.*/
+    struct hmap match_flow_table;
+
+    /* SB uuid index for the nodes in match_flow_table.*/
+    struct hindex uuid_flow_table;
+};
+
 /* Interface for OVN main loop. */
 void ofctrl_init(struct ovn_extend_table *group_table,
                  struct ovn_extend_table *meter_table);
-enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int,
-                            struct shash *pending_ct_zones);
-bool ofctrl_can_put(void);
-void ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
-                const struct sbrec_meter_table *meter_table, int64_t nb_cfg);
+void ofctrl_run(const struct ovsrec_bridge *br_int,
+                struct shash *pending_ct_zones);
+enum mf_field_id ofctrl_get_mf_field_id(void);
+void ofctrl_put(struct ovn_desired_flow_table *,
+                struct shash *pending_ct_zones,
+                const struct sbrec_meter_table *,
+                int64_t nb_cfg,
+                bool flow_changed);
 void ofctrl_wait(void);
 void ofctrl_destroy(void);
 int64_t ofctrl_get_cur_cfg(void);
@@ -51,15 +63,22 @@ char *ofctrl_inject_pkt(const struct ovsrec_bridge *br_int,
                         const struct shash *port_groups);
 
 /* 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(struct ovn_desired_flow_table *, 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 *);
+
+void ofctrl_remove_flows(struct ovn_desired_flow_table *, const struct uuid *);
+
+void ovn_desired_flow_table_init(struct ovn_desired_flow_table *);
+void ovn_desired_flow_table_clear(struct ovn_desired_flow_table *);
+void ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *);
 
-void ofctrl_check_and_add_flow(struct hmap *desired_flows, uint8_t table_id,
-                               uint16_t priority, uint64_t cookie,
-                               const struct match *,
+void ofctrl_check_and_add_flow(struct ovn_desired_flow_table *,
+                               uint8_t table_id, uint16_t priority,
+                               uint64_t cookie, const struct match *,
                                const struct ofpbuf *ofpacts,
-                               bool log_duplicate_flow);
+                               const struct uuid *, bool log_duplicate_flow);
 
 bool ofctrl_is_connected(void);
 
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 1274001..711d96a 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -60,6 +60,7 @@
 #include "timeval.h"
 #include "timer.h"
 #include "stopwatch.h"
+#include "ovn/lib/inc-proc-eng.h"
 
 VLOG_DEFINE_THIS_MODULE(main);
 
@@ -196,15 +197,27 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
     ovsdb_idl_condition_destroy(&dns);
 }
 
+static const char *
+br_int_name(const struct ovsrec_open_vswitch *cfg)
+{
+    return smap_get_def(&cfg->external_ids, "ovn-bridge", DEFAULT_BRIDGE_NAME);
+}
+
 static const struct ovsrec_bridge *
 create_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
-              const struct ovsrec_open_vswitch *cfg,
-              const char *bridge_name)
+              const struct ovsrec_open_vswitch_table *ovs_table)
 {
     if (!ovs_idl_txn) {
         return NULL;
     }
 
+    const struct ovsrec_open_vswitch *cfg;
+    cfg = ovsrec_open_vswitch_table_first(ovs_table);
+    if (!cfg) {
+        return NULL;
+    }
+    const char *bridge_name = br_int_name(cfg);
+
     ovsdb_idl_txn_add_comment(ovs_idl_txn,
             "ovn-controller: creating integration bridge '%s'", bridge_name);
 
@@ -239,8 +252,7 @@ create_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
 }
 
 static const struct ovsrec_bridge *
-get_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
-           const struct ovsrec_bridge_table *bridge_table,
+get_br_int(const struct ovsrec_bridge_table *bridge_table,
            const struct ovsrec_open_vswitch_table *ovs_table)
 {
     const struct ovsrec_open_vswitch *cfg;
@@ -249,23 +261,32 @@ get_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
         return NULL;
     }
 
-    const char *br_int_name = smap_get_def(&cfg->external_ids, "ovn-bridge",
-                                           DEFAULT_BRIDGE_NAME);
+    return get_bridge(bridge_table, br_int_name(cfg));
+}
 
-    const struct ovsrec_bridge *br = get_bridge(bridge_table, br_int_name);
-    if (!br) {
-        br = create_br_int(ovs_idl_txn, cfg, br_int_name);
+static const struct ovsrec_bridge *
+process_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
+               const struct ovsrec_bridge_table *bridge_table,
+               const struct ovsrec_open_vswitch_table *ovs_table)
+{
+    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
+                                                    ovs_table);
+    if (!br_int) {
+        br_int = create_br_int(ovs_idl_txn, ovs_table);
     }
-    if (br && ovs_idl_txn) {
+    if (br_int && ovs_idl_txn) {
+        const struct ovsrec_open_vswitch *cfg;
+        cfg = ovsrec_open_vswitch_table_first(ovs_table);
+        ovs_assert(cfg);
         const char *datapath_type = smap_get(&cfg->external_ids,
                                              "ovn-bridge-datapath-type");
         /* Check for the datapath_type and set it only if it is defined in
          * cfg. */
-        if (datapath_type && strcmp(br->datapath_type, datapath_type)) {
-            ovsrec_bridge_set_datapath_type(br, datapath_type);
+        if (datapath_type && strcmp(br_int->datapath_type, datapath_type)) {
+            ovsrec_bridge_set_datapath_type(br_int, datapath_type);
         }
     }
-    return br;
+    return br_int;
 }
 
 static const char *
@@ -273,7 +294,8 @@ get_chassis_id(const struct ovsrec_open_vswitch_table *ovs_table)
 {
     const struct ovsrec_open_vswitch *cfg
         = ovsrec_open_vswitch_table_first(ovs_table);
-    const char *chassis_id = cfg ? smap_get(&cfg->external_ids, "system-id") : NULL;
+    const char *chassis_id = cfg ? smap_get(&cfg->external_ids, "system-id")
+                                 : NULL;
 
     if (!chassis_id) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
@@ -476,11 +498,8 @@ restore_ct_zones(const struct ovsrec_bridge_table *bridge_table,
         return;
     }
 
-    const char *br_int_name = smap_get_def(&cfg->external_ids, "ovn-bridge",
-                                           DEFAULT_BRIDGE_NAME);
-
     const struct ovsrec_bridge *br_int;
-    br_int = get_bridge(bridge_table, br_int_name);
+    br_int = get_bridge(bridge_table, br_int_name(cfg));
     if (!br_int) {
         /* If the integration bridge hasn't been defined, assume that
          * any existing ct-zone definitions aren't valid. */
@@ -557,6 +576,452 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
     physical_register_ovs_idl(ovs_idl);
 }
 
+#define SB_NODES \
+    SB_NODE(chassis, "chassis") \
+    SB_NODE(encap, "encap") \
+    SB_NODE(address_set, "address_set") \
+    SB_NODE(port_group, "port_group") \
+    SB_NODE(multicast_group, "multicast_group") \
+    SB_NODE(datapath_binding, "datapath_binding") \
+    SB_NODE(port_binding, "port_binding") \
+    SB_NODE(mac_binding, "mac_binding") \
+    SB_NODE(logical_flow, "logical_flow") \
+    SB_NODE(dhcp_options, "dhcp_options") \
+    SB_NODE(dhcpv6_options, "dhcpv6_options") \
+    SB_NODE(dns, "dns")
+
+enum sb_engine_node {
+#define SB_NODE(NAME, NAME_STR) SB_##NAME,
+    SB_NODES
+#undef SB_NODE
+};
+
+#define SB_NODE(NAME, NAME_STR) ENGINE_FUNC_SB(NAME);
+    SB_NODES
+#undef SB_NODE
+
+#define OVS_NODES \
+    OVS_NODE(open_vswitch, "open_vswitch") \
+    OVS_NODE(bridge, "bridge") \
+    OVS_NODE(port, "port") \
+    OVS_NODE(qos, "qos")
+
+enum ovs_engine_node {
+#define OVS_NODE(NAME, NAME_STR) OVS_##NAME,
+    OVS_NODES
+#undef OVS_NODE
+};
+
+#define OVS_NODE(NAME, NAME_STR) ENGINE_FUNC_OVS(NAME);
+    OVS_NODES
+#undef OVS_NODE
+
+struct ed_type_ofctrl_is_connected {
+    bool connected;
+};
+
+static void
+en_ofctrl_is_connected_init(struct engine_node *node)
+{
+    struct ed_type_ofctrl_is_connected *data =
+        (struct ed_type_ofctrl_is_connected *)node->data;
+    data->connected = false;
+}
+
+static void
+en_ofctrl_is_connected_cleanup(struct engine_node *node OVS_UNUSED)
+{
+}
+
+static void
+en_ofctrl_is_connected_run(struct engine_node *node)
+{
+    struct ed_type_ofctrl_is_connected *data =
+        (struct ed_type_ofctrl_is_connected *)node->data;
+    if (data->connected != ofctrl_is_connected()) {
+        data->connected = !data->connected;
+        node->changed = true;
+        return;
+    }
+    node->changed = false;
+}
+
+struct ed_type_runtime_data {
+    /* Contains "struct local_datapath" nodes. */
+    struct hmap local_datapaths;
+
+    /* Contains the name of each logical port resident on the local
+     * hypervisor.  These logical ports include the VIFs (and their child
+     * logical ports, if any) that belong to VMs running on the hypervisor,
+     * l2gateway ports for which options:l2gateway-chassis designates the
+     * local hypervisor, and localnet ports. */
+    struct sset local_lports;
+
+    /* Contains the same ports as local_lports, but in the format:
+     * <datapath-tunnel-key>_<port-tunnel-key> */
+    struct sset local_lport_ids;
+    struct sset active_tunnels;
+    struct shash addr_sets;
+    struct shash port_groups;
+
+    /* connection tracking zones. */
+    unsigned long ct_zone_bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)];
+    struct shash pending_ct_zones;
+    struct simap ct_zones;
+};
+
+static void
+en_runtime_data_init(struct engine_node *node)
+{
+    struct ed_type_runtime_data *data =
+        (struct ed_type_runtime_data *)node->data;
+    struct ovsrec_open_vswitch_table *ovs_table =
+        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
+            engine_get_input("OVS_open_vswitch", node));
+    struct ovsrec_bridge_table *bridge_table =
+        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
+            engine_get_input("OVS_bridge", node));
+    hmap_init(&data->local_datapaths);
+    sset_init(&data->local_lports);
+    sset_init(&data->local_lport_ids);
+    sset_init(&data->active_tunnels);
+    shash_init(&data->addr_sets);
+    shash_init(&data->port_groups);
+    shash_init(&data->pending_ct_zones);
+    simap_init(&data->ct_zones);
+
+    /* Initialize connection tracking zones. */
+    memset(data->ct_zone_bitmap, 0, sizeof data->ct_zone_bitmap);
+    bitmap_set1(data->ct_zone_bitmap, 0); /* Zone 0 is reserved. */
+    restore_ct_zones(bridge_table, ovs_table,
+                     &data->ct_zones, data->ct_zone_bitmap);
+}
+
+static void
+en_runtime_data_cleanup(struct engine_node *node)
+{
+    struct ed_type_runtime_data *data =
+        (struct ed_type_runtime_data *)node->data;
+
+    expr_const_sets_destroy(&data->addr_sets);
+    shash_destroy(&data->addr_sets);
+    expr_const_sets_destroy(&data->port_groups);
+    shash_destroy(&data->port_groups);
+
+    sset_destroy(&data->local_lports);
+    sset_destroy(&data->local_lport_ids);
+    sset_destroy(&data->active_tunnels);
+    struct local_datapath *cur_node, *next_node;
+    HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
+                        &data->local_datapaths) {
+        free(cur_node->peer_dps);
+        hmap_remove(&data->local_datapaths, &cur_node->hmap_node);
+        free(cur_node);
+    }
+    hmap_destroy(&data->local_datapaths);
+
+    simap_destroy(&data->ct_zones);
+    shash_destroy(&data->pending_ct_zones);
+}
+
+static void
+en_runtime_data_run(struct engine_node *node)
+{
+    struct ed_type_runtime_data *data =
+        (struct ed_type_runtime_data *)node->data;
+    struct hmap *local_datapaths = &data->local_datapaths;
+    struct sset *local_lports = &data->local_lports;
+    struct sset *local_lport_ids = &data->local_lport_ids;
+    struct sset *active_tunnels = &data->active_tunnels;
+    struct shash *addr_sets = &data->addr_sets;
+    struct shash *port_groups = &data->port_groups;
+    unsigned long *ct_zone_bitmap = data->ct_zone_bitmap;
+    struct shash *pending_ct_zones = &data->pending_ct_zones;
+    struct simap *ct_zones = &data->ct_zones;
+
+    static bool first_run = true;
+    if (first_run) {
+        /* don't cleanup since there is no data yet */
+        first_run = false;
+    } else {
+        struct local_datapath *cur_node, *next_node;
+        HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, local_datapaths) {
+            free(cur_node->peer_dps);
+            hmap_remove(local_datapaths, &cur_node->hmap_node);
+            free(cur_node);
+        }
+        hmap_clear(local_datapaths);
+        sset_destroy(local_lports);
+        sset_destroy(local_lport_ids);
+        sset_destroy(active_tunnels);
+        expr_const_sets_destroy(addr_sets);
+        expr_const_sets_destroy(port_groups);
+        sset_init(local_lports);
+        sset_init(local_lport_ids);
+        sset_init(active_tunnels);
+    }
+
+    struct ovsrec_open_vswitch_table *ovs_table =
+        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
+            engine_get_input("OVS_open_vswitch", node));
+    struct ovsrec_bridge_table *bridge_table =
+        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
+            engine_get_input("OVS_bridge", node));
+    const char *chassis_id = get_chassis_id(ovs_table);
+    const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
+
+    ovs_assert(br_int && chassis_id);
+
+    struct ovsdb_idl_index *sbrec_chassis_by_name =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_chassis", node),
+                "name");
+
+    const struct sbrec_chassis *chassis
+        = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
+    ovs_assert(chassis);
+
+    struct ed_type_ofctrl_is_connected *ed_ofctrl_is_connected =
+        (struct ed_type_ofctrl_is_connected *)engine_get_input(
+            "ofctrl_is_connected", node)->data;
+    if (ed_ofctrl_is_connected->connected) {
+        /* Calculate the active tunnels only if have an an active
+         * OpenFlow connection to br-int.
+         * If we don't have a connection to br-int, it could mean
+         * ovs-vswitchd is down for some reason and the BFD status
+         * in the Interface rows could be stale. So its better to
+         * consider 'active_tunnels' set to be empty if it's not
+         * connected. */
+        bfd_calculate_active_tunnels(br_int, active_tunnels);
+    }
+
+    struct ovsrec_port_table *port_table =
+        (struct ovsrec_port_table *)EN_OVSDB_GET(
+            engine_get_input("OVS_port", node));
+
+    struct ovsrec_qos_table *qos_table =
+        (struct ovsrec_qos_table *)EN_OVSDB_GET(
+            engine_get_input("OVS_qos", node));
+
+    struct sbrec_port_binding_table *pb_table =
+        (struct sbrec_port_binding_table *)EN_OVSDB_GET(
+            engine_get_input("SB_port_binding", node));
+
+    struct ovsdb_idl_index *sbrec_datapath_binding_by_key =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_datapath_binding", node),
+                "key");
+
+    struct ovsdb_idl_index *sbrec_port_binding_by_name =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_port_binding", node),
+                "name");
+
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_port_binding", node),
+                "datapath");
+
+    binding_run(engine_get_context()->ovnsb_idl_txn,
+                engine_get_context()->ovs_idl_txn,
+                sbrec_datapath_binding_by_key,
+                sbrec_port_binding_by_datapath,
+                sbrec_port_binding_by_name,
+                port_table, qos_table, pb_table,
+                br_int, chassis,
+                active_tunnels, local_datapaths,
+                local_lports, local_lport_ids);
+
+    struct sbrec_address_set_table *as_table =
+        (struct sbrec_address_set_table *)EN_OVSDB_GET(
+            engine_get_input("SB_address_set", node));
+    addr_sets_init(as_table, addr_sets);
+
+    struct sbrec_port_group_table *pg_table =
+        (struct sbrec_port_group_table *)EN_OVSDB_GET(
+            engine_get_input("SB_port_group", node));
+    port_groups_init(pg_table, port_groups);
+
+    update_ct_zones(local_lports, local_datapaths, ct_zones,
+                    ct_zone_bitmap, pending_ct_zones);
+
+    node->changed = true;
+}
+
+struct ed_type_mff_ovn_geneve {
+    enum mf_field_id mff_ovn_geneve;
+};
+
+static void
+en_mff_ovn_geneve_init(struct engine_node *node)
+{
+    struct ed_type_mff_ovn_geneve *data =
+        (struct ed_type_mff_ovn_geneve *)node->data;
+    data->mff_ovn_geneve = 0;
+}
+
+static void
+en_mff_ovn_geneve_cleanup(struct engine_node *node OVS_UNUSED)
+{
+}
+
+static void
+en_mff_ovn_geneve_run(struct engine_node *node)
+{
+    struct ed_type_mff_ovn_geneve *data =
+        (struct ed_type_mff_ovn_geneve *)node->data;
+    enum mf_field_id mff_ovn_geneve = ofctrl_get_mf_field_id();
+    if (data->mff_ovn_geneve != mff_ovn_geneve) {
+        data->mff_ovn_geneve = mff_ovn_geneve;
+        node->changed = true;
+        return;
+    }
+    node->changed = false;
+}
+
+struct ed_type_flow_output {
+    /* desired flows */
+    struct ovn_desired_flow_table flow_table;
+    /* group ids for load balancing */
+    struct ovn_extend_table group_table;
+    /* meter ids for QoS */
+    struct ovn_extend_table meter_table;
+    /* conjunction id offset */
+    uint32_t conj_id_ofs;
+};
+
+static void
+en_flow_output_init(struct engine_node *node)
+{
+    struct ed_type_flow_output *data =
+        (struct ed_type_flow_output *)node->data;
+    ovn_desired_flow_table_init(&data->flow_table);
+    ovn_extend_table_init(&data->group_table);
+    ovn_extend_table_init(&data->meter_table);
+    data->conj_id_ofs = 1;
+}
+
+static void
+en_flow_output_cleanup(struct engine_node *node)
+{
+    struct ed_type_flow_output *data =
+        (struct ed_type_flow_output *)node->data;
+    ovn_desired_flow_table_destroy(&data->flow_table);
+    ovn_extend_table_destroy(&data->group_table);
+    ovn_extend_table_destroy(&data->meter_table);
+}
+
+static void
+en_flow_output_run(struct engine_node *node)
+{
+    struct ed_type_runtime_data *rt_data =
+        (struct ed_type_runtime_data *)engine_get_input(
+            "runtime_data", node)->data;
+    struct hmap *local_datapaths = &rt_data->local_datapaths;
+    struct sset *local_lports = &rt_data->local_lports;
+    struct sset *local_lport_ids = &rt_data->local_lport_ids;
+    struct sset *active_tunnels = &rt_data->active_tunnels;
+    struct shash *addr_sets = &rt_data->addr_sets;
+    struct shash *port_groups = &rt_data->port_groups;
+    struct simap *ct_zones = &rt_data->ct_zones;
+
+    struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
+        (struct ed_type_mff_ovn_geneve *)engine_get_input(
+            "mff_ovn_geneve", node)->data;
+    enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
+
+    struct ovsrec_open_vswitch_table *ovs_table =
+        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
+            engine_get_input("OVS_open_vswitch", node));
+    struct ovsrec_bridge_table *bridge_table =
+        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
+            engine_get_input("OVS_bridge", node));
+    const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
+    const char *chassis_id = get_chassis_id(ovs_table);
+
+    struct ovsdb_idl_index *sbrec_chassis_by_name =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_chassis", node),
+                "name");
+    const struct sbrec_chassis *chassis = NULL;
+    if (chassis_id) {
+        chassis = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
+    }
+
+    ovs_assert(br_int && chassis);
+
+    struct ed_type_flow_output *fo =
+        (struct ed_type_flow_output *)node->data;
+    struct ovn_desired_flow_table *flow_table = &fo->flow_table;
+    struct ovn_extend_table *group_table = &fo->group_table;
+    struct ovn_extend_table *meter_table = &fo->meter_table;
+    uint32_t *conj_id_ofs = &fo->conj_id_ofs;
+
+    static bool first_run = true;
+    if (first_run) {
+        first_run = false;
+    } else {
+        ovn_desired_flow_table_clear(flow_table);
+        ovn_extend_table_clear(group_table, false /* desired */);
+        ovn_extend_table_clear(meter_table, false /* desired */);
+    }
+
+    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_multicast_group", node),
+                "name_datapath");
+
+    struct ovsdb_idl_index *sbrec_port_binding_by_name =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_port_binding", node),
+                "name");
+
+    struct sbrec_dhcp_options_table *dhcp_table =
+        (struct sbrec_dhcp_options_table *)EN_OVSDB_GET(
+            engine_get_input("SB_dhcp_options", node));
+
+    struct sbrec_dhcpv6_options_table *dhcpv6_table =
+        (struct sbrec_dhcpv6_options_table *)EN_OVSDB_GET(
+            engine_get_input("SB_dhcpv6_options", node));
+
+    struct sbrec_logical_flow_table *logical_flow_table =
+        (struct sbrec_logical_flow_table *)EN_OVSDB_GET(
+            engine_get_input("SB_logical_flow", node));
+
+    struct sbrec_mac_binding_table *mac_binding_table =
+        (struct sbrec_mac_binding_table *)EN_OVSDB_GET(
+            engine_get_input("SB_mac_binding", node));
+
+    *conj_id_ofs = 1;
+    lflow_run(sbrec_multicast_group_by_name_datapath,
+              sbrec_port_binding_by_name,
+              dhcp_table, dhcpv6_table,
+              logical_flow_table,
+              mac_binding_table,
+              chassis, local_datapaths, addr_sets,
+              port_groups, active_tunnels, local_lport_ids,
+              flow_table, group_table, meter_table, conj_id_ofs);
+
+    struct sbrec_multicast_group_table *multicast_group_table =
+        (struct sbrec_multicast_group_table *)EN_OVSDB_GET(
+            engine_get_input("SB_multicast_group", node));
+
+    struct sbrec_port_binding_table *port_binding_table =
+        (struct sbrec_port_binding_table *)EN_OVSDB_GET(
+            engine_get_input("SB_port_binding", node));
+
+    physical_run(sbrec_port_binding_by_name,
+                 multicast_group_table,
+                 port_binding_table,
+                 mff_ovn_geneve,
+                 br_int, chassis, ct_zones,
+                 local_datapaths, local_lports,
+                 active_tunnels,
+                 flow_table);
+
+    node->changed = true;
+}
+
 struct ovn_controller_exit_args {
     bool *exiting;
     bool *restart;
@@ -586,21 +1051,8 @@ main(int argc, char *argv[])
     unixctl_command_register("exit", "", 0, 1, ovn_controller_exit,
                              &exit_args);
 
-    /* Initialize group ids for loadbalancing. */
-    struct ovn_extend_table group_table;
-    ovn_extend_table_init(&group_table);
-    unixctl_command_register("group-table-list", "", 0, 0,
-                             group_table_list, &group_table);
-
-    /* Initialize meter ids for QoS. */
-    struct ovn_extend_table meter_table;
-    ovn_extend_table_init(&meter_table);
-    unixctl_command_register("meter-table-list", "", 0, 0,
-                             meter_table_list, &meter_table);
-
     daemonize_complete();
 
-    ofctrl_init(&group_table, &meter_table);
     pinctrl_init();
     lflow_init();
 
@@ -646,119 +1098,191 @@ main(int argc, char *argv[])
     ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
     update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL);
 
-    /* Initialize connection tracking zones. */
-    struct simap ct_zones = SIMAP_INITIALIZER(&ct_zones);
-    struct shash pending_ct_zones = SHASH_INITIALIZER(&pending_ct_zones);
-    unsigned long ct_zone_bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)];
-    memset(ct_zone_bitmap, 0, sizeof ct_zone_bitmap);
-    bitmap_set1(ct_zone_bitmap, 0); /* Zone 0 is reserved. */
-    restore_ct_zones(ovsrec_bridge_table_get(ovs_idl_loop.idl),
-                     ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
-                     &ct_zones, ct_zone_bitmap);
+    stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
+
+    /* Define inc-proc-engine nodes. */
+    struct ed_type_runtime_data ed_runtime_data;
+    struct ed_type_mff_ovn_geneve ed_mff_ovn_geneve;
+    struct ed_type_ofctrl_is_connected ed_ofctrl_is_connected;
+    struct ed_type_flow_output ed_flow_output;
+
+    ENGINE_NODE(runtime_data, "runtime_data");
+    ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve");
+    ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected");
+    ENGINE_NODE(flow_output, "flow_output");
+
+#define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
+    SB_NODES
+#undef SB_NODE
+
+#define OVS_NODE(NAME, NAME_STR) ENGINE_NODE_OVS(NAME, NAME_STR);
+    OVS_NODES
+#undef OVS_NODE
+
+    engine_ovsdb_node_add_index(&en_sb_chassis, "name", sbrec_chassis_by_name);
+    engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath",
+                                sbrec_multicast_group_by_name_datapath);
+    engine_ovsdb_node_add_index(&en_sb_port_binding, "name",
+                                sbrec_port_binding_by_name);
+    engine_ovsdb_node_add_index(&en_sb_port_binding, "key",
+                                sbrec_port_binding_by_key);
+    engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath",
+                                sbrec_port_binding_by_datapath);
+    engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key",
+                                sbrec_datapath_binding_by_key);
+
+    /* Add dependencies between inc-proc-engine nodes. */
+    engine_add_input(&en_flow_output, &en_runtime_data, NULL);
+    engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
+
+    engine_add_input(&en_flow_output, &en_ovs_open_vswitch, NULL);
+    engine_add_input(&en_flow_output, &en_ovs_bridge, NULL);
+
+    engine_add_input(&en_flow_output, &en_sb_chassis, NULL);
+    engine_add_input(&en_flow_output, &en_sb_encap, NULL);
+    engine_add_input(&en_flow_output, &en_sb_multicast_group, 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_dhcp_options, NULL);
+    engine_add_input(&en_flow_output, &en_sb_dhcpv6_options, NULL);
+    engine_add_input(&en_flow_output, &en_sb_dns, NULL);
+
+    engine_add_input(&en_runtime_data, &en_ofctrl_is_connected, NULL);
+
+    engine_add_input(&en_runtime_data, &en_ovs_open_vswitch, NULL);
+    engine_add_input(&en_runtime_data, &en_ovs_bridge, NULL);
+    engine_add_input(&en_runtime_data, &en_ovs_port, NULL);
+    engine_add_input(&en_runtime_data, &en_ovs_qos, NULL);
+
+    engine_add_input(&en_runtime_data, &en_sb_chassis, NULL);
+    engine_add_input(&en_runtime_data, &en_sb_address_set, NULL);
+    engine_add_input(&en_runtime_data, &en_sb_port_group, NULL);
+    engine_add_input(&en_runtime_data, &en_sb_datapath_binding, NULL);
+    engine_add_input(&en_runtime_data, &en_sb_port_binding, NULL);
+
+    engine_init(&en_flow_output);
+
+    ofctrl_init(&ed_flow_output.group_table,
+                &ed_flow_output.meter_table);
+
+    unixctl_command_register("group-table-list", "", 0, 0,
+                             group_table_list, &ed_flow_output.group_table);
+
+    unixctl_command_register("meter-table-list", "", 0, 0,
+                             meter_table_list, &ed_flow_output.meter_table);
+
     unixctl_command_register("ct-zone-list", "", 0, 0,
-                             ct_zone_list, &ct_zones);
+                             ct_zone_list, &ed_runtime_data.ct_zones);
 
     struct pending_pkt pending_pkt = { .conn = NULL };
     unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
                              &pending_pkt);
 
-    stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
+    uint64_t engine_run_id = 0;
+    uint64_t old_engine_run_id = 0;
+
+    unsigned int ovs_cond_seqno = UINT_MAX;
+    unsigned int ovnsb_cond_seqno = UINT_MAX;
+
     /* Main loop. */
     exiting = false;
     restart = false;
     while (!exiting) {
         update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl);
         update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
+        old_engine_run_id = engine_run_id;
 
         struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop);
+        unsigned int new_ovs_cond_seqno
+            = ovsdb_idl_get_condition_seqno(ovs_idl_loop.idl);
+        if (new_ovs_cond_seqno != ovs_cond_seqno) {
+            if (!new_ovs_cond_seqno) {
+                VLOG_INFO("OVS IDL reconnected, force recompute.");
+                engine_set_force_recompute(true);
+            }
+            ovs_cond_seqno = new_ovs_cond_seqno;
+        }
+
         struct ovsdb_idl_txn *ovnsb_idl_txn
             = ovsdb_idl_loop_run(&ovnsb_idl_loop);
+        unsigned int new_ovnsb_cond_seqno
+            = ovsdb_idl_get_condition_seqno(ovnsb_idl_loop.idl);
+        if (new_ovnsb_cond_seqno != ovnsb_cond_seqno) {
+            if (!new_ovnsb_cond_seqno) {
+                VLOG_INFO("OVNSB IDL reconnected, force recompute.");
+                engine_set_force_recompute(true);
+            }
+            ovnsb_cond_seqno = new_ovnsb_cond_seqno;
+        }
+
+        struct engine_context eng_ctx = {
+            .ovs_idl_txn = ovs_idl_txn,
+            .ovnsb_idl_txn = ovnsb_idl_txn
+        };
+
+        engine_set_context(&eng_ctx);
 
         if (ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl)) {
-            /* Contains "struct local_datapath" nodes. */
-            struct hmap local_datapaths = HMAP_INITIALIZER(&local_datapaths);
-
-            /* Contains the name of each logical port resident on the local
-             * hypervisor.  These logical ports include the VIFs (and their
-             * child logical ports, if any) that belong to VMs running on the
-             * hypervisor, l2gateway ports for which options:l2gateway-chassis
-             * designates the local hypervisor, and localnet ports. */
-            struct sset local_lports = SSET_INITIALIZER(&local_lports);
-            /* Contains the same ports as local_lports, but in the format:
-             * <datapath-tunnel-key>_<port-tunnel-key> */
-            struct sset local_lport_ids = SSET_INITIALIZER(&local_lport_ids);
-            struct sset active_tunnels = SSET_INITIALIZER(&active_tunnels);
             /* Contains the transport zones that this Chassis belongs to */
             struct sset transport_zones = SSET_INITIALIZER(&transport_zones);
             sset_from_delimited_string(&transport_zones,
                 get_transport_zones(ovsrec_open_vswitch_table_get(
                                     ovs_idl_loop.idl)), ",");
 
-            const struct ovsrec_bridge *br_int
-                = get_br_int(ovs_idl_txn,
-                             ovsrec_bridge_table_get(ovs_idl_loop.idl),
-                             ovsrec_open_vswitch_table_get(ovs_idl_loop.idl));
-            const char *chassis_id
-                = get_chassis_id(ovsrec_open_vswitch_table_get(
-                                     ovs_idl_loop.idl));
-
+            const struct ovsrec_bridge_table *bridge_table =
+                ovsrec_bridge_table_get(ovs_idl_loop.idl);
+            const struct ovsrec_open_vswitch_table *ovs_table =
+                ovsrec_open_vswitch_table_get(ovs_idl_loop.idl);
+            const struct ovsrec_bridge *br_int =
+                process_br_int(ovs_idl_txn, bridge_table, ovs_table);
+            const char *chassis_id = get_chassis_id(ovs_table);
             const struct sbrec_chassis *chassis = NULL;
             if (chassis_id) {
-                chassis = chassis_run(
-                    ovnsb_idl_txn, sbrec_chassis_by_name,
-                    ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
-                    chassis_id, br_int, &transport_zones);
-                encaps_run(
-                    ovs_idl_txn,
-                    ovsrec_bridge_table_get(ovs_idl_loop.idl), br_int,
-                    sbrec_chassis_table_get(ovnsb_idl_loop.idl), chassis_id,
-                    sbrec_sb_global_first(ovnsb_idl_loop.idl),
-                    &transport_zones);
-
-                if (ofctrl_is_connected()) {
-                    /* Calculate the active tunnels only if have an an active
-                     * OpenFlow connection to br-int.
-                     * If we don't have a connection to br-int, it could mean
-                     * ovs-vswitchd is down for some reason and the BFD status
-                     * in the Interface rows could be stale. So its better to
-                     * consider 'active_tunnels' set to be empty if it's not
-                     * connected. */
-                    bfd_calculate_active_tunnels(br_int, &active_tunnels);
-                }
-
-                binding_run(ovnsb_idl_txn, ovs_idl_txn,
-                            sbrec_datapath_binding_by_key,
-                            sbrec_port_binding_by_datapath,
-                            sbrec_port_binding_by_name,
-                            ovsrec_port_table_get(ovs_idl_loop.idl),
-                            ovsrec_qos_table_get(ovs_idl_loop.idl),
-                            sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
-                            br_int, chassis,
-                            &active_tunnels, &local_datapaths,
-                            &local_lports, &local_lport_ids);
+                chassis = chassis_run(ovnsb_idl_txn, sbrec_chassis_by_name,
+                                      ovs_table, chassis_id, br_int,
+                                      &transport_zones);
             }
 
             if (br_int) {
-                enum mf_field_id mff_ovn_geneve = ofctrl_run(
-                    br_int, &pending_ct_zones);
+                ofctrl_run(br_int, &ed_runtime_data.pending_ct_zones);
 
                 if (chassis) {
-                    struct shash addr_sets = SHASH_INITIALIZER(&addr_sets);
-                    addr_sets_init(
-                        sbrec_address_set_table_get(ovnsb_idl_loop.idl),
-                        &addr_sets);
-                    struct shash port_groups = SHASH_INITIALIZER(&port_groups);
-                    port_groups_init(
-                        sbrec_port_group_table_get(ovnsb_idl_loop.idl),
-                        &port_groups);
-
                     patch_run(ovs_idl_txn,
                               ovsrec_bridge_table_get(ovs_idl_loop.idl),
                               ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
                               ovsrec_port_table_get(ovs_idl_loop.idl),
                               sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
                               br_int, chassis);
-
+                    encaps_run(ovs_idl_txn,
+                               bridge_table, br_int,
+                               sbrec_chassis_table_get(ovnsb_idl_loop.idl),
+                               chassis_id,
+                               sbrec_sb_global_first(ovnsb_idl_loop.idl),
+                               &transport_zones);
+
+                    stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME,
+                                    time_msec());
+                    if (ovnsb_idl_txn) {
+                        engine_run(&en_flow_output, ++engine_run_id);
+                    }
+                    stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
+                                   time_msec());
+                    if (ovs_idl_txn) {
+                        commit_ct_zones(br_int,
+                                        &ed_runtime_data.pending_ct_zones);
+                        bfd_run(ovsrec_interface_table_get(ovs_idl_loop.idl),
+                                br_int, chassis,
+                                sbrec_ha_chassis_group_table_get(
+                                    ovnsb_idl_loop.idl),
+                                sbrec_sb_global_table_get(ovnsb_idl_loop.idl));
+                    }
+                    ofctrl_put(&ed_flow_output.flow_table,
+                               &ed_runtime_data.pending_ct_zones,
+                               sbrec_meter_table_get(ovnsb_idl_loop.idl),
+                               get_nb_cfg(sbrec_sb_global_table_get(
+                                              ovnsb_idl_loop.idl)),
+                               en_flow_output.changed);
                     pinctrl_run(ovnsb_idl_txn,
                                 sbrec_datapath_binding_by_key,
                                 sbrec_port_binding_by_datapath,
@@ -767,113 +1291,64 @@ main(int argc, char *argv[])
                                 sbrec_mac_binding_by_lport_ip,
                                 sbrec_dns_table_get(ovnsb_idl_loop.idl),
                                 br_int, chassis,
-                                &local_datapaths, &active_tunnels);
-                    update_ct_zones(&local_lports, &local_datapaths, &ct_zones,
-                                    ct_zone_bitmap, &pending_ct_zones);
-                    if (ovs_idl_txn && ofctrl_can_put()) {
-                        stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME,
-                                        time_msec());
-
-                        commit_ct_zones(br_int, &pending_ct_zones);
-
-                        struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
-                        lflow_run(
-                            sbrec_multicast_group_by_name_datapath,
-                            sbrec_port_binding_by_name,
-                            sbrec_dhcp_options_table_get(ovnsb_idl_loop.idl),
-                            sbrec_dhcpv6_options_table_get(ovnsb_idl_loop.idl),
-                            sbrec_logical_flow_table_get(ovnsb_idl_loop.idl),
-                            sbrec_mac_binding_table_get(ovnsb_idl_loop.idl),
-                            chassis,
-                            &local_datapaths, &addr_sets,
-                            &port_groups, &active_tunnels, &local_lport_ids,
-                            &flow_table, &group_table, &meter_table);
-
-                        if (chassis_id) {
-                            bfd_run(
-                                ovsrec_interface_table_get(ovs_idl_loop.idl),
-                                br_int, chassis,
-                                sbrec_ha_chassis_group_table_get(
-                                    ovnsb_idl_loop.idl),
-                                sbrec_sb_global_table_get(ovnsb_idl_loop.idl));
-                        }
-                        physical_run(
-                            sbrec_port_binding_by_name,
-                            sbrec_multicast_group_table_get(
-                                ovnsb_idl_loop.idl),
-                            sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
-                            mff_ovn_geneve,
-                            br_int, chassis, &ct_zones,
-                            &local_datapaths, &local_lports,
-                            &active_tunnels,
-                            &flow_table);
-
-                        stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
-                                       time_msec());
-
-                        ofctrl_put(&flow_table, &pending_ct_zones,
-                                   sbrec_meter_table_get(ovnsb_idl_loop.idl),
-                                   get_nb_cfg(sbrec_sb_global_table_get(
-                                                  ovnsb_idl_loop.idl)));
-
-                        hmap_destroy(&flow_table);
-                    }
-                    if (ovnsb_idl_txn) {
-                        int64_t cur_cfg = ofctrl_get_cur_cfg();
-                        if (cur_cfg && cur_cfg != chassis->nb_cfg) {
-                            sbrec_chassis_set_nb_cfg(chassis, cur_cfg);
-                        }
-                    }
+                                &ed_runtime_data.local_datapaths,
+                                &ed_runtime_data.active_tunnels);
 
-                    if (pending_pkt.conn) {
-                        char *error = ofctrl_inject_pkt(br_int,
-                                                        pending_pkt.flow_s,
-                                                        &addr_sets,
-                                                        &port_groups);
-                        if (error) {
-                            unixctl_command_reply_error(pending_pkt.conn,
-                                                        error);
-                            free(error);
-                        } else {
-                            unixctl_command_reply(pending_pkt.conn, NULL);
-                        }
-                        pending_pkt.conn = NULL;
-                        free(pending_pkt.flow_s);
+                    if (en_runtime_data.changed) {
+                        update_sb_monitors(ovnsb_idl_loop.idl, chassis,
+                                           &ed_runtime_data.local_lports,
+                                           &ed_runtime_data.local_datapaths);
                     }
+                }
 
-                    update_sb_monitors(ovnsb_idl_loop.idl, chassis,
-                                       &local_lports, &local_datapaths);
+            }
+            if (old_engine_run_id == engine_run_id) {
+                if (engine_need_run(&en_flow_output)) {
+                    VLOG_DBG("engine did not run, force recompute next time: "
+                             "br_int %p, chassis %p", br_int, chassis);
+                    engine_set_force_recompute(true);
+                    poll_immediate_wake();
+                } else {
+                    VLOG_DBG("engine did not run, and it was not needed"
+                             " either: br_int %p, chassis %p",
+                             br_int, chassis);
+                }
+            } else {
+                engine_set_force_recompute(false);
+            }
 
-                    expr_const_sets_destroy(&addr_sets);
-                    shash_destroy(&addr_sets);
-                    expr_const_sets_destroy(&port_groups);
-                    shash_destroy(&port_groups);
+            if (ovnsb_idl_txn && chassis) {
+                int64_t cur_cfg = ofctrl_get_cur_cfg();
+                if (cur_cfg && cur_cfg != chassis->nb_cfg) {
+                    sbrec_chassis_set_nb_cfg(chassis, cur_cfg);
                 }
             }
 
-            /* If we haven't handled the pending packet insertion
-             * request, the system is not ready. */
+
             if (pending_pkt.conn) {
-                unixctl_command_reply_error(pending_pkt.conn,
-                                            "ovn-controller not ready.");
+                if (br_int && chassis) {
+                    char *error = ofctrl_inject_pkt(
+                        br_int, pending_pkt.flow_s, &ed_runtime_data.addr_sets,
+                        &ed_runtime_data.port_groups);
+                    if (error) {
+                        unixctl_command_reply_error(pending_pkt.conn, error);
+                        free(error);
+                    } else {
+                        unixctl_command_reply(pending_pkt.conn, NULL);
+                    }
+                } else {
+                    VLOG_DBG("Pending_pkt conn but br_int %p or chassis %p not"
+                              " ready. run-id: %"PRIu64, br_int, chassis,
+                              engine_run_id);
+                    unixctl_command_reply_error(pending_pkt.conn,
+                                                "ovn-controller not ready.");
+                }
                 pending_pkt.conn = NULL;
                 free(pending_pkt.flow_s);
             }
 
-            sset_destroy(&local_lports);
-            sset_destroy(&local_lport_ids);
-            sset_destroy(&active_tunnels);
             sset_destroy(&transport_zones);
 
-            struct local_datapath *cur_node, *next_node;
-            HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
-                                &local_datapaths) {
-                free(cur_node->peer_dps);
-                hmap_remove(&local_datapaths, &cur_node->hmap_node);
-                free(cur_node);
-            }
-            hmap_destroy(&local_datapaths);
-
             if (br_int) {
                 ofctrl_wait();
                 pinctrl_wait(ovnsb_idl_txn);
@@ -887,14 +1362,18 @@ main(int argc, char *argv[])
             poll_immediate_wake();
         }
 
-        ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
+        if (!ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop)) {
+            VLOG_INFO("OVNSB commit failed, force recompute next time.");
+            engine_set_force_recompute(true);
+        }
 
         if (ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop) == 1) {
             struct shash_node *iter, *iter_next;
-            SHASH_FOR_EACH_SAFE(iter, iter_next, &pending_ct_zones) {
+            SHASH_FOR_EACH_SAFE (iter, iter_next,
+                                 &ed_runtime_data.pending_ct_zones) {
                 struct ct_zone_pending_entry *ctzpe = iter->data;
                 if (ctzpe->state == CT_ZONE_DB_SENT) {
-                    shash_delete(&pending_ct_zones, iter);
+                    shash_delete(&ed_runtime_data.pending_ct_zones, iter);
                     free(ctzpe);
                 }
             }
@@ -908,6 +1387,9 @@ main(int argc, char *argv[])
         }
     }
 
+    engine_set_context(NULL);
+    engine_cleanup(&en_flow_output);
+
     /* It's time to exit.  Clean up the databases if we are not restarting */
     if (!restart) {
         bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl);
@@ -928,8 +1410,7 @@ main(int argc, char *argv[])
             const struct sbrec_port_binding_table *port_binding_table
                 = sbrec_port_binding_table_get(ovnsb_idl_loop.idl);
 
-            const struct ovsrec_bridge *br_int = get_br_int(ovs_idl_txn,
-                                                            bridge_table,
+            const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
                                                             ovs_table);
             const char *chassis_id = get_chassis_id(ovs_table);
             const struct sbrec_chassis *chassis
@@ -957,12 +1438,6 @@ main(int argc, char *argv[])
     ofctrl_destroy();
     pinctrl_destroy();
 
-    simap_destroy(&ct_zones);
-    shash_destroy(&pending_ct_zones);
-
-    ovn_extend_table_destroy(&group_table);
-    ovn_extend_table_destroy(&meter_table);
-
     ovsdb_idl_loop_destroy(&ovs_idl_loop);
     ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
 
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index f3ab72f..24aea52 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)
 {
@@ -236,7 +239,8 @@ static void
 put_local_common_flows(uint32_t dp_key, uint32_t port_key,
                        uint32_t parent_port_key,
                        const struct zone_ids *zone_ids,
-                       struct ofpbuf *ofpacts_p, struct hmap *flow_table)
+                       struct ofpbuf *ofpacts_p,
+                       struct ovn_desired_flow_table *flow_table)
 {
     struct match match;
 
@@ -270,7 +274,7 @@ 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);
+                    &match, ofpacts_p, hc_uuid);
 
     /* Table 34, Priority 100.
      * =======================
@@ -285,7 +289,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
     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);
+                    &match, ofpacts_p, hc_uuid);
 
     /* Table 64, Priority 100.
      * =======================
@@ -317,7 +321,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
     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);
+                    &match, ofpacts_p, hc_uuid);
 
     if (nested_container) {
         /* It's a nested container and when the packet from the nested
@@ -348,7 +352,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
         put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
         put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
         ofctrl_check_and_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
-                                  &match, ofpacts_p, false);
+                                  &match, ofpacts_p, hc_uuid, false);
     }
 }
 
@@ -384,8 +388,8 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                       const struct hmap *local_datapaths,
                       const struct sbrec_port_binding *binding,
                       const struct sbrec_chassis *chassis,
-                      struct ofpbuf *ofpacts_p,
-                      struct hmap *flow_table)
+                      struct ovn_desired_flow_table *flow_table,
+                      struct ofpbuf *ofpacts_p)
 {
     uint32_t dp_key = binding->datapath->tunnel_key;
     uint32_t port_key = binding->tunnel_key;
@@ -441,7 +445,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
         ofpact_finish_CLONE(ofpacts_p, &clone);
 
         ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
-                        &match, ofpacts_p);
+                        &match, ofpacts_p, hc_uuid);
         return;
     }
 
@@ -510,7 +514,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
         }
 
         ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
-                        &match, ofpacts_p);
+                        &match, ofpacts_p, hc_uuid);
 
         goto out;
     }
@@ -662,7 +666,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
         /* 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);
+                        tag ? 150 : 100, 0, &match, ofpacts_p, hc_uuid);
 
         if (!tag && (!strcmp(binding->type, "localnet")
                      || !strcmp(binding->type, "l2gateway"))) {
@@ -672,7 +676,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
              * 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(flow_table, 0, 100, 0, &match, ofpacts_p, hc_uuid);
         }
 
         /* Table 65, Priority 100.
@@ -700,7 +704,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
             ofpact_put_STRIP_VLAN(ofpacts_p);
         }
         ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
-                        &match, ofpacts_p);
+                        &match, ofpacts_p, hc_uuid);
     } else if (!tun && !is_ha_remote) {
         /* Remote port connected by localnet port */
         /* Table 33, priority 100.
@@ -723,7 +727,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
         /* Resubmit to table 33. */
         put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
         ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
-                        &match, ofpacts_p);
+                        &match, ofpacts_p, hc_uuid);
     } else {
         /* Remote port connected by tunnel */
 
@@ -824,7 +828,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
             ofpact_finish_BUNDLE(ofpacts_p, &bundle);
         }
         ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
-                        &match, ofpacts_p);
+                        &match, ofpacts_p, hc_uuid);
     }
 out:
     if (ha_ch_ordered) {
@@ -840,7 +844,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
                   const struct sbrec_multicast_group *mc,
                   struct ofpbuf *ofpacts_p,
                   struct ofpbuf *remote_ofpacts_p,
-                  struct hmap *flow_table)
+                  struct ovn_desired_flow_table *flow_table)
 {
     uint32_t dp_key = mc->datapath->tunnel_key;
     if (!get_local_datapath(local_datapaths, dp_key)) {
@@ -916,7 +920,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
         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);
+                        &match, ofpacts_p, hc_uuid);
     }
 
     /* Table 32, priority 100.
@@ -954,7 +958,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
                 put_resubmit(OFTABLE_LOCAL_OUTPUT, remote_ofpacts_p);
             }
             ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
-                            &match, remote_ofpacts_p);
+                            &match, remote_ofpacts_p, hc_uuid);
         }
     }
     sset_destroy(&remote_chassis);
@@ -982,8 +986,13 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
              const struct hmap *local_datapaths,
              const struct sset *local_lports,
              const struct sset *active_tunnels,
-             struct hmap *flow_table)
+             struct ovn_desired_flow_table *flow_table)
 {
+    if (!hc_uuid) {
+        hc_uuid = xmalloc(sizeof(struct uuid));
+        uuid_generate(hc_uuid);
+    }
+
     /* This bool tracks physical mapping changes. */
     bool physical_map_changed = false;
 
@@ -1127,7 +1136,7 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                               mff_ovn_geneve, ct_zones,
                               active_tunnels,
                               local_datapaths, binding, chassis,
-                              &ofpacts, flow_table);
+                              flow_table, &ofpacts);
     }
 
     /* Handle output to multicast groups, in tables 32 and 33. */
@@ -1177,7 +1186,7 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
         put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
 
         ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
-                        &ofpacts);
+                        &ofpacts, hc_uuid);
     }
 
     /* Add flows for VXLAN encapsulations.  Due to the limited amount of
@@ -1210,7 +1219,7 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
             put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
 
             ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
-                            &ofpacts);
+                            &ofpacts, hc_uuid);
         }
     }
 
@@ -1231,7 +1240,7 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
     ofpbuf_clear(&ofpacts);
     put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
     ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
-                    &match, &ofpacts);
+                    &match, &ofpacts, hc_uuid);
 
     /* Table 32, priority 150.
      * =======================
@@ -1245,7 +1254,7 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
     ofpbuf_clear(&ofpacts);
     put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
     ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
-                    &match, &ofpacts);
+                    &match, &ofpacts, hc_uuid);
 
     /* Table 32, priority 150.
      * =======================
@@ -1268,7 +1277,7 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
             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);
+                            &match, &ofpacts, hc_uuid);
         }
     }
 
@@ -1280,7 +1289,8 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
     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(flow_table, OFTABLE_REMOTE_OUTPUT, 0, 0, &match, &ofpacts,
+                    hc_uuid);
 
     /* Table 34, Priority 0.
      * =======================
@@ -1295,7 +1305,7 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
     }
     put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts);
     ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, 0, &match,
-                    &ofpacts);
+                    &ofpacts, hc_uuid);
 
     /* Table 64, Priority 0.
      * =======================
@@ -1305,7 +1315,8 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
     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(flow_table, 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 38c3a87..d9b48fc 100644
--- a/ovn/controller/physical.h
+++ b/ovn/controller/physical.h
@@ -53,6 +53,6 @@ void physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                   const struct hmap *local_datapaths,
                   const struct sset *local_lports,
                   const struct sset *active_tunnels,
-                  struct hmap *flow_table);
+                  struct ovn_desired_flow_table *);
 
 #endif /* ovn/physical.h */
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index d590991..4c36b69 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_cstr(&ds));
+    table_id = ovn_extend_table_assign_id(ep->group_table, ds_cstr(&ds),
+                                          ep->lflow_uuid);
     ds_destroy(&ds);
     if (table_id == EXT_TABLE_ID_INVALID) {
         return;
@@ -2213,7 +2214,8 @@ encode_LOG(const struct ovnact_log *log,
     uint32_t meter_id = NX_CTLR_NO_METER;
 
     if (log->meter) {
-        meter_id = ovn_extend_table_assign_id(ep->meter_table, log->meter);
+        meter_id = ovn_extend_table_assign_id(ep->meter_table, log->meter,
+                                              ep->lflow_uuid);
         if (meter_id == EXT_TABLE_ID_INVALID) {
             VLOG_WARN("Unable to assign id for log meter: %s", log->meter);
             return;
@@ -2306,7 +2308,8 @@ encode_SET_METER(const struct ovnact_set_meter *cl,
                          "rate=%"PRId64"", cl->rate);
     }
 
-    table_id = ovn_extend_table_assign_id(ep->meter_table, name);
+    table_id = ovn_extend_table_assign_id(ep->meter_table, name,
+                                          ep->lflow_uuid);
     free(name);
     if (table_id == EXT_TABLE_ID_INVALID) {
         return;
diff --git a/ovn/lib/extend-table.c b/ovn/lib/extend-table.c
index 56c784f..ccf70ca 100644
--- a/ovn/lib/extend-table.c
+++ b/ovn/lib/extend-table.c
@@ -19,6 +19,7 @@
 
 #include "bitmap.h"
 #include "hash.h"
+#include "lib/uuid.h"
 #include "openvswitch/vlog.h"
 #include "ovn/lib/extend-table.h"
 
@@ -90,9 +91,10 @@ ovn_extend_table_clear(struct ovn_extend_table *table, bool existing)
     }
 }
 
+/* Remove an entry from existing table */
 void
-ovn_extend_table_remove(struct ovn_extend_table *table,
-                        struct ovn_extend_table_info *existing)
+ovn_extend_table_remove_existing(struct ovn_extend_table *table,
+                                 struct ovn_extend_table_info *existing)
 {
     /* Remove 'existing' from 'groups->existing' */
     hmap_remove(&table->existing, &existing->hmap_node);
@@ -103,21 +105,50 @@ ovn_extend_table_remove(struct ovn_extend_table *table,
     free(existing);
 }
 
+/* Remove entries in desired table that are created by the lflow_uuid */
 void
-ovn_extend_table_move(struct ovn_extend_table *table)
+ovn_extend_table_remove_desired(struct ovn_extend_table *table,
+                                const struct uuid *lflow_uuid)
+{
+    struct ovn_extend_table_info *e, *next_e;
+    HMAP_FOR_EACH_SAFE (e, next_e, hmap_node, &table->desired) {
+        if (uuid_equals(&e->lflow_uuid, lflow_uuid)) {
+            hmap_remove(&table->desired, &e->hmap_node);
+            free(e->name);
+            if (e->new_table_id) {
+                bitmap_set0(table->table_ids, e->table_id);
+            }
+            free(e);
+        }
+    }
+
+}
+
+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);
+    clone->name = xstrdup(source->name);
+    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_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 {
-           free(desired->name);
-           free(desired);
+            desired->new_table_id = false;
+            struct ovn_extend_table_info *clone =
+                ovn_extend_info_clone(desired);
+            hmap_insert(&table->existing, &clone->hmap_node,
+                        clone->hmap_node.hash);
         }
     }
 }
@@ -125,7 +156,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, const char *name)
+ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name,
+                           struct uuid lflow_uuid)
 {
     uint32_t table_id = 0, hash;
     struct ovn_extend_table_info *table_info;
@@ -134,7 +166,8 @@ ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name)
 
     /* Check whether we have non installed but allocated group_id. */
     HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->desired) {
-        if (!strcmp(table_info->name, name)) {
+        if (!strcmp(table_info->name, name) &&
+            table_info->new_table_id) {
             return table_info->table_id;
         }
     }
@@ -166,6 +199,7 @@ ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name)
     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 e4501f2..5be13fe 100644
--- a/ovn/lib/extend-table.h
+++ b/ovn/lib/extend-table.h
@@ -22,6 +22,7 @@
 
 #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. */
@@ -36,6 +37,7 @@ struct ovn_extend_table {
 struct ovn_extend_table_info {
     struct hmap_node hmap_node;
     char *name;         /* Name 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. */
@@ -50,14 +52,18 @@ struct ovn_extend_table_info *ovn_extend_table_lookup(
 
 void ovn_extend_table_clear(struct ovn_extend_table *, bool);
 
-void ovn_extend_table_remove(struct ovn_extend_table *,
-                             struct ovn_extend_table_info *);
+void ovn_extend_table_remove_existing(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 *);
+void ovn_extend_table_remove_desired(struct ovn_extend_table *,
+                                     const struct uuid *lflow_uuid);
+
+/* 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 *,
-                                    const char *name);
+                                    const char *name,
+                                    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