This patch introduces a change handler for 'northd' input within the
'lflow' node. It specifically handles cases when VIFs are created, which
is an easier start for lflow incremental processing. Support for
update/delete will be added later.

Below are the performance test results simulating an ovn-k8s topology of 500
nodes x 50 lsp per node:

Before:
ovn-nbctl --wait=hv --print-wait-time lsp-add ls_1_0001 lsp_1_0001_01 -- 
lsp-set-addresses lsp_1_0001_01 "ff:f1:bb:00:01:01 1.0.1.101" -- 
lsp-set-port-security lsp_1_0001_01 "ff:f1:bb:00:01:01 1.0.1.101"
    ovn-northd completion:          773ms

After:
ovn-nbctl --wait=hv --print-wait-time lsp-add ls_1_0001 lsp_1_0001_01 -- 
lsp-set-addresses lsp_1_0001_01 "ff:f1:bb:00:01:01 1.0.1.101" -- 
lsp-set-port-security lsp_1_0001_01 "ff:f1:bb:00:01:01 1.0.1.101"
    ovn-northd completion:          30ms

It is more than 95% reduction (or 20x faster).

Signed-off-by: Han Zhou <[email protected]>
---
 northd/en-lflow.c        |  82 ++++++++-----
 northd/en-lflow.h        |   1 +
 northd/inc-proc-northd.c |   2 +-
 northd/northd.c          | 245 +++++++++++++++++++++++++++++++++------
 northd/northd.h          |   6 +-
 tests/ovn-northd.at      |   4 +-
 6 files changed, 277 insertions(+), 63 deletions(-)

diff --git a/northd/en-lflow.c b/northd/en-lflow.c
index 081ec7c353ed..28ab1c67fb8f 100644
--- a/northd/en-lflow.c
+++ b/northd/en-lflow.c
@@ -30,43 +30,49 @@
 
 VLOG_DEFINE_THIS_MODULE(en_lflow);
 
-void en_lflow_run(struct engine_node *node, void *data)
+static void
+lflow_get_input_data(struct engine_node *node,
+                     struct lflow_input *lflow_input)
 {
-    const struct engine_context *eng_ctx = engine_get_context();
-
-    struct lflow_input lflow_input;
-
     struct northd_data *northd_data = engine_get_input_data("northd", node);
-
-    struct hmap bfd_connections = HMAP_INITIALIZER(&bfd_connections);
-
-    lflow_input.nbrec_bfd_table =
+    lflow_input->nbrec_bfd_table =
         EN_OVSDB_GET(engine_get_input("NB_bfd", node));
-    lflow_input.sbrec_bfd_table =
+    lflow_input->sbrec_bfd_table =
         EN_OVSDB_GET(engine_get_input("SB_bfd", node));
-    lflow_input.sbrec_logical_flow_table =
+    lflow_input->sbrec_logical_flow_table =
         EN_OVSDB_GET(engine_get_input("SB_logical_flow", node));
-    lflow_input.sbrec_multicast_group_table =
+    lflow_input->sbrec_multicast_group_table =
         EN_OVSDB_GET(engine_get_input("SB_multicast_group", node));
-    lflow_input.sbrec_igmp_group_table =
+    lflow_input->sbrec_igmp_group_table =
         EN_OVSDB_GET(engine_get_input("SB_igmp_group", node));
 
-    lflow_input.sbrec_mcast_group_by_name_dp =
+    lflow_input->sbrec_mcast_group_by_name_dp =
            engine_ovsdb_node_get_index(
                           engine_get_input("SB_multicast_group", node),
                          "sbrec_mcast_group_by_name");
 
-    lflow_input.ls_datapaths = &northd_data->ls_datapaths;
-    lflow_input.lr_datapaths = &northd_data->lr_datapaths;
-    lflow_input.ls_ports = &northd_data->ls_ports;
-    lflow_input.lr_ports = &northd_data->lr_ports;
-    lflow_input.port_groups = &northd_data->port_groups;
-    lflow_input.meter_groups = &northd_data->meter_groups;
-    lflow_input.lbs = &northd_data->lbs;
-    lflow_input.bfd_connections = &bfd_connections;
-    lflow_input.features = &northd_data->features;
-    lflow_input.ovn_internal_version_changed =
+    lflow_input->ls_datapaths = &northd_data->ls_datapaths;
+    lflow_input->lr_datapaths = &northd_data->lr_datapaths;
+    lflow_input->ls_ports = &northd_data->ls_ports;
+    lflow_input->lr_ports = &northd_data->lr_ports;
+    lflow_input->port_groups = &northd_data->port_groups;
+    lflow_input->meter_groups = &northd_data->meter_groups;
+    lflow_input->lbs = &northd_data->lbs;
+    lflow_input->features = &northd_data->features;
+    lflow_input->ovn_internal_version_changed =
                       northd_data->ovn_internal_version_changed;
+    lflow_input->bfd_connections = NULL;
+}
+
+void en_lflow_run(struct engine_node *node, void *data)
+{
+    const struct engine_context *eng_ctx = engine_get_context();
+
+    struct lflow_input lflow_input;
+    lflow_get_input_data(node, &lflow_input);
+
+    struct hmap bfd_connections = HMAP_INITIALIZER(&bfd_connections);
+    lflow_input.bfd_connections = &bfd_connections;
 
     struct lflow_data *lflow_data = data;
     lflow_data_destroy(lflow_data);
@@ -76,8 +82,8 @@ void en_lflow_run(struct engine_node *node, void *data)
     build_bfd_table(eng_ctx->ovnsb_idl_txn,
                     lflow_input.nbrec_bfd_table,
                     lflow_input.sbrec_bfd_table,
-                    &bfd_connections,
-                    &northd_data->lr_ports);
+                    lflow_input.lr_ports,
+                    &bfd_connections);
     build_lflows(eng_ctx->ovnsb_idl_txn, &lflow_input, &lflow_data->lflows);
     bfd_cleanup_connections(lflow_input.nbrec_bfd_table,
                             &bfd_connections);
@@ -87,6 +93,30 @@ void en_lflow_run(struct engine_node *node, void *data)
     engine_set_node_state(node, EN_UPDATED);
 }
 
+bool
+lflow_northd_handler(struct engine_node *node,
+                     void *data)
+{
+    struct northd_data *northd_data = engine_get_input_data("northd", node);
+    if (!northd_data->change_tracked) {
+        return false;
+    }
+    const struct engine_context *eng_ctx = engine_get_context();
+    struct lflow_data *lflow_data = data;
+
+    struct lflow_input lflow_input;
+    lflow_get_input_data(node, &lflow_input);
+
+    if (!lflow_handle_northd_ls_changes(eng_ctx->ovnsb_idl_txn,
+                                        &northd_data->tracked_ls_changes,
+                                        &lflow_input, &lflow_data->lflows)) {
+        return false;
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
 void *en_lflow_init(struct engine_node *node OVS_UNUSED,
                      struct engine_arg *arg OVS_UNUSED)
 {
diff --git a/northd/en-lflow.h b/northd/en-lflow.h
index 0e4d522ff3fe..5e3fbc25e3e0 100644
--- a/northd/en-lflow.h
+++ b/northd/en-lflow.h
@@ -12,5 +12,6 @@
 void en_lflow_run(struct engine_node *node, void *data);
 void *en_lflow_init(struct engine_node *node, struct engine_arg *arg);
 void en_lflow_cleanup(void *data);
+bool lflow_northd_handler(struct engine_node *, void *data);
 
 #endif /* EN_LFLOW_H */
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index f992a9ec8420..f6ceb8280624 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -182,7 +182,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_lflow, &en_sb_logical_flow, NULL);
     engine_add_input(&en_lflow, &en_sb_multicast_group, NULL);
     engine_add_input(&en_lflow, &en_sb_igmp_group, NULL);
-    engine_add_input(&en_lflow, &en_northd, NULL);
+    engine_add_input(&en_lflow, &en_northd, lflow_northd_handler);
 
     engine_add_input(&en_sync_to_sb_addr_set, &en_nb_address_set,
                      sync_to_sb_addr_set_nb_address_set_handler);
diff --git a/northd/northd.c b/northd/northd.c
index 2cb568b8e1dd..32c18ab3c932 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -5500,6 +5500,7 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups,
 
 struct ovn_lflow {
     struct hmap_node hmap_node;
+    struct ovs_list list_node;
 
     struct ovn_datapath *od;     /* 'logical_datapath' in SB schema.  */
     unsigned long *dpg_bitmap;   /* Bitmap of all datapaths by their 'index'.*/
@@ -5557,6 +5558,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct 
ovn_datapath *od,
                char *match, char *actions, char *io_port, char *ctrl_meter,
                char *stage_hint, const char *where)
 {
+    ovs_list_init(&lflow->list_node);
     lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
     lflow->od = od;
     lflow->stage = stage;
@@ -5685,7 +5687,18 @@ ovn_dp_group_add_with_reference(struct ovn_lflow 
*lflow_ref,
 
 /* Adds a row with the specified contents to the Logical_Flow table.
  * Version to use when hash bucket locking is NOT required.
+ *
+ * Note: This function can add generated lflows to the global variable
+ * temp_lflow_list as its output, controlled by the global variable
+ * add_lflow_to_temp_list. The caller of the ovn_lflow_add_... marcros can get
+ * a list of lflows generated by setting add_lflow_to_temp_list to true. The
+ * caller is responsible for initializing the temp_lflow_list, and also
+ * reset the add_lflow_to_temp_list to false when it is no longer needed.
+ * XXX: this mechanism is temporary and will be replaced when we add hash index
+ * to lflow_data and refactor related functions.
  */
+static bool add_lflow_to_temp_list = false;
+static struct ovs_list temp_lflow_list;
 static void
 do_ovn_lflow_add(struct hmap *lflow_map, const struct ovn_datapath *od,
                  const unsigned long *dp_bitmap, size_t dp_bitmap_len,
@@ -5702,12 +5715,17 @@ do_ovn_lflow_add(struct hmap *lflow_map, const struct 
ovn_datapath *od,
     size_t bitmap_len = od ? ods_size(od->datapaths) : dp_bitmap_len;
     ovs_assert(bitmap_len);
 
-    old_lflow = ovn_lflow_find(lflow_map, NULL, stage, priority, match,
-                               actions, ctrl_meter, hash);
-    if (old_lflow) {
-        ovn_dp_group_add_with_reference(old_lflow, od, dp_bitmap,
-                                        bitmap_len);
-        return;
+    if (add_lflow_to_temp_list) {
+        ovs_assert(od);
+        ovs_assert(!dp_bitmap);
+    } else {
+        old_lflow = ovn_lflow_find(lflow_map, NULL, stage, priority, match,
+                                   actions, ctrl_meter, hash);
+        if (old_lflow) {
+            ovn_dp_group_add_with_reference(old_lflow, od, dp_bitmap,
+                                            bitmap_len);
+            return;
+        }
     }
 
     lflow = xmalloc(sizeof *lflow);
@@ -5728,6 +5746,10 @@ do_ovn_lflow_add(struct hmap *lflow_map, const struct 
ovn_datapath *od,
         hmap_insert_fast(lflow_map, &lflow->hmap_node, hash);
         thread_lflow_counter++;
     }
+
+    if (add_lflow_to_temp_list) {
+        ovs_list_insert(&temp_lflow_list, &lflow->list_node);
+    }
 }
 
 /* Adds a row with the specified contents to the Logical_Flow table. */
@@ -9867,7 +9889,7 @@ void
 build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
                 const struct nbrec_bfd_table *nbrec_bfd_table,
                 const struct sbrec_bfd_table *sbrec_bfd_table,
-                struct hmap *bfd_connections, struct hmap *lr_ports)
+                const struct hmap *lr_ports, struct hmap *bfd_connections)
 {
     struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
     const struct sbrec_bfd *sb_bt;
@@ -15219,32 +15241,28 @@ build_lswitch_and_lrouter_iterate_by_lr(struct 
ovn_datapath *od,
  */
 static void
 build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op,
-                                         struct lswitch_flow_build_info *lsi)
+                                         const struct hmap *ls_ports,
+                                         const struct hmap *lr_ports,
+                                         const struct shash *meter_groups,
+                                         struct ds *match,
+                                         struct ds *actions,
+                                         struct hmap *lflows)
 {
     ovs_assert(op->nbsp);
 
     /* Build Logical Switch Flows. */
-    build_lswitch_port_sec_op(op, lsi->lflows, &lsi->actions, &lsi->match);
-    build_lswitch_learn_fdb_op(op, lsi->lflows, &lsi->actions,
-                               &lsi->match);
-    build_lswitch_arp_nd_responder_skip_local(op, lsi->lflows,
-                                              &lsi->match);
-    build_lswitch_arp_nd_responder_known_ips(op, lsi->lflows,
-                                             lsi->ls_ports,
-                                             lsi->meter_groups,
-                                             &lsi->actions,
-                                             &lsi->match);
-    build_lswitch_dhcp_options_and_response(op, lsi->lflows,
-                                            lsi->meter_groups);
-    build_lswitch_external_port(op, lsi->lflows);
-    build_lswitch_ip_unicast_lookup(op, lsi->lflows, &lsi->actions,
-                                    &lsi->match);
+    build_lswitch_port_sec_op(op, lflows, actions, match);
+    build_lswitch_learn_fdb_op(op, lflows, actions, match);
+    build_lswitch_arp_nd_responder_skip_local(op, lflows, match);
+    build_lswitch_arp_nd_responder_known_ips(op, lflows, ls_ports,
+                                             meter_groups, actions, match);
+    build_lswitch_dhcp_options_and_response(op, lflows, meter_groups);
+    build_lswitch_external_port(op, lflows);
+    build_lswitch_ip_unicast_lookup(op, lflows, actions, match);
 
     /* Build Logical Router Flows. */
-    build_ip_routing_flows_for_router_type_lsp(op, lsi->lr_ports,
-                                               lsi->lflows);
-    build_arp_resolve_flows_for_lsp(op, lsi->lflows, lsi->lr_ports,
-                                    &lsi->match, &lsi->actions);
+    build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows);
+    build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions);
 }
 
 /* Helper function to combine all lflow generation which is iterated by logical
@@ -15330,7 +15348,12 @@ build_lflows_thread(void *arg)
                     if (stop_parallel_processing()) {
                         return NULL;
                     }
-                    build_lswitch_and_lrouter_iterate_by_lsp(op, lsi);
+                    build_lswitch_and_lrouter_iterate_by_lsp(op, lsi->ls_ports,
+                                                             lsi->lr_ports,
+                                                             lsi->meter_groups,
+                                                             &lsi->match,
+                                                             &lsi->actions,
+                                                             lsi->lflows);
                 }
             }
             for (bnum = control->id;
@@ -15515,7 +15538,11 @@ build_lswitch_and_lrouter_flows(const struct 
ovn_datapaths *ls_datapaths,
         stopwatch_stop(LFLOWS_DATAPATHS_STOPWATCH_NAME, time_msec());
         stopwatch_start(LFLOWS_PORTS_STOPWATCH_NAME, time_msec());
         HMAP_FOR_EACH (op, key_node, ls_ports) {
-            build_lswitch_and_lrouter_iterate_by_lsp(op, &lsi);
+            build_lswitch_and_lrouter_iterate_by_lsp(op, lsi.ls_ports,
+                                                     lsi.lr_ports,
+                                                     lsi.meter_groups,
+                                                     &lsi.match, &lsi.actions,
+                                                     lsi.lflows);
         }
         HMAP_FOR_EACH (op, key_node, lr_ports) {
             build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
@@ -15629,6 +15656,20 @@ build_mcast_groups(const struct sbrec_igmp_group_table 
*sbrec_igmp_group_table,
                    struct hmap *mcast_groups,
                    struct hmap *igmp_groups);
 
+static struct sbrec_multicast_group *
+create_sb_multicast_group(struct ovsdb_idl_txn *ovnsb_txn,
+                          const struct sbrec_datapath_binding *dp,
+                          const char *name,
+                          int64_t tunnel_key)
+{
+    struct sbrec_multicast_group *sbmc =
+        sbrec_multicast_group_insert(ovnsb_txn);
+    sbrec_multicast_group_set_datapath(sbmc, dp);
+    sbrec_multicast_group_set_name(sbmc, name);
+    sbrec_multicast_group_set_tunnel_key(sbmc, tunnel_key);
+    return sbmc;
+}
+
 /* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB database,
  * constructing their contents based on the OVN_NB database. */
 void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
@@ -15995,10 +16036,8 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
             ovn_multicast_destroy(&mcast_groups, mc);
             continue;
         }
-        sbmc = sbrec_multicast_group_insert(ovnsb_txn);
-        sbrec_multicast_group_set_datapath(sbmc, mc->datapath->sb);
-        sbrec_multicast_group_set_name(sbmc, mc->group->name);
-        sbrec_multicast_group_set_tunnel_key(sbmc, mc->group->key);
+        sbmc = create_sb_multicast_group(ovnsb_txn, mc->datapath->sb,
+                                         mc->group->name, mc->group->key);
         ovn_multicast_update_sbrec(mc, sbmc);
         ovn_multicast_destroy(&mcast_groups, mc);
     }
@@ -16013,6 +16052,146 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
     hmap_destroy(&mcast_groups);
 }
 
+bool lflow_handle_northd_ls_changes(struct ovsdb_idl_txn *ovnsb_txn,
+                                    struct tracked_ls_changes *ls_changes,
+                                    struct lflow_input *lflow_input,
+                                    struct hmap *lflows)
+{
+    struct ls_change *ls_change;
+    LIST_FOR_EACH (ls_change, list_node, &ls_changes->updated) {
+        ovs_list_init(&temp_lflow_list);
+        add_lflow_to_temp_list = true;
+        if (!ovs_list_is_empty(&ls_change->updated_ports) ||
+            !ovs_list_is_empty(&ls_change->deleted_ports)) {
+            /* XXX: implement lflow index so that we can handle updated and
+             * deleted LSPs incrementally. */
+            ovs_list_init(&temp_lflow_list);
+            add_lflow_to_temp_list = false;
+            return false;
+        }
+
+        const struct sbrec_multicast_group *sbmc_flood =
+            mcast_group_lookup(lflow_input->sbrec_mcast_group_by_name_dp,
+                               MC_FLOOD, ls_change->od->sb);
+        const struct sbrec_multicast_group *sbmc_flood_l2 =
+            mcast_group_lookup(lflow_input->sbrec_mcast_group_by_name_dp,
+                               MC_FLOOD_L2, ls_change->od->sb);
+        const struct sbrec_multicast_group *sbmc_unknown =
+            mcast_group_lookup(lflow_input->sbrec_mcast_group_by_name_dp,
+                               MC_UNKNOWN, ls_change->od->sb);
+
+        struct ovn_port *op;
+        LIST_FOR_EACH (op, list, &ls_change->added_ports) {
+            struct ds match = DS_EMPTY_INITIALIZER;
+            struct ds actions = DS_EMPTY_INITIALIZER;
+            build_lswitch_and_lrouter_iterate_by_lsp(op, lflow_input->ls_ports,
+                                                     lflow_input->lr_ports,
+                                                     lflow_input->meter_groups,
+                                                     &match, &actions,
+                                                     lflows);
+            ds_destroy(&match);
+            ds_destroy(&actions);
+            if (!sbmc_flood) {
+                sbmc_flood = create_sb_multicast_group(ovnsb_txn,
+                    ls_change->od->sb, MC_FLOOD, OVN_MCAST_FLOOD_TUNNEL_KEY);
+            }
+            sbrec_multicast_group_update_ports_addvalue(sbmc_flood, op->sb);
+
+            if (!sbmc_flood_l2) {
+                sbmc_flood_l2 = create_sb_multicast_group(ovnsb_txn,
+                    ls_change->od->sb, MC_FLOOD_L2,
+                    OVN_MCAST_FLOOD_L2_TUNNEL_KEY);
+            }
+            sbrec_multicast_group_update_ports_addvalue(sbmc_flood_l2, op->sb);
+
+            if (op->has_unknown) {
+                if (!sbmc_unknown) {
+                    sbmc_unknown = create_sb_multicast_group(ovnsb_txn,
+                        ls_change->od->sb, MC_UNKNOWN,
+                        OVN_MCAST_UNKNOWN_TUNNEL_KEY);
+                }
+                sbrec_multicast_group_update_ports_addvalue(sbmc_unknown,
+                                                            op->sb);
+            }
+        }
+        /* Sync the newly added flows to SB. */
+        struct ovn_lflow *lflow;
+        LIST_FOR_EACH (lflow, list_node, &temp_lflow_list) {
+            size_t n_datapaths;
+            struct ovn_datapath **datapaths_array;
+            if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH) {
+                n_datapaths = ods_size(lflow_input->ls_datapaths);
+                datapaths_array = lflow_input->ls_datapaths->array;
+            } else {
+                n_datapaths = ods_size(lflow_input->lr_datapaths);
+                datapaths_array = lflow_input->lr_datapaths->array;
+            }
+            uint32_t n_ods = bitmap_count1(lflow->dpg_bitmap, n_datapaths);
+            ovs_assert(n_ods == 1);
+            /* There is only one datapath, so it should be moved out of the
+             * group to a single 'od'. */
+            size_t index = bitmap_scan(lflow->dpg_bitmap, true, 0,
+                                       n_datapaths);
+
+            bitmap_set0(lflow->dpg_bitmap, index);
+            lflow->od = datapaths_array[index];
+
+            /* Logical flow should be re-hashed to allow lookups. */
+            uint32_t hash = hmap_node_hash(&lflow->hmap_node);
+            /* Remove from lflows. */
+            hmap_remove(lflows, &lflow->hmap_node);
+            hash = ovn_logical_flow_hash_datapath(&lflow->od->sb->header_.uuid,
+                                                  hash);
+            /* Add back. */
+            hmap_insert(lflows, &lflow->hmap_node, hash);
+
+            /* Sync to SB. */
+            const struct sbrec_logical_flow *sbflow;
+            sbflow = sbrec_logical_flow_insert(ovnsb_txn);
+            const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
+            uint8_t table = ovn_stage_get_table(lflow->stage);
+            sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
+            sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
+            sbrec_logical_flow_set_pipeline(sbflow, pipeline);
+            sbrec_logical_flow_set_table_id(sbflow, table);
+            sbrec_logical_flow_set_priority(sbflow, lflow->priority);
+            sbrec_logical_flow_set_match(sbflow, lflow->match);
+            sbrec_logical_flow_set_actions(sbflow, lflow->actions);
+            if (lflow->io_port) {
+                struct smap tags = SMAP_INITIALIZER(&tags);
+                smap_add(&tags, "in_out_port", lflow->io_port);
+                sbrec_logical_flow_set_tags(sbflow, &tags);
+                smap_destroy(&tags);
+            }
+            sbrec_logical_flow_set_controller_meter(sbflow, lflow->ctrl_meter);
+            /* Trim the source locator lflow->where, which looks something like
+             * "ovn/northd/northd.c:1234", down to just the part following the
+             * last slash, e.g. "northd.c:1234". */
+            const char *slash = strrchr(lflow->where, '/');
+#if _WIN32
+            const char *backslash = strrchr(lflow->where, '\\');
+            if (!slash || backslash > slash) {
+                slash = backslash;
+            }
+#endif
+            const char *where = slash ? slash + 1 : lflow->where;
+
+            struct smap ids = SMAP_INITIALIZER(&ids);
+            smap_add(&ids, "stage-name", ovn_stage_to_str(lflow->stage));
+            smap_add(&ids, "source", where);
+            if (lflow->stage_hint) {
+                smap_add(&ids, "stage-hint", lflow->stage_hint);
+            }
+            sbrec_logical_flow_set_external_ids(sbflow, &ids);
+            smap_destroy(&ids);
+        }
+    }
+    ovs_list_init(&temp_lflow_list);
+    add_lflow_to_temp_list = false;
+    return true;
+
+}
+
 /* Each port group in Port_Group table in OVN_Northbound has a corresponding
  * entry in Port_Group table in OVN_Southbound. In OVN_Northbound the entries
  * contains lport uuids, while in OVN_Southbound we store the lport names.
diff --git a/northd/northd.h b/northd/northd.h
index f549fee0b5f7..8be504811921 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -330,11 +330,15 @@ void northd_indices_create(struct northd_data *data,
 void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
                   struct lflow_input *input_data,
                   struct hmap *lflows);
+bool lflow_handle_northd_ls_changes(struct ovsdb_idl_txn *ovnsb_txn,
+                                    struct tracked_ls_changes *,
+                                    struct lflow_input *, struct hmap *lflows);
 
 void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
                      const struct nbrec_bfd_table *,
                      const struct sbrec_bfd_table *,
-                     struct hmap *bfd_connections, struct hmap *lr_ports);
+                     const struct hmap *lr_ports,
+                     struct hmap *bfd_connections);
 void bfd_cleanup_connections(const struct nbrec_bfd_table *,
                              struct hmap *bfd_map);
 void run_update_worker_pool(int n_threads);
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 428237e432c2..deeb1d310925 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -9000,14 +9000,14 @@ check as northd ovn-appctl -t NORTHD_TYPE 
inc-engine/clear-stats
 check ovn-nbctl --wait=hv lsp-add ls0 lsp0-1 -- lsp-set-addresses lsp0-1 
"aa:aa:aa:00:00:01 192.168.0.11"
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd 
recompute], [0], [3
 ])
-AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats lflow 
recompute], [0], [6
+AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats lflow 
recompute], [0], [5
 ])
 
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
 check ovn-nbctl --wait=hv lsp-add ls0 lsp0-2 -- lsp-set-addresses lsp0-2 
"aa:aa:aa:00:00:02 192.168.0.12"
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd 
recompute], [0], [3
 ])
-AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats lflow 
recompute], [0], [6
+AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats lflow 
recompute], [0], [5
 ])
 
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-- 
2.30.2

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to