[ovs-dev] [PATCH ovn v4 4/6] northd: Introduce struct northd_data

Han Zhou hzhou at ovn.org
Wed Nov 3 23:37:04 UTC 2021


On Wed, Oct 27, 2021 at 10:24 AM Mark Gray <mark.d.gray at redhat.com> wrote:
>
> 'struct northd_data' is used to hold the global state data for the
> incremental processing node 'en_northd'. This structure will also hold
> 'struct northd_input' which will hold references to output data from
> its input nodes. In particular, this will hold references to database
tables
> and indexes. In order to achieve this, we refactor in the following way:

Thanks Mark for the revision. It does look more clear now with input data
wrapped in northd_input. However, my suggestion was to move the inputs out
of the northd_data completely (sorry if I wasn't clear on this).
The reason is, northd_data should contain only output data of en_northd,
which is potentially going to be used as input data for other nodes that
depend on en_northd. The other nodes depending on en_northd are supposed to
access any data included in northd_data. If we put northd_input in the
northd_data itself, those nodes may access them as well by mistake, and it
is going to be hard to track the dependencies properly with the I-P engine.
So my suggestion is just keep these two as independent structures, one for
input and one for output. The northd_input will be used by en_northd's
run() and handlers only. If another node depends on some common input in
northd_input, they should declare the dependency in the I-P engine and get
the data from their own input nodes.

For northd_run(), it can have the signature like northd_run(northd_input,
..., north_output).

With convention of I-P in ovn-controller, we would name the struct
northd_data ed_type_northd_data, to make it clear this is the output data
maintained for the I-P engine node. I understand that you don't want
anything related to I-P engine in the northd.h/c, so not following the
naming convention is ok for me. I'd just remind that these data types are
interfaces between different engine nodes, so be aware that it is less of
an encapsulation but more like table definitions in a DB schema.

The rest of the patch LGTM.

Thanks,
Han

>
> * Introduce northd_init() which initializes this data.
> * Introduce northd_destroy() which clears this data for a new iteration.
> * Remove 'ovn_internal_version' from the context passed to northd
>   as it can be determined within northd using ovn_get_internal_version.
> * Remove 'use_parallel_build' from the context as it is read from DB as
>   suggested by Han.
> * Refactor northd.c to use 'struct northd_data' and 'struct northd_input'
>   where applicable.
>
> Signed-off-by: Mark Gray <mark.d.gray at redhat.com>
> ---
>
> Notes:
>     v4: Add northd_input (move DB references and indexes to this struct)
>         Remove the use of client_ctx from engine_context
>
>  lib/inc-proc-eng.h       |   3 +
>  northd/en-northd.c       | 106 ++++++-
>  northd/inc-proc-northd.c |  42 ++-
>  northd/inc-proc-northd.h |   6 +-
>  northd/northd.c          | 603 +++++++++++++++++++++------------------
>  northd/northd.h          |  75 ++++-
>  northd/ovn-northd.c      |  85 ++----
>  7 files changed, 568 insertions(+), 352 deletions(-)
>
> diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h
> index f89a40bd54ca..1823750c814c 100644
> --- a/lib/inc-proc-eng.h
> +++ b/lib/inc-proc-eng.h
> @@ -72,6 +72,9 @@ struct engine_context {
>      struct ovsdb_idl_txn *ovs_idl_txn;
>      struct ovsdb_idl_txn *ovnsb_idl_txn;
>      struct ovsdb_idl_txn *ovnnb_idl_txn;
> +
> +    struct ovsdb_idl_loop *ovnsb_idl_loop;
> +
>      void *client_ctx;
>  };
>
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index d310fa4dd31f..36ea890535fe 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -20,26 +20,122 @@
>
>  #include "en-northd.h"
>  #include "lib/inc-proc-eng.h"
> +#include "openvswitch/list.h" /* TODO This is needed for
ovn-parallel-hmap.h.
> +                               * lib/ovn-parallel-hmap.h should be
updated
> +                               * to include this dependency itself */
> +#include "lib/ovn-parallel-hmap.h"
>  #include "northd.h"
> +#include "lib/util.h"
>  #include "openvswitch/vlog.h"
>
>  VLOG_DEFINE_THIS_MODULE(en_northd);
>
> -void en_northd_run(struct engine_node *node, void *data OVS_UNUSED)
> +void en_northd_run(struct engine_node *node, void *data)
>  {
>      const struct engine_context *eng_ctx = engine_get_context();
> -    struct northd_context *ctx = eng_ctx->client_ctx;
> -    ovn_db_run(ctx);
>
> +    northd_destroy(data);
> +    northd_init(data);
> +
> +    struct northd_data *northd_data = data;
> +    northd_data->input.sbrec_chassis_by_name =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_chassis", node),
> +            "sbrec_chassis_by_name");
> +    northd_data->input.sbrec_chassis_by_hostname =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_chassis", node),
> +            "sbrec_chassis_by_hostname");
> +    northd_data->input.sbrec_ha_chassis_grp_by_name =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_ha_chassis_group", node),
> +            "sbrec_ha_chassis_grp_by_name");
> +    northd_data->input.sbrec_mcast_group_by_name_dp =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_multicast_group", node),
> +            "sbrec_mcast_group_by_name");
> +    northd_data->input.sbrec_ip_mcast_by_dp =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_ip_multicast", node),
> +            "sbrec_ip_mcast_by_dp");
> +
> +    northd_data->input.nbrec_nb_global_table =
> +        EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
> +    northd_data->input.nbrec_logical_switch =
> +        EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
> +    northd_data->input.nbrec_logical_router =
> +        EN_OVSDB_GET(engine_get_input("NB_logical_router", node));
> +    northd_data->input.nbrec_load_balancer_table =
> +        EN_OVSDB_GET(engine_get_input("NB_load_balancer", node));
> +    northd_data->input.nbrec_port_group_table =
> +        EN_OVSDB_GET(engine_get_input("NB_port_group", node));
> +    northd_data->input.nbrec_bfd_table =
> +        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
> +    northd_data->input.nbrec_address_set_table =
> +        EN_OVSDB_GET(engine_get_input("NB_address_set", node));
> +    northd_data->input.nbrec_meter_table =
> +        EN_OVSDB_GET(engine_get_input("NB_meter", node));
> +    northd_data->input.nbrec_acl_table =
> +        EN_OVSDB_GET(engine_get_input("NB_acl", node));
> +
> +    northd_data->input.sbrec_sb_global_table =
> +        EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
> +    northd_data->input.sbrec_datapath_binding_table =
> +        EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
> +    northd_data->input.sbrec_port_binding_table =
> +        EN_OVSDB_GET(engine_get_input("SB_port_binding", node));
> +    northd_data->input.sbrec_mac_binding_table =
> +        EN_OVSDB_GET(engine_get_input("SB_mac_binding", node));
> +    northd_data->input.sbrec_ha_chassis_group_table =
> +        EN_OVSDB_GET(engine_get_input("SB_ha_chassis_group", node));
> +    northd_data->input.sbrec_chassis =
> +        EN_OVSDB_GET(engine_get_input("SB_chassis", node));
> +    northd_data->input.sbrec_fdb_table =
> +        EN_OVSDB_GET(engine_get_input("SB_fdb", node));
> +    northd_data->input.sbrec_load_balancer_table =
> +        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
> +    northd_data->input.sbrec_service_monitor_table =
> +        EN_OVSDB_GET(engine_get_input("SB_service_monitor", node));
> +    northd_data->input.sbrec_bfd_table =
> +        EN_OVSDB_GET(engine_get_input("SB_bfd", node));
> +    northd_data->input.sbrec_logical_flow_table =
> +        EN_OVSDB_GET(engine_get_input("SB_logical_flow", node));
> +    northd_data->input.sbrec_multicast_group_table =
> +        EN_OVSDB_GET(engine_get_input("SB_multicast_group", node));
> +    northd_data->input.sbrec_address_set_table =
> +        EN_OVSDB_GET(engine_get_input("SB_address_set", node));
> +    northd_data->input.sbrec_port_group_table =
> +        EN_OVSDB_GET(engine_get_input("SB_port_group", node));
> +    northd_data->input.sbrec_meter_table =
> +        EN_OVSDB_GET(engine_get_input("SB_meter", node));
> +    northd_data->input.sbrec_dns_table =
> +        EN_OVSDB_GET(engine_get_input("SB_dns", node));
> +    northd_data->input.sbrec_ip_multicast_table =
> +        EN_OVSDB_GET(engine_get_input("SB_ip_multicast", node));
> +    northd_data->input.sbrec_igmp_group_table =
> +        EN_OVSDB_GET(engine_get_input("SB_igmp_group", node));
> +    northd_data->input.sbrec_chassis_private_table =
> +        EN_OVSDB_GET(engine_get_input("SB_chassis_private", node));
> +
> +    northd_run(data,
> +               eng_ctx->ovnnb_idl_txn,
> +               eng_ctx->ovnsb_idl_txn,
> +               eng_ctx->ovnsb_idl_loop);
>      engine_set_node_state(node, EN_UPDATED);
>
>  }
>  void *en_northd_init(struct engine_node *node OVS_UNUSED,
>                       struct engine_arg *arg OVS_UNUSED)
>  {
> -    return NULL;
> +    struct northd_data *data = xmalloc(sizeof *data);
> +
> +    northd_init(data);
> +
> +    return data;
>  }
>
> -void en_northd_cleanup(void *data OVS_UNUSED)
> +void en_northd_cleanup(void *data)
>  {
> +    northd_destroy(data);
> +    free(data);
>  }
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index 85baeb07d3d9..d2da0489cc1c 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -18,9 +18,12 @@
>  #include <stdlib.h>
>  #include <stdio.h>
>
> +#include "chassis-index.h"
> +#include "ip-mcast-index.h"
>  #include "lib/inc-proc-eng.h"
>  #include "lib/ovn-nb-idl.h"
>  #include "lib/ovn-sb-idl.h"
> +#include "mcast-group-index.h"
>  #include "openvswitch/poll-loop.h"
>  #include "openvswitch/vlog.h"
>  #include "inc-proc-northd.h"
> @@ -210,23 +213,52 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>          .sb_idl = sb->idl,
>      };
>
> +    struct ovsdb_idl_index *sbrec_chassis_by_name =
> +                         chassis_index_create(sb->idl);
> +    struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name =
> +                         ha_chassis_group_index_create(sb->idl);
> +    struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp =
> +                         mcast_group_index_create(sb->idl);
> +    struct ovsdb_idl_index *sbrec_ip_mcast_by_dp =
> +                         ip_mcast_index_create(sb->idl);
> +    struct ovsdb_idl_index *sbrec_chassis_by_hostname =
> +        chassis_hostname_index_create(sb->idl);
> +
>      engine_init(&en_northd, &engine_arg);
> +
> +    engine_ovsdb_node_add_index(&en_sb_chassis,
> +                                "sbrec_chassis_by_name",
> +                                sbrec_chassis_by_name);
> +    engine_ovsdb_node_add_index(&en_sb_chassis,
> +                                "sbrec_chassis_by_hostname",
> +                                sbrec_chassis_by_hostname);
> +    engine_ovsdb_node_add_index(&en_sb_ha_chassis_group,
> +                                "sbrec_ha_chassis_grp_by_name",
> +                                sbrec_ha_chassis_grp_by_name);
> +    engine_ovsdb_node_add_index(&en_sb_multicast_group,
> +                                "sbrec_mcast_group_by_name",
> +                                sbrec_mcast_group_by_name_dp);
> +    engine_ovsdb_node_add_index(&en_sb_ip_multicast,
> +                                "sbrec_ip_mcast_by_dp",
> +                                sbrec_ip_mcast_by_dp);
>  }
>
> -void inc_proc_northd_run(struct northd_context *ctx,
> +void inc_proc_northd_run(struct ovsdb_idl_txn *ovnnb_txn,
> +                         struct ovsdb_idl_txn *ovnsb_txn,
> +                         struct ovsdb_idl_loop *ovnsb_idl_loop,
>                           bool recompute) {
>      engine_set_force_recompute(recompute);
>      engine_init_run();
>
>      struct engine_context eng_ctx = {
> -        .ovnnb_idl_txn = ctx->ovnnb_txn,
> -        .ovnsb_idl_txn = ctx->ovnsb_txn,
> -        .client_ctx = ctx,
> +        .ovnnb_idl_txn = ovnnb_txn,
> +        .ovnsb_idl_txn = ovnsb_txn,
> +        .ovnsb_idl_loop = ovnsb_idl_loop,
>      };
>
>      engine_set_context(&eng_ctx);
>
> -    if (ctx->ovnnb_txn && ctx->ovnsb_txn) {
> +    if (ovnnb_txn && ovnsb_txn) {
>          engine_run(true);
>      }
>
> diff --git a/northd/inc-proc-northd.h b/northd/inc-proc-northd.h
> index 09cb8f3b3a80..b6c38e68749d 100644
> --- a/northd/inc-proc-northd.h
> +++ b/northd/inc-proc-northd.h
> @@ -6,9 +6,13 @@
>  #include "northd.h"
>  #include "ovsdb-idl.h"
>
> +
> +
>  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>                            struct ovsdb_idl_loop *sb);
> -void inc_proc_northd_run(struct northd_context *ctx,
> +void inc_proc_northd_run(struct ovsdb_idl_txn *ovnnb_txn,
> +                         struct ovsdb_idl_txn *ovnsb_txn,
> +                         struct ovsdb_idl_loop *ovnsb_idl_loop,
>                           bool recompute);
>  void inc_proc_northd_cleanup(void);
>
> diff --git a/northd/northd.c b/northd/northd.c
> index 455b9b0e867a..9649d94772ad 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -1261,21 +1261,24 @@ ovn_datapath_update_external_ids(struct
ovn_datapath *od)
>  }
>
>  static void
> -join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
> -               struct ovs_list *sb_only, struct ovs_list *nb_only,
> -               struct ovs_list *both, struct ovs_list *lr_list)
> +join_datapaths(struct northd_data *data,
> +               struct ovsdb_idl_txn *ovnsb_txn,
> +               struct hmap *datapaths, struct ovs_list *sb_only,
> +               struct ovs_list *nb_only, struct ovs_list *both,
> +               struct ovs_list *lr_list)
>  {
>      ovs_list_init(sb_only);
>      ovs_list_init(nb_only);
>      ovs_list_init(both);
>
>      const struct sbrec_datapath_binding *sb, *sb_next;
> -    SBREC_DATAPATH_BINDING_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
> +    SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_SAFE (sb, sb_next,
> +                            data->input.sbrec_datapath_binding_table) {
>          struct uuid key;
>          if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
>              !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
>              ovsdb_idl_txn_add_comment(
> -                ctx->ovnsb_txn,
> +                ovnsb_txn,
>                  "deleting Datapath_Binding "UUID_FMT" that lacks "
>                  "external-ids:logical-switch and "
>                  "external-ids:logical-router",
> @@ -1300,7 +1303,8 @@ join_datapaths(struct northd_context *ctx, struct
hmap *datapaths,
>      }
>
>      const struct nbrec_logical_switch *nbs;
> -    NBREC_LOGICAL_SWITCH_FOR_EACH (nbs, ctx->ovnnb_idl) {
> +    NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH (nbs,
> +                              data->input.nbrec_logical_switch) {
>          struct ovn_datapath *od = ovn_datapath_find(datapaths,
>                                                      &nbs->header_.uuid);
>          if (od) {
> @@ -1320,7 +1324,8 @@ join_datapaths(struct northd_context *ctx, struct
hmap *datapaths,
>      }
>
>      const struct nbrec_logical_router *nbr;
> -    NBREC_LOGICAL_ROUTER_FOR_EACH (nbr, ctx->ovnnb_idl) {
> +    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH (nbr,
> +                               data->input.nbrec_logical_router) {
>          if (!lrouter_is_enabled(nbr)) {
>              continue;
>          }
> @@ -1358,10 +1363,10 @@ join_datapaths(struct northd_context *ctx, struct
hmap *datapaths,
>  }
>
>  static bool
> -is_vxlan_mode(struct ovsdb_idl *ovnsb_idl)
> +is_vxlan_mode(struct northd_data *data)
>  {
>      const struct sbrec_chassis *chassis;
> -    SBREC_CHASSIS_FOR_EACH (chassis, ovnsb_idl) {
> +    SBREC_CHASSIS_TABLE_FOR_EACH (chassis, data->input.sbrec_chassis) {
>          for (int i = 0; i < chassis->n_encaps; i++) {
>              if (!strcmp(chassis->encaps[i]->type, "vxlan")) {
>                  return true;
> @@ -1372,9 +1377,9 @@ is_vxlan_mode(struct ovsdb_idl *ovnsb_idl)
>  }
>
>  static uint32_t
> -get_ovn_max_dp_key_local(struct northd_context *ctx)
> +get_ovn_max_dp_key_local(struct northd_data *data)
>  {
> -    if (is_vxlan_mode(ctx->ovnsb_idl)) {
> +    if (is_vxlan_mode(data)) {
>          /* OVN_MAX_DP_GLOBAL_NUM doesn't apply for vxlan mode. */
>          return OVN_MAX_DP_VXLAN_KEY;
>      }
> @@ -1382,14 +1387,14 @@ get_ovn_max_dp_key_local(struct northd_context
*ctx)
>  }
>
>  static void
> -ovn_datapath_allocate_key(struct northd_context *ctx,
> +ovn_datapath_allocate_key(struct northd_data *data,
>                            struct hmap *datapaths, struct hmap *dp_tnlids,
>                            struct ovn_datapath *od, uint32_t *hint)
>  {
>      if (!od->tunnel_key) {
>          od->tunnel_key = ovn_allocate_tnlid(dp_tnlids, "datapath",
>                                              OVN_MIN_DP_KEY_LOCAL,
> -
 get_ovn_max_dp_key_local(ctx),
> +
 get_ovn_max_dp_key_local(data),
>                                              hint);
>          if (!od->tunnel_key) {
>              if (od->sb) {
> @@ -1402,7 +1407,7 @@ ovn_datapath_allocate_key(struct northd_context
*ctx,
>  }
>
>  static void
> -ovn_datapath_assign_requested_tnl_id(struct northd_context *ctx,
> +ovn_datapath_assign_requested_tnl_id(struct northd_data *data,
>                                       struct hmap *dp_tnlids,
>                                       struct ovn_datapath *od)
>  {
> @@ -1411,7 +1416,7 @@ ovn_datapath_assign_requested_tnl_id(struct
northd_context *ctx,
>                                         : &od->nbr->options);
>      uint32_t tunnel_key = smap_get_int(other_config,
"requested-tnl-key", 0);
>      if (tunnel_key) {
> -        if (is_vxlan_mode(ctx->ovnsb_idl) && tunnel_key >= 1 << 12) {
> +        if (is_vxlan_mode(data) && tunnel_key >= 1 << 12) {
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1,
1);
>              VLOG_WARN_RL(&rl, "Tunnel key %"PRIu32" for datapath %s is "
>                           "incompatible with VXLAN", tunnel_key,
> @@ -1436,21 +1441,24 @@ ovn_datapath_assign_requested_tnl_id(struct
northd_context *ctx,
>   * Initializes 'datapaths' to contain a "struct ovn_datapath" for every
logical
>   * switch and router. */
>  static void
> -build_datapaths(struct northd_context *ctx, struct hmap *datapaths,
> +build_datapaths(struct northd_data *data,
> +                struct ovsdb_idl_txn *ovnsb_txn,
> +                struct hmap *datapaths,
>                  struct ovs_list *lr_list)
>  {
>      struct ovs_list sb_only, nb_only, both;
>
> -    join_datapaths(ctx, datapaths, &sb_only, &nb_only, &both, lr_list);
> +    join_datapaths(data, ovnsb_txn,
> +                   datapaths, &sb_only, &nb_only, &both, lr_list);
>
>      /* Assign explicitly requested tunnel ids first. */
>      struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
>      struct ovn_datapath *od, *next;
>      LIST_FOR_EACH (od, list, &both) {
> -        ovn_datapath_assign_requested_tnl_id(ctx, &dp_tnlids, od);
> +        ovn_datapath_assign_requested_tnl_id(data, &dp_tnlids, od);
>      }
>      LIST_FOR_EACH (od, list, &nb_only) {
> -        ovn_datapath_assign_requested_tnl_id(ctx, &dp_tnlids, od);
> +        ovn_datapath_assign_requested_tnl_id(data, &dp_tnlids, od);
>      }
>
>      /* Keep nonconflicting tunnel IDs that are already assigned. */
> @@ -1463,10 +1471,10 @@ build_datapaths(struct northd_context *ctx,
struct hmap *datapaths,
>      /* Assign new tunnel ids where needed. */
>      uint32_t hint = 0;
>      LIST_FOR_EACH_SAFE (od, next, list, &both) {
> -        ovn_datapath_allocate_key(ctx, datapaths, &dp_tnlids, od, &hint);
> +        ovn_datapath_allocate_key(data, datapaths, &dp_tnlids, od,
&hint);
>      }
>      LIST_FOR_EACH_SAFE (od, next, list, &nb_only) {
> -        ovn_datapath_allocate_key(ctx, datapaths, &dp_tnlids, od, &hint);
> +        ovn_datapath_allocate_key(data, datapaths, &dp_tnlids, od,
&hint);
>      }
>
>      /* Sync tunnel ids from nb to sb. */
> @@ -1477,7 +1485,7 @@ build_datapaths(struct northd_context *ctx, struct
hmap *datapaths,
>          ovn_datapath_update_external_ids(od);
>      }
>      LIST_FOR_EACH (od, list, &nb_only) {
> -        od->sb = sbrec_datapath_binding_insert(ctx->ovnsb_txn);
> +        od->sb = sbrec_datapath_binding_insert(ovnsb_txn);
>          ovn_datapath_update_external_ids(od);
>          sbrec_datapath_binding_set_tunnel_key(od->sb, od->tunnel_key);
>      }
> @@ -2407,7 +2415,7 @@ tag_alloc_create_new_tag(struct hmap
*tag_alloc_table,
>
>
>  static void
> -join_logical_ports(struct northd_context *ctx,
> +join_logical_ports(struct northd_data *data,
>                     struct hmap *datapaths, struct hmap *ports,
>                     struct hmap *chassis_qdisc_queues,
>                     struct hmap *tag_alloc_table, struct ovs_list
*sb_only,
> @@ -2418,7 +2426,8 @@ join_logical_ports(struct northd_context *ctx,
>      ovs_list_init(both);
>
>      const struct sbrec_port_binding *sb;
> -    SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) {
> +    SBREC_PORT_BINDING_TABLE_FOR_EACH (sb,
> +                                 data->input.sbrec_port_binding_table) {
>          struct ovn_port *op = ovn_port_create(ports, sb->logical_port,
>                                                NULL, NULL, sb);
>          ovs_list_push_back(sb_only, &op->list);
> @@ -2869,12 +2878,12 @@ sbpb_gw_chassis_needs_update(
>  }
>
>  static struct sbrec_ha_chassis *
> -create_sb_ha_chassis(struct northd_context *ctx,
> +create_sb_ha_chassis(struct ovsdb_idl_txn *ovnsb_txn,
>                       const struct sbrec_chassis *chassis,
>                       const char *chassis_name, int priority)
>  {
>      struct sbrec_ha_chassis *sb_ha_chassis =
> -        sbrec_ha_chassis_insert(ctx->ovnsb_txn);
> +        sbrec_ha_chassis_insert(ovnsb_txn);
>      sbrec_ha_chassis_set_chassis(sb_ha_chassis, chassis);
>      sbrec_ha_chassis_set_priority(sb_ha_chassis, priority);
>      /* Store the chassis_name in external_ids. If the chassis
> @@ -2954,7 +2963,8 @@ chassis_group_list_changed(
>  }
>
>  static void
> -sync_ha_chassis_group_for_sbpb(struct northd_context *ctx,
> +sync_ha_chassis_group_for_sbpb(struct northd_data *data,
> +                               struct ovsdb_idl_txn *ovnsb_txn,
>                                 const struct nbrec_ha_chassis_group
*nb_ha_grp,
>                                 struct ovsdb_idl_index
*sbrec_chassis_by_name,
>                                 const struct sbrec_port_binding *pb)
> @@ -2962,10 +2972,10 @@ sync_ha_chassis_group_for_sbpb(struct
northd_context *ctx,
>      bool new_sb_chassis_group = false;
>      const struct sbrec_ha_chassis_group *sb_ha_grp =
>          ha_chassis_group_lookup_by_name(
> -            ctx->sbrec_ha_chassis_grp_by_name, nb_ha_grp->name);
> +            data->input.sbrec_ha_chassis_grp_by_name, nb_ha_grp->name);
>
>      if (!sb_ha_grp) {
> -        sb_ha_grp = sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
> +        sb_ha_grp = sbrec_ha_chassis_group_insert(ovnsb_txn);
>          sbrec_ha_chassis_group_set_name(sb_ha_grp, nb_ha_grp->name);
>          new_sb_chassis_group = true;
>      }
> @@ -2982,7 +2992,7 @@ sync_ha_chassis_group_for_sbpb(struct
northd_context *ctx,
>              const struct sbrec_chassis *chassis =
>                  chassis_lookup_by_name(sbrec_chassis_by_name,
>                                         nb_ha_chassis->chassis_name);
> -            sb_ha_chassis[i] = sbrec_ha_chassis_insert(ctx->ovnsb_txn);
> +            sb_ha_chassis[i] = sbrec_ha_chassis_insert(ovnsb_txn);
>              /* It's perfectly ok if the chassis is NULL. This could
>               * happen when ovn-controller exits and removes its row
>               * from the chassis table in OVN SB DB. */
> @@ -3007,7 +3017,8 @@ sync_ha_chassis_group_for_sbpb(struct
northd_context *ctx,
>   */
>  static void
>  copy_gw_chassis_from_nbrp_to_sbpb(
> -        struct northd_context *ctx,
> +        struct northd_data *data,
> +        struct ovsdb_idl_txn *ovnsb_txn,
>          struct ovsdb_idl_index *sbrec_chassis_by_name,
>          const struct nbrec_logical_router_port *lrp,
>          const struct sbrec_port_binding *port_binding)
> @@ -3017,9 +3028,9 @@ copy_gw_chassis_from_nbrp_to_sbpb(
>       * for the distributed gateway router port. */
>      const struct sbrec_ha_chassis_group *sb_ha_chassis_group =
>          ha_chassis_group_lookup_by_name(
> -            ctx->sbrec_ha_chassis_grp_by_name, lrp->name);
> +            data->input.sbrec_ha_chassis_grp_by_name, lrp->name);
>      if (!sb_ha_chassis_group) {
> -        sb_ha_chassis_group =
sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
> +        sb_ha_chassis_group = sbrec_ha_chassis_group_insert(ovnsb_txn);
>          sbrec_ha_chassis_group_set_name(sb_ha_chassis_group, lrp->name);
>      }
>
> @@ -3037,7 +3048,7 @@ copy_gw_chassis_from_nbrp_to_sbpb(
>                                     lrp_gwc->chassis_name);
>
>          sb_ha_chassis[n_sb_ha_ch] =
> -            create_sb_ha_chassis(ctx, chassis, lrp_gwc->chassis_name,
> +            create_sb_ha_chassis(ovnsb_txn, chassis,
lrp_gwc->chassis_name,
>                                   lrp_gwc->priority);
>          n_sb_ha_ch++;
>      }
> @@ -3085,7 +3096,8 @@ ovn_update_ipv6_prefix(struct hmap *ports)
>  }
>
>  static void
> -ovn_port_update_sbrec(struct northd_context *ctx,
> +ovn_port_update_sbrec(struct northd_data *data,
> +                      struct ovsdb_idl_txn *ovnsb_txn,
>                        struct ovsdb_idl_index *sbrec_chassis_by_name,
>                        struct ovsdb_idl_index *sbrec_chassis_by_hostname,
>                        const struct ovn_port *op,
> @@ -3121,7 +3133,8 @@ ovn_port_update_sbrec(struct northd_context *ctx,
>                  }
>
>                  /* HA Chassis group is set. Ignore 'gateway_chassis'. */
> -                sync_ha_chassis_group_for_sbpb(ctx,
op->nbrp->ha_chassis_group,
> +                sync_ha_chassis_group_for_sbpb(data, ovnsb_txn,
> +
op->nbrp->ha_chassis_group,
>                                                 sbrec_chassis_by_name,
op->sb);
>                  sset_add(active_ha_chassis_grps,
>                           op->nbrp->ha_chassis_group->name);
> @@ -3131,7 +3144,7 @@ ovn_port_update_sbrec(struct northd_context *ctx,
>                   * associated with the lrp. */
>                  if (sbpb_gw_chassis_needs_update(op->sb, op->nbrp,
>                                                   sbrec_chassis_by_name))
{
> -                    copy_gw_chassis_from_nbrp_to_sbpb(ctx,
> +                    copy_gw_chassis_from_nbrp_to_sbpb(data, ovnsb_txn,
>
 sbrec_chassis_by_name,
>                                                        op->nbrp, op->sb);
>                  }
> @@ -3255,7 +3268,7 @@ ovn_port_update_sbrec(struct northd_context *ctx,
>              if (!strcmp(op->nbsp->type, "external")) {
>                  if (op->nbsp->ha_chassis_group) {
>                      sync_ha_chassis_group_for_sbpb(
> -                        ctx, op->nbsp->ha_chassis_group,
> +                        data, ovnsb_txn, op->nbsp->ha_chassis_group,
>                          sbrec_chassis_by_name, op->sb);
>                      sset_add(active_ha_chassis_grps,
>                               op->nbsp->ha_chassis_group->name);
> @@ -3454,11 +3467,12 @@ ovn_port_update_sbrec(struct northd_context *ctx,
>  /* Remove mac_binding entries that refer to logical_ports which are
>   * deleted. */
>  static void
> -cleanup_mac_bindings(struct northd_context *ctx, struct hmap *datapaths,
> +cleanup_mac_bindings(struct northd_data *data, struct hmap *datapaths,
>                       struct hmap *ports)
>  {
>      const struct sbrec_mac_binding *b, *n;
> -    SBREC_MAC_BINDING_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
> +    SBREC_MAC_BINDING_TABLE_FOR_EACH_SAFE (b, n,
> +                             data->input.sbrec_mac_binding_table) {
>          const struct ovn_datapath *od =
>              ovn_datapath_from_sbrec(datapaths, b->datapath);
>
> @@ -3470,11 +3484,12 @@ cleanup_mac_bindings(struct northd_context *ctx,
struct hmap *datapaths,
>  }
>
>  static void
> -cleanup_sb_ha_chassis_groups(struct northd_context *ctx,
> +cleanup_sb_ha_chassis_groups(struct northd_data *data,
>                               struct sset *active_ha_chassis_groups)
>  {
>      const struct sbrec_ha_chassis_group *b, *n;
> -    SBREC_HA_CHASSIS_GROUP_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
> +    SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH_SAFE (b, n,
> +
 data->input.sbrec_ha_chassis_group_table) {
>          if (!sset_contains(active_ha_chassis_groups, b->name)) {
>              sbrec_ha_chassis_group_delete(b);
>          }
> @@ -3482,10 +3497,12 @@ cleanup_sb_ha_chassis_groups(struct
northd_context *ctx,
>  }
>
>  static void
> -cleanup_stale_fdp_entries(struct northd_context *ctx, struct hmap
*datapaths)
> +cleanup_stale_fdp_entries(struct northd_data *data,
> +                          struct hmap *datapaths)
>  {
>      const struct sbrec_fdb *fdb_e, *next;
> -    SBREC_FDB_FOR_EACH_SAFE (fdb_e, next, ctx->ovnsb_idl) {
> +    SBREC_FDB_TABLE_FOR_EACH_SAFE (fdb_e, next,
> +                         data->input.sbrec_fdb_table) {
>          bool delete = true;
>          struct ovn_datapath *od
>              = ovn_datapath_find_by_key(datapaths, fdb_e->dp_key);
> @@ -3509,7 +3526,7 @@ struct service_monitor_info {
>
>
>  static struct service_monitor_info *
> -create_or_get_service_mon(struct northd_context *ctx,
> +create_or_get_service_mon(struct ovsdb_idl_txn *ovnsb_txn,
>                            struct hmap *monitor_map,
>                            const char *ip, const char *logical_port,
>                            uint16_t service_port, const char *protocol)
> @@ -3529,7 +3546,7 @@ create_or_get_service_mon(struct northd_context
*ctx,
>      }
>
>      struct sbrec_service_monitor *sbrec_mon =
> -        sbrec_service_monitor_insert(ctx->ovnsb_txn);
> +        sbrec_service_monitor_insert(ovnsb_txn);
>      sbrec_service_monitor_set_ip(sbrec_mon, ip);
>      sbrec_service_monitor_set_port(sbrec_mon, service_port);
>      sbrec_service_monitor_set_logical_port(sbrec_mon, logical_port);
> @@ -3541,7 +3558,7 @@ create_or_get_service_mon(struct northd_context
*ctx,
>  }
>
>  static void
> -ovn_lb_svc_create(struct northd_context *ctx, struct ovn_northd_lb *lb,
> +ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, struct ovn_northd_lb
*lb,
>                    struct hmap *monitor_map, struct hmap *ports)
>  {
>      for (size_t i = 0; i < lb->n_vips; i++) {
> @@ -3582,7 +3599,7 @@ ovn_lb_svc_create(struct northd_context *ctx,
struct ovn_northd_lb *lb,
>              }
>              backend_nb->health_check = true;
>              struct service_monitor_info *mon_info =
> -                create_or_get_service_mon(ctx, monitor_map,
> +                create_or_get_service_mon(ovnsb_txn, monitor_map,
>                                            backend->ip_str,
>                                            backend_nb->op->nbsp->name,
>                                            backend->port,
> @@ -3716,15 +3733,17 @@ build_ovn_lr_lbs(struct hmap *datapaths, struct
hmap *lbs)
>  }
>
>  static void
> -build_ovn_lbs(struct northd_context *ctx, struct hmap *datapaths,
> -              struct hmap *lbs)
> +build_ovn_lbs(struct northd_data *data,
> +              struct ovsdb_idl_txn *ovnsb_txn,
> +              struct hmap *datapaths, struct hmap *lbs)
>  {
>      struct ovn_northd_lb *lb;
>
>      hmap_init(lbs);
>
>      const struct nbrec_load_balancer *nbrec_lb;
> -    NBREC_LOAD_BALANCER_FOR_EACH (nbrec_lb, ctx->ovnnb_idl) {
> +    NBREC_LOAD_BALANCER_TABLE_FOR_EACH (nbrec_lb,
> +                               data->input.nbrec_load_balancer_table) {
>          struct ovn_northd_lb *lb_nb = ovn_northd_lb_create(nbrec_lb);
>          hmap_insert(lbs, &lb_nb->hmap_node,
>                      uuid_hash(&nbrec_lb->header_.uuid));
> @@ -3757,7 +3776,8 @@ build_ovn_lbs(struct northd_context *ctx, struct
hmap *datapaths,
>
>      /* Delete any stale SB load balancer rows. */
>      const struct sbrec_load_balancer *sbrec_lb, *next;
> -    SBREC_LOAD_BALANCER_FOR_EACH_SAFE (sbrec_lb, next, ctx->ovnsb_idl) {
> +    SBREC_LOAD_BALANCER_TABLE_FOR_EACH_SAFE (sbrec_lb, next,
> +                            data->input.sbrec_load_balancer_table) {
>          const char *nb_lb_uuid = smap_get(&sbrec_lb->external_ids,
"lb_id");
>          struct uuid lb_uuid;
>          if (!nb_lb_uuid || !uuid_from_string(&lb_uuid, nb_lb_uuid)) {
> @@ -3796,7 +3816,7 @@ build_ovn_lbs(struct northd_context *ctx, struct
hmap *datapaths,
>          }
>
>          if (!lb->slb) {
> -            sbrec_lb = sbrec_load_balancer_insert(ctx->ovnsb_txn);
> +            sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn);
>              lb->slb = sbrec_lb;
>              char *lb_id = xasprintf(
>                  UUID_FMT, UUID_ARGS(&lb->nlb->header_.uuid));
> @@ -3829,13 +3849,14 @@ build_ovn_lbs(struct northd_context *ctx, struct
hmap *datapaths,
>  }
>
>  static void
> -build_ovn_lb_svcs(struct northd_context *ctx, struct hmap *ports,
> -                  struct hmap *lbs)
> +build_ovn_lb_svcs(struct northd_data *data, struct ovsdb_idl_txn
*ovnsb_txn,
> +                  struct hmap *ports, struct hmap *lbs)
>  {
>      struct hmap monitor_map = HMAP_INITIALIZER(&monitor_map);
>
>      const struct sbrec_service_monitor *sbrec_mon;
> -    SBREC_SERVICE_MONITOR_FOR_EACH (sbrec_mon, ctx->ovnsb_idl) {
> +    SBREC_SERVICE_MONITOR_TABLE_FOR_EACH (sbrec_mon,
> +                            data->input.sbrec_service_monitor_table) {
>          uint32_t hash = sbrec_mon->port;
>          hash = hash_string(sbrec_mon->ip, hash);
>          hash = hash_string(sbrec_mon->logical_port, hash);
> @@ -3847,7 +3868,7 @@ build_ovn_lb_svcs(struct northd_context *ctx,
struct hmap *ports,
>
>      struct ovn_northd_lb *lb;
>      HMAP_FOR_EACH (lb, hmap_node, lbs) {
> -        ovn_lb_svc_create(ctx, lb, &monitor_map, ports);
> +        ovn_lb_svc_create(ovnsb_txn, lb, &monitor_map, ports);
>      }
>
>      struct service_monitor_info *mon_info;
> @@ -3925,7 +3946,7 @@ ovn_port_add_tnlid(struct ovn_port *op, uint32_t
tunnel_key)
>  }
>
>  static void
> -ovn_port_assign_requested_tnl_id(struct northd_context *ctx,
> +ovn_port_assign_requested_tnl_id(struct northd_data *data,
>                                   struct ovn_port *op)
>  {
>      const struct smap *options = (op->nbsp
> @@ -3933,7 +3954,7 @@ ovn_port_assign_requested_tnl_id(struct
northd_context *ctx,
>                                    : &op->nbrp->options);
>      uint32_t tunnel_key = smap_get_int(options, "requested-tnl-key", 0);
>      if (tunnel_key) {
> -        if (is_vxlan_mode(ctx->ovnsb_idl) &&
> +        if (is_vxlan_mode(data) &&
>                  tunnel_key >= OVN_VXLAN_MIN_MULTICAST) {
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1,
1);
>              VLOG_WARN_RL(&rl, "Tunnel key %"PRIu32" for port %s "
> @@ -3952,11 +3973,11 @@ ovn_port_assign_requested_tnl_id(struct
northd_context *ctx,
>  }
>
>  static void
> -ovn_port_allocate_key(struct northd_context *ctx, struct hmap *ports,
> +ovn_port_allocate_key(struct northd_data *data, struct hmap *ports,
>                        struct ovn_port *op)
>  {
>      if (!op->tunnel_key) {
> -        uint8_t key_bits = is_vxlan_mode(ctx->ovnsb_idl)? 12 : 16;
> +        uint8_t key_bits = is_vxlan_mode(data)? 12 : 16;
>          op->tunnel_key = ovn_allocate_tnlid(&op->od->port_tnlids, "port",
>                                              1, (1u << (key_bits - 1)) -
1,
>                                              &op->od->port_key_hint);
> @@ -3977,7 +3998,8 @@ ovn_port_allocate_key(struct northd_context *ctx,
struct hmap *ports,
>   * using the "struct ovn_datapath"s in 'datapaths' to look up logical
>   * datapaths. */
>  static void
> -build_ports(struct northd_context *ctx,
> +build_ports(struct northd_data *data,
> +            struct ovsdb_idl_txn *ovnsb_txn,
>              struct ovsdb_idl_index *sbrec_chassis_by_name,
>              struct ovsdb_idl_index *sbrec_chassis_by_hostname,
>              struct hmap *datapaths, struct hmap *ports)
> @@ -3990,7 +4012,7 @@ build_ports(struct northd_context *ctx,
>      struct sset active_ha_chassis_grps =
>          SSET_INITIALIZER(&active_ha_chassis_grps);
>
> -    join_logical_ports(ctx, datapaths, ports, &chassis_qdisc_queues,
> +    join_logical_ports(data, datapaths, ports, &chassis_qdisc_queues,
>                         &tag_alloc_table, &sb_only, &nb_only, &both);
>
>      /* Purge stale Mac_Bindings if ports are deleted. */
> @@ -3999,10 +4021,10 @@ build_ports(struct northd_context *ctx,
>      /* Assign explicitly requested tunnel ids first. */
>      struct ovn_port *op, *next;
>      LIST_FOR_EACH (op, list, &both) {
> -        ovn_port_assign_requested_tnl_id(ctx, op);
> +        ovn_port_assign_requested_tnl_id(data, op);
>      }
>      LIST_FOR_EACH (op, list, &nb_only) {
> -        ovn_port_assign_requested_tnl_id(ctx, op);
> +        ovn_port_assign_requested_tnl_id(data, op);
>      }
>
>      /* Keep nonconflicting tunnel IDs that are already assigned. */
> @@ -4014,10 +4036,10 @@ build_ports(struct northd_context *ctx,
>
>      /* Assign new tunnel ids where needed. */
>      LIST_FOR_EACH_SAFE (op, next, list, &both) {
> -        ovn_port_allocate_key(ctx, ports, op);
> +        ovn_port_allocate_key(data, ports, op);
>      }
>      LIST_FOR_EACH_SAFE (op, next, list, &nb_only) {
> -        ovn_port_allocate_key(ctx, ports, op);
> +        ovn_port_allocate_key(data, ports, op);
>      }
>
>      /* For logical ports that are in both databases, update the
southbound
> @@ -4034,7 +4056,7 @@ build_ports(struct northd_context *ctx,
>          if (op->nbsp) {
>              tag_alloc_create_new_tag(&tag_alloc_table, op->nbsp);
>          }
> -        ovn_port_update_sbrec(ctx, sbrec_chassis_by_name,
> +        ovn_port_update_sbrec(data, ovnsb_txn, sbrec_chassis_by_name,
>                                sbrec_chassis_by_hostname,
>                                op, &chassis_qdisc_queues,
>                                &active_ha_chassis_grps);
> @@ -4042,9 +4064,9 @@ build_ports(struct northd_context *ctx,
>
>      /* Add southbound record for each unmatched northbound record. */
>      LIST_FOR_EACH_SAFE (op, next, list, &nb_only) {
> -        op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn);
> -        ovn_port_update_sbrec(ctx, sbrec_chassis_by_name,
> -                              sbrec_chassis_by_hostname, op,
> +        op->sb = sbrec_port_binding_insert(ovnsb_txn);
> +        ovn_port_update_sbrec(data, ovnsb_txn, sbrec_chassis_by_name,
> +                             sbrec_chassis_by_hostname, op,
>                                &chassis_qdisc_queues,
>                                &active_ha_chassis_grps);
>          sbrec_port_binding_set_logical_port(op->sb, op->key);
> @@ -4059,12 +4081,12 @@ build_ports(struct northd_context *ctx,
>          }
>      }
>      if (remove_mac_bindings) {
> -        cleanup_mac_bindings(ctx, datapaths, ports);
> +        cleanup_mac_bindings(data, datapaths, ports);
>      }
>
>      tag_alloc_destroy(&tag_alloc_table);
>      destroy_chassis_queues(&chassis_qdisc_queues);
> -    cleanup_sb_ha_chassis_groups(ctx, &active_ha_chassis_grps);
> +    cleanup_sb_ha_chassis_groups(data, &active_ha_chassis_grps);
>      sset_destroy(&active_ha_chassis_grps);
>  }
>
> @@ -4247,7 +4269,8 @@ ovn_igmp_group_find(struct hmap *igmp_groups,
>  }
>
>  static struct ovn_igmp_group *
> -ovn_igmp_group_add(struct northd_context *ctx, struct hmap *igmp_groups,
> +ovn_igmp_group_add(struct northd_data *data,
> +                   struct hmap *igmp_groups,
>                     struct ovn_datapath *datapath,
>                     const struct in6_addr *address,
>                     const char *address_s)
> @@ -4259,7 +4282,8 @@ ovn_igmp_group_add(struct northd_context *ctx,
struct hmap *igmp_groups,
>          igmp_group = xmalloc(sizeof *igmp_group);
>
>          const struct sbrec_multicast_group *mcgroup =
> -            mcast_group_lookup(ctx->sbrec_mcast_group_by_name_dp,
address_s,
> +            mcast_group_lookup(data->input.sbrec_mcast_group_by_name_dp,
> +                               address_s,
>                                 datapath->sb);
>
>          igmp_group->datapath = datapath;
> @@ -4466,7 +4490,7 @@ ovn_lflow_equal(const struct ovn_lflow *a, const
struct ovn_datapath *od,
>  /* If this option is 'true' northd will combine logical flows that
differ by
>   * logical datapath only by creating a datapath group. */
>  static bool use_logical_dp_groups = false;
> -static bool use_parallel_build = true;
> +static bool use_parallel_build = false;
>
>  static void
>  ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
> @@ -6219,13 +6243,14 @@ ovn_port_group_destroy(struct hmap *pgs, struct
ovn_port_group *pg)
>  }
>
>  static void
> -build_port_group_lswitches(struct northd_context *ctx, struct hmap *pgs,
> +build_port_group_lswitches(struct northd_data *data, struct hmap *pgs,
>                             struct hmap *ports)
>  {
>      hmap_init(pgs);
>
>      const struct nbrec_port_group *nb_pg;
> -    NBREC_PORT_GROUP_FOR_EACH (nb_pg, ctx->ovnnb_idl) {
> +    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg,
> +                                  data->input.nbrec_port_group_table) {
>          struct ovn_port_group *pg = ovn_port_group_create(pgs, nb_pg);
>          for (size_t i = 0; i < nb_pg->n_ports; i++) {
>              struct ovn_port *op = ovn_port_find(ports,
nb_pg->ports[i]->name);
> @@ -8242,12 +8267,12 @@ bfd_port_lookup(struct hmap *bfd_map, const char
*logical_port,
>  }
>
>  static void
> -bfd_cleanup_connections(struct northd_context *ctx, struct hmap *bfd_map)
> +bfd_cleanup_connections(struct northd_data *data, struct hmap *bfd_map)
>  {
>      const struct nbrec_bfd *nb_bt;
>      struct bfd_entry *bfd_e;
>
> -    NBREC_BFD_FOR_EACH (nb_bt, ctx->ovnnb_idl) {
> +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, data->input.nbrec_bfd_table) {
>          bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port,
nb_bt->dst_ip);
>          if (!bfd_e) {
>              continue;
> @@ -8325,8 +8350,8 @@ static int bfd_get_unused_port(unsigned long
*bfd_src_ports)
>  }
>
>  static void
> -build_bfd_table(struct northd_context *ctx, struct hmap *bfd_connections,
> -                struct hmap *ports)
> +build_bfd_table(struct northd_data *data, struct ovsdb_idl_txn
*ovnsb_txn,
> +                struct hmap *bfd_connections, struct hmap *ports)
>  {
>      struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
>      const struct sbrec_bfd *sb_bt;
> @@ -8336,7 +8361,7 @@ build_bfd_table(struct northd_context *ctx, struct
hmap *bfd_connections,
>
>      bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
>
> -    SBREC_BFD_FOR_EACH (sb_bt, ctx->ovnsb_idl) {
> +    SBREC_BFD_TABLE_FOR_EACH (sb_bt, data->input.sbrec_bfd_table) {
>          bfd_e = xmalloc(sizeof *bfd_e);
>          bfd_e->sb_bt = sb_bt;
>          hash = hash_string(sb_bt->dst_ip, 0);
> @@ -8346,7 +8371,7 @@ build_bfd_table(struct northd_context *ctx, struct
hmap *bfd_connections,
>      }
>
>      const struct nbrec_bfd *nb_bt;
> -    NBREC_BFD_FOR_EACH (nb_bt, ctx->ovnnb_idl) {
> +    NBREC_BFD_TABLE_FOR_EACH (nb_bt, data->input.nbrec_bfd_table) {
>          if (!nb_bt->status) {
>              /* default state is admin_down */
>              nbrec_bfd_set_status(nb_bt, "admin_down");
> @@ -8359,7 +8384,7 @@ build_bfd_table(struct northd_context *ctx, struct
hmap *bfd_connections,
>                  continue;
>              }
>
> -            sb_bt = sbrec_bfd_insert(ctx->ovnsb_txn);
> +            sb_bt = sbrec_bfd_insert(ovnsb_txn);
>              sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
>              sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
>              sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
> @@ -13308,8 +13333,8 @@ ovn_dp_group_find(const struct hmap *dp_groups,
>  }
>
>  static struct sbrec_logical_dp_group *
> -ovn_sb_insert_logical_dp_group(struct northd_context *ctx,
> -                                     const struct hmapx *od)
> +ovn_sb_insert_logical_dp_group(struct ovsdb_idl_txn *ovnsb_txn,
> +                               const struct hmapx *od)
>  {
>      struct sbrec_logical_dp_group *dp_group;
>      const struct sbrec_datapath_binding **sb;
> @@ -13320,7 +13345,7 @@ ovn_sb_insert_logical_dp_group(struct
northd_context *ctx,
>      HMAPX_FOR_EACH (node, od) {
>          sb[n++] = ((struct ovn_datapath *) node->data)->sb;
>      }
> -    dp_group = sbrec_logical_dp_group_insert(ctx->ovnsb_txn);
> +    dp_group = sbrec_logical_dp_group_insert(ovnsb_txn);
>      sbrec_logical_dp_group_set_datapaths(
>          dp_group, (struct sbrec_datapath_binding **) sb, n);
>      free(sb);
> @@ -13330,7 +13355,7 @@ ovn_sb_insert_logical_dp_group(struct
northd_context *ctx,
>
>  static void
>  ovn_sb_set_lflow_logical_dp_group(
> -    struct northd_context *ctx,
> +    struct ovsdb_idl_txn *ovnsb_txn,
>      struct hmap *dp_groups,
>      const struct sbrec_logical_flow *sbflow,
>      const struct hmapx *od_group)
> @@ -13349,7 +13374,7 @@ ovn_sb_set_lflow_logical_dp_group(
>      ovs_assert(dpg != NULL);
>
>      if (!dpg->dp_group) {
> -        dpg->dp_group = ovn_sb_insert_logical_dp_group(ctx, &dpg->map);
> +        dpg->dp_group = ovn_sb_insert_logical_dp_group(ovnsb_txn,
&dpg->map);
>      }
>      sbrec_logical_flow_set_logical_dp_group(sbflow, dpg->dp_group);
>  }
> @@ -13360,13 +13385,7 @@ static bool reset_parallel = false;
>
>  /* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB
database,
>   * constructing their contents based on the OVN_NB database. */
> -static void
> -build_lflows(struct northd_context *ctx, struct hmap *datapaths,
> -             struct hmap *ports, struct hmap *port_groups,
> -             struct hmap *mcgroups, struct hmap *igmp_groups,
> -             struct shash *meter_groups,
> -             struct hmap *lbs, struct hmap *bfd_connections,
> -             bool ovn_internal_version_changed)
> +void build_lflows(struct northd_data *data, struct ovsdb_idl_txn
*ovnsb_txn)
>  {
>      struct hmap lflows;
>
> @@ -13388,10 +13407,11 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>          use_parallel_build = false;
>          reset_parallel = true;
>      }
> -    build_lswitch_and_lrouter_flows(datapaths, ports,
> -                                    port_groups, &lflows, mcgroups,
> -                                    igmp_groups, meter_groups, lbs,
> -                                    bfd_connections);
> +    build_lswitch_and_lrouter_flows(&data->datapaths, &data->ports,
> +                                    &data->port_groups, &lflows,
> +                                    &data->mcast_groups,
&data->igmp_groups,
> +                                    &data->meter_groups, &data->lbs,
> +                                    &data->bfd_connections);
>
>      /* Parallel build may result in a suboptimal hash. Resize the
>       * hash to a correct size before doing lookups */
> @@ -13461,7 +13481,8 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>
>      /* Push changes to the Logical_Flow table to database. */
>      const struct sbrec_logical_flow *sbflow, *next_sbflow;
> -    SBREC_LOGICAL_FLOW_FOR_EACH_SAFE (sbflow, next_sbflow,
ctx->ovnsb_idl) {
> +    SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_SAFE (sbflow, next_sbflow,
> +
data->input.sbrec_logical_flow_table) {
>          struct sbrec_logical_dp_group *dp_group =
sbflow->logical_dp_group;
>          struct ovn_datapath *logical_datapath_od = NULL;
>          size_t i;
> @@ -13469,7 +13490,8 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>          /* Find one valid datapath to get the datapath type. */
>          struct sbrec_datapath_binding *dp = sbflow->logical_datapath;
>          if (dp) {
> -            logical_datapath_od = ovn_datapath_from_sbrec(datapaths, dp);
> +            logical_datapath_od = ovn_datapath_from_sbrec(
> +                                            &data->datapaths, dp);
>              if (logical_datapath_od
>                  && ovn_datapath_is_stale(logical_datapath_od)) {
>                  logical_datapath_od = NULL;
> @@ -13477,7 +13499,7 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>          }
>          for (i = 0; dp_group && i < dp_group->n_datapaths; i++) {
>              logical_datapath_od = ovn_datapath_from_sbrec(
> -                                      datapaths, dp_group->datapaths[i]);
> +                                    &data->datapaths,
dp_group->datapaths[i]);
>              if (logical_datapath_od
>                  && !ovn_datapath_is_stale(logical_datapath_od)) {
>                  break;
> @@ -13501,7 +13523,7 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>              sbflow->priority, sbflow->match, sbflow->actions,
>              sbflow->controller_meter, sbflow->hash);
>          if (lflow) {
> -            if (ovn_internal_version_changed) {
> +            if (data->ovn_internal_version_changed) {
>                  const char *stage_name =
smap_get_def(&sbflow->external_ids,
>                                                    "stage-name", "");
>                  const char *stage_hint =
smap_get_def(&sbflow->external_ids,
> @@ -13553,7 +13575,7 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>                  /* Check all logical datapaths from the group. */
>                  for (i = 0; i < dp_group->n_datapaths; i++) {
>                      od[n_datapaths] = ovn_datapath_from_sbrec(
> -                                        datapaths,
dp_group->datapaths[i]);
> +                                    &data->datapaths,
dp_group->datapaths[i]);
>                      if (!od[n_datapaths]
>                          || ovn_datapath_is_stale(od[n_datapaths])) {
>                          continue;
> @@ -13574,7 +13596,7 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>              }
>
>              if (update_dp_group) {
> -                ovn_sb_set_lflow_logical_dp_group(ctx, &dp_groups,
> +                ovn_sb_set_lflow_logical_dp_group(ovnsb_txn, &dp_groups,
>                                                    sbflow,
&lflow->od_group);
>              } else if (lflow->dpg && !lflow->dpg->dp_group) {
>                  /* Setting relation between unique datapath group and
> @@ -13594,11 +13616,11 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>          const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
>          uint8_t table = ovn_stage_get_table(lflow->stage);
>
> -        sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
> +        sbflow = sbrec_logical_flow_insert(ovnsb_txn);
>          if (lflow->od) {
>              sbrec_logical_flow_set_logical_datapath(sbflow,
lflow->od->sb);
>          }
> -        ovn_sb_set_lflow_logical_dp_group(ctx, &dp_groups,
> +        ovn_sb_set_lflow_logical_dp_group(ovnsb_txn, &dp_groups,
>                                            sbflow, &lflow->od_group);
>          sbrec_logical_flow_set_pipeline(sbflow, pipeline);
>          sbrec_logical_flow_set_table_id(sbflow, table);
> @@ -13647,8 +13669,9 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>
>      /* Push changes to the Multicast_Group table to database. */
>      const struct sbrec_multicast_group *sbmc, *next_sbmc;
> -    SBREC_MULTICAST_GROUP_FOR_EACH_SAFE (sbmc, next_sbmc,
ctx->ovnsb_idl) {
> -        struct ovn_datapath *od = ovn_datapath_from_sbrec(datapaths,
> +    SBREC_MULTICAST_GROUP_TABLE_FOR_EACH_SAFE (sbmc, next_sbmc,
> +                                data->input.sbrec_multicast_group_table)
{
> +        struct ovn_datapath *od =
ovn_datapath_from_sbrec(&data->datapaths,
>
 sbmc->datapath);
>
>          if (!od || ovn_datapath_is_stale(od)) {
> @@ -13658,31 +13681,32 @@ build_lflows(struct northd_context *ctx, struct
hmap *datapaths,
>
>          struct multicast_group group = { .name = sbmc->name,
>                                           .key = sbmc->tunnel_key };
> -        struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od,
&group);
> +        struct ovn_multicast *mc =
ovn_multicast_find(&data->mcast_groups,
> +                                                      od, &group);
>          if (mc) {
>              ovn_multicast_update_sbrec(mc, sbmc);
> -            ovn_multicast_destroy(mcgroups, mc);
> +            ovn_multicast_destroy(&data->mcast_groups, mc);
>          } else {
>              sbrec_multicast_group_delete(sbmc);
>          }
>      }
>      struct ovn_multicast *mc, *next_mc;
> -    HMAP_FOR_EACH_SAFE (mc, next_mc, hmap_node, mcgroups) {
> +    HMAP_FOR_EACH_SAFE (mc, next_mc, hmap_node, &data->mcast_groups) {
>          if (!mc->datapath) {
> -            ovn_multicast_destroy(mcgroups, mc);
> +            ovn_multicast_destroy(&data->mcast_groups, mc);
>              continue;
>          }
> -        sbmc = sbrec_multicast_group_insert(ctx->ovnsb_txn);
> +        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);
>          ovn_multicast_update_sbrec(mc, sbmc);
> -        ovn_multicast_destroy(mcgroups, mc);
> +        ovn_multicast_destroy(&data->mcast_groups, mc);
>      }
>  }
>
>  static void
> -sync_address_set(struct northd_context *ctx, const char *name,
> +sync_address_set(struct ovsdb_idl_txn *ovnsb_txn, const char *name,
>                   const char **addrs, size_t n_addrs,
>                   struct shash *sb_address_sets)
>  {
> @@ -13690,7 +13714,7 @@ sync_address_set(struct northd_context *ctx,
const char *name,
>      sb_address_set = shash_find_and_delete(sb_address_sets,
>                                             name);
>      if (!sb_address_set) {
> -        sb_address_set = sbrec_address_set_insert(ctx->ovnsb_txn);
> +        sb_address_set = sbrec_address_set_insert(ovnsb_txn);
>          sbrec_address_set_set_name(sb_address_set, name);
>      }
>
> @@ -13709,23 +13733,26 @@ sync_address_set(struct northd_context *ctx,
const char *name,
>   * in OVN_Northbound, so that the address sets used in Logical_Flows in
>   * OVN_Southbound is checked against the proper set.*/
>  static void
> -sync_address_sets(struct northd_context *ctx, struct hmap *datapaths)
> +sync_address_sets(struct northd_data *data, struct ovsdb_idl_txn
*ovnsb_txn,
> +                  struct hmap *datapaths)
>  {
>      struct shash sb_address_sets = SHASH_INITIALIZER(&sb_address_sets);
>
>      const struct sbrec_address_set *sb_address_set;
> -    SBREC_ADDRESS_SET_FOR_EACH (sb_address_set, ctx->ovnsb_idl) {
> +    SBREC_ADDRESS_SET_TABLE_FOR_EACH (sb_address_set,
> +                                   data->input.sbrec_address_set_table) {
>          shash_add(&sb_address_sets, sb_address_set->name,
sb_address_set);
>      }
>
>      /* Service monitor MAC. */
>      const char *svc_monitor_macp = svc_monitor_mac;
> -    sync_address_set(ctx, "svc_monitor_mac", &svc_monitor_macp, 1,
> +    sync_address_set(ovnsb_txn, "svc_monitor_mac", &svc_monitor_macp, 1,
>                       &sb_address_sets);
>
>      /* sync port group generated address sets first */
>      const struct nbrec_port_group *nb_port_group;
> -    NBREC_PORT_GROUP_FOR_EACH (nb_port_group, ctx->ovnnb_idl) {
> +    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_port_group,
> +                                     data->input.nbrec_port_group_table)
{
>          struct svec ipv4_addrs = SVEC_EMPTY_INITIALIZER;
>          struct svec ipv6_addrs = SVEC_EMPTY_INITIALIZER;
>          for (size_t i = 0; i < nb_port_group->n_ports; i++) {
> @@ -13742,11 +13769,11 @@ sync_address_sets(struct northd_context *ctx,
struct hmap *datapaths)
>          }
>          char *ipv4_addrs_name = xasprintf("%s_ip4", nb_port_group->name);
>          char *ipv6_addrs_name = xasprintf("%s_ip6", nb_port_group->name);
> -        sync_address_set(ctx, ipv4_addrs_name,
> +        sync_address_set(ovnsb_txn, ipv4_addrs_name,
>                           /* "char **" is not compatible with "const char
**" */
>                           (const char **)ipv4_addrs.names,
>                           ipv4_addrs.n, &sb_address_sets);
> -        sync_address_set(ctx, ipv6_addrs_name,
> +        sync_address_set(ovnsb_txn, ipv6_addrs_name,
>                           /* "char **" is not compatible with "const char
**" */
>                           (const char **)ipv6_addrs.names,
>                           ipv6_addrs.n, &sb_address_sets);
> @@ -13767,7 +13794,7 @@ sync_address_sets(struct northd_context *ctx,
struct hmap *datapaths)
>              char *ipv4_addrs_name = lr_lb_address_set_name(od, AF_INET);
>              const char **ipv4_addrs = sset_array(&od->lb_ips_v4);
>
> -            sync_address_set(ctx, ipv4_addrs_name, ipv4_addrs,
> +            sync_address_set(ovnsb_txn, ipv4_addrs_name, ipv4_addrs,
>                               sset_count(&od->lb_ips_v4),
&sb_address_sets);
>              free(ipv4_addrs_name);
>              free(ipv4_addrs);
> @@ -13777,7 +13804,7 @@ sync_address_sets(struct northd_context *ctx,
struct hmap *datapaths)
>              char *ipv6_addrs_name = lr_lb_address_set_name(od, AF_INET6);
>              const char **ipv6_addrs = sset_array(&od->lb_ips_v6);
>
> -            sync_address_set(ctx, ipv6_addrs_name, ipv6_addrs,
> +            sync_address_set(ovnsb_txn, ipv6_addrs_name, ipv6_addrs,
>                               sset_count(&od->lb_ips_v6),
&sb_address_sets);
>              free(ipv6_addrs_name);
>              free(ipv6_addrs);
> @@ -13787,8 +13814,9 @@ sync_address_sets(struct northd_context *ctx,
struct hmap *datapaths)
>      /* sync user defined address sets, which may overwrite port group
>       * generated address sets if same name is used */
>      const struct nbrec_address_set *nb_address_set;
> -    NBREC_ADDRESS_SET_FOR_EACH (nb_address_set, ctx->ovnnb_idl) {
> -        sync_address_set(ctx, nb_address_set->name,
> +    NBREC_ADDRESS_SET_TABLE_FOR_EACH (nb_address_set,
> +                              data->input.nbrec_address_set_table) {
> +        sync_address_set(ovnsb_txn, nb_address_set->name,
>              /* "char **" is not compatible with "const char **" */
>              (const char **)nb_address_set->addresses,
>              nb_address_set->n_addresses, &sb_address_sets);
> @@ -13807,12 +13835,14 @@ sync_address_sets(struct northd_context *ctx,
struct hmap *datapaths)
>   * contains lport uuids, while in OVN_Southbound we store the lport
names.
>   */
>  static void
> -sync_port_groups(struct northd_context *ctx, struct hmap *pgs)
> +sync_port_groups(struct northd_data *data, struct ovsdb_idl_txn
*ovnsb_txn,
> +                 struct hmap *pgs)
>  {
>      struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
>
>      const struct sbrec_port_group *sb_port_group;
> -    SBREC_PORT_GROUP_FOR_EACH (sb_port_group, ctx->ovnsb_idl) {
> +    SBREC_PORT_GROUP_TABLE_FOR_EACH (sb_port_group,
> +                               data->input.sbrec_port_group_table) {
>          shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
>      }
>
> @@ -13828,7 +13858,7 @@ sync_port_groups(struct northd_context *ctx,
struct hmap *pgs)
>              sb_port_group = shash_find_and_delete(&sb_port_groups,
>                                                    ds_cstr(&sb_name));
>              if (!sb_port_group) {
> -                sb_port_group = sbrec_port_group_insert(ctx->ovnsb_txn);
> +                sb_port_group = sbrec_port_group_insert(ovnsb_txn);
>                  sbrec_port_group_set_name(sb_port_group,
ds_cstr(&sb_name));
>              }
>
> @@ -13935,7 +13965,7 @@ done:
>  }
>
>  static void
> -sync_meters_iterate_nb_meter(struct northd_context *ctx,
> +sync_meters_iterate_nb_meter(struct ovsdb_idl_txn *ovnsb_txn,
>                               const char *meter_name,
>                               const struct nbrec_meter *nb_meter,
>                               struct shash *sb_meters,
> @@ -13946,7 +13976,7 @@ sync_meters_iterate_nb_meter(struct
northd_context *ctx,
>
>      sb_meter = shash_find_data(sb_meters, meter_name);
>      if (!sb_meter) {
> -        sb_meter = sbrec_meter_insert(ctx->ovnsb_txn);
> +        sb_meter = sbrec_meter_insert(ovnsb_txn);
>          sbrec_meter_set_name(sb_meter, meter_name);
>          shash_add(sb_meters, sb_meter->name, sb_meter);
>          new_sb_meter = true;
> @@ -13959,7 +13989,7 @@ sync_meters_iterate_nb_meter(struct
northd_context *ctx,
>          for (size_t i = 0; i < nb_meter->n_bands; i++) {
>              const struct nbrec_meter_band *nb_band = nb_meter->bands[i];
>
> -            sb_bands[i] = sbrec_meter_band_insert(ctx->ovnsb_txn);
> +            sb_bands[i] = sbrec_meter_band_insert(ovnsb_txn);
>
>              sbrec_meter_band_set_action(sb_bands[i], nb_band->action);
>              sbrec_meter_band_set_rate(sb_bands[i], nb_band->rate);
> @@ -13974,7 +14004,8 @@ sync_meters_iterate_nb_meter(struct
northd_context *ctx,
>  }
>
>  static void
> -sync_acl_fair_meter(struct northd_context *ctx, struct shash
*meter_groups,
> +sync_acl_fair_meter(struct ovsdb_idl_txn *ovnsb_txn,
> +                    struct shash *meter_groups,
>                      const struct nbrec_acl *acl, struct shash *sb_meters,
>                      struct sset *used_sb_meters)
>  {
> @@ -13986,7 +14017,7 @@ sync_acl_fair_meter(struct northd_context *ctx,
struct shash *meter_groups,
>      }
>
>      char *meter_name = alloc_acl_log_unique_meter_name(acl);
> -    sync_meters_iterate_nb_meter(ctx, meter_name, nb_meter, sb_meters,
> +    sync_meters_iterate_nb_meter(ovnsb_txn, meter_name, nb_meter,
sb_meters,
>                                   used_sb_meters);
>      free(meter_name);
>  }
> @@ -13997,19 +14028,20 @@ sync_acl_fair_meter(struct northd_context *ctx,
struct shash *meter_groups,
>   * a private copy of its meter in the SB table.
>   */
>  static void
> -sync_meters(struct northd_context *ctx, struct shash *meter_groups)
> +sync_meters(struct northd_data *data, struct ovsdb_idl_txn *ovnsb_txn,
> +            struct shash *meter_groups)
>  {
>      struct shash sb_meters = SHASH_INITIALIZER(&sb_meters);
>      struct sset used_sb_meters = SSET_INITIALIZER(&used_sb_meters);
>
>      const struct sbrec_meter *sb_meter;
> -    SBREC_METER_FOR_EACH (sb_meter, ctx->ovnsb_idl) {
> +    SBREC_METER_TABLE_FOR_EACH (sb_meter, data->input.sbrec_meter_table)
{
>          shash_add(&sb_meters, sb_meter->name, sb_meter);
>      }
>
>      const struct nbrec_meter *nb_meter;
> -    NBREC_METER_FOR_EACH (nb_meter, ctx->ovnnb_idl) {
> -        sync_meters_iterate_nb_meter(ctx, nb_meter->name, nb_meter,
> +    NBREC_METER_TABLE_FOR_EACH (nb_meter, data->input.nbrec_meter_table)
{
> +        sync_meters_iterate_nb_meter(ovnsb_txn, nb_meter->name, nb_meter,
>                                       &sb_meters, &used_sb_meters);
>      }
>
> @@ -14019,8 +14051,8 @@ sync_meters(struct northd_context *ctx, struct
shash *meter_groups)
>       * rate-limited.
>       */
>      const struct nbrec_acl *acl;
> -    NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
> -        sync_acl_fair_meter(ctx, meter_groups, acl,
> +    NBREC_ACL_TABLE_FOR_EACH (acl, data->input.nbrec_acl_table) {
> +        sync_acl_fair_meter(ovnsb_txn, meter_groups, acl,
>                              &sb_meters, &used_sb_meters);
>      }
>
> @@ -14069,7 +14101,8 @@ get_dns_info_from_hmap(struct hmap *dns_map,
struct uuid *uuid)
>  }
>
>  static void
> -sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
> +sync_dns_entries(struct northd_data *data, struct ovsdb_idl_txn
*ovnsb_txn,
> +                 struct hmap *datapaths)
>  {
>      struct hmap dns_map = HMAP_INITIALIZER(&dns_map);
>      struct ovn_datapath *od;
> @@ -14097,7 +14130,8 @@ sync_dns_entries(struct northd_context *ctx,
struct hmap *datapaths)
>      }
>
>      const struct sbrec_dns *sbrec_dns, *next;
> -    SBREC_DNS_FOR_EACH_SAFE (sbrec_dns, next, ctx->ovnsb_idl) {
> +    SBREC_DNS_TABLE_FOR_EACH_SAFE (sbrec_dns, next,
> +                                   data->input.sbrec_dns_table) {
>          const char *nb_dns_uuid = smap_get(&sbrec_dns->external_ids,
"dns_id");
>          struct uuid dns_uuid;
>          if (!nb_dns_uuid || !uuid_from_string(&dns_uuid, nb_dns_uuid)) {
> @@ -14117,7 +14151,7 @@ sync_dns_entries(struct northd_context *ctx,
struct hmap *datapaths)
>      struct dns_info *dns_info;
>      HMAP_FOR_EACH_POP (dns_info, hmap_node, &dns_map) {
>          if (!dns_info->sb_dns) {
> -            sbrec_dns = sbrec_dns_insert(ctx->ovnsb_txn);
> +            sbrec_dns = sbrec_dns_insert(ovnsb_txn);
>              dns_info->sb_dns = sbrec_dns;
>              char *dns_id = xasprintf(
>                  UUID_FMT, UUID_ARGS(&dns_info->nb_dns->header_.uuid));
> @@ -14187,7 +14221,8 @@ destroy_datapaths_and_ports(struct hmap
*datapaths, struct hmap *ports,
>  }
>
>  static void
> -build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths)
> +build_ip_mcast(struct northd_data *data, struct ovsdb_idl_txn *ovnsb_txn,
> +               struct hmap *datapaths)
>  {
>      struct ovn_datapath *od;
>
> @@ -14197,10 +14232,10 @@ build_ip_mcast(struct northd_context *ctx,
struct hmap *datapaths)
>          }
>
>          const struct sbrec_ip_multicast *ip_mcast =
> -            ip_mcast_lookup(ctx->sbrec_ip_mcast_by_dp, od->sb);
> +            ip_mcast_lookup(data->input.sbrec_ip_mcast_by_dp, od->sb);
>
>          if (!ip_mcast) {
> -            ip_mcast = sbrec_ip_multicast_insert(ctx->ovnsb_txn);
> +            ip_mcast = sbrec_ip_multicast_insert(ovnsb_txn);
>          }
>          store_mcast_info_for_switch_datapath(ip_mcast, od);
>      }
> @@ -14208,7 +14243,8 @@ build_ip_mcast(struct northd_context *ctx, struct
hmap *datapaths)
>      /* Delete southbound records without northbound matches. */
>      const struct sbrec_ip_multicast *sb, *sb_next;
>
> -    SBREC_IP_MULTICAST_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
> +    SBREC_IP_MULTICAST_TABLE_FOR_EACH_SAFE (sb, sb_next,
> +                                   data->input.sbrec_ip_multicast_table)
{
>          od = ovn_datapath_from_sbrec(datapaths, sb->datapath);
>          if (!od || ovn_datapath_is_stale(od)) {
>              sbrec_ip_multicast_delete(sb);
> @@ -14217,7 +14253,7 @@ build_ip_mcast(struct northd_context *ctx, struct
hmap *datapaths)
>  }
>
>  static void
> -build_mcast_groups(struct northd_context *ctx,
> +build_mcast_groups(struct northd_data *data,
>                     struct hmap *datapaths, struct hmap *ports,
>                     struct hmap *mcast_groups,
>                     struct hmap *igmp_groups)
> @@ -14271,7 +14307,8 @@ build_mcast_groups(struct northd_context *ctx,
>
>      const struct sbrec_igmp_group *sb_igmp, *sb_igmp_next;
>
> -    SBREC_IGMP_GROUP_FOR_EACH_SAFE (sb_igmp, sb_igmp_next,
ctx->ovnsb_idl) {
> +    SBREC_IGMP_GROUP_TABLE_FOR_EACH_SAFE (sb_igmp, sb_igmp_next,
> +                                     data->input.sbrec_igmp_group_table)
{
>          /* If this is a stale group (e.g., controller had crashed,
>           * purge it).
>           */
> @@ -14313,7 +14350,7 @@ build_mcast_groups(struct northd_context *ctx,
>           * if the multicast group already exists.
>           */
>          struct ovn_igmp_group *igmp_group =
> -            ovn_igmp_group_add(ctx, igmp_groups, od, &group_address,
> +            ovn_igmp_group_add(data, igmp_groups, od, &group_address,
>                                 sb_igmp->address);
>
>          /* Add the extracted ports to the IGMP group. */
> @@ -14357,7 +14394,7 @@ build_mcast_groups(struct northd_context *ctx,
>                  }
>
>                  struct ovn_igmp_group *igmp_group_rtr =
> -                    ovn_igmp_group_add(ctx, igmp_groups, router_port->od,
> +                    ovn_igmp_group_add(data, igmp_groups,
router_port->od,
>                                         address, igmp_group->mcgroup.name
);
>                  struct ovn_port **router_igmp_ports =
>                      xmalloc(sizeof *router_igmp_ports);
> @@ -14392,47 +14429,101 @@ build_mcast_groups(struct northd_context *ctx,
>  }
>
>  static void
> -build_meter_groups(struct northd_context *ctx,
> +build_meter_groups(struct northd_data *data,
>                     struct shash *meter_groups)
>  {
>      const struct nbrec_meter *nb_meter;
> -    NBREC_METER_FOR_EACH (nb_meter, ctx->ovnnb_idl) {
> +    NBREC_METER_TABLE_FOR_EACH (nb_meter, data->input.nbrec_meter_table)
{
>          shash_add(meter_groups, nb_meter->name, nb_meter);
>      }
>  }
>
> +void
> +northd_init(struct northd_data *data)
> +{
> +    hmap_init(&data->datapaths);
> +    hmap_init(&data->ports);
> +    hmap_init(&data->port_groups);
> +    hmap_init(&data->mcast_groups);
> +    hmap_init(&data->igmp_groups);
> +    shash_init(&data->meter_groups);
> +    hmap_init(&data->lbs);
> +    hmap_init(&data->bfd_connections);
> +    ovs_list_init(&data->lr_list);
> +    data->ovn_internal_version_changed = false;
> +}
> +
> +void
> +northd_destroy(struct northd_data *data)
> +{
> +    struct ovn_northd_lb *lb;
> +    HMAP_FOR_EACH_POP (lb, hmap_node, &data->lbs) {
> +        ovn_northd_lb_destroy(lb);
> +    }
> +    hmap_destroy(&data->lbs);
> +
> +    struct ovn_igmp_group *igmp_group, *next_igmp_group;
> +
> +    HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node,
> +                        &data->igmp_groups) {
> +        ovn_igmp_group_destroy(&data->igmp_groups, igmp_group);
> +    }
> +
> +    struct ovn_port_group *pg, *next_pg;
> +    HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &data->port_groups) {
> +        ovn_port_group_destroy(&data->port_groups, pg);
> +    }
> +
> +    hmap_destroy(&data->igmp_groups);
> +    hmap_destroy(&data->mcast_groups);
> +    hmap_destroy(&data->port_groups);
> +    hmap_destroy(&data->bfd_connections);
> +
> +    struct shash_node *node, *next;
> +    SHASH_FOR_EACH_SAFE (node, next, &data->meter_groups) {
> +        shash_delete(&data->meter_groups, node);
> +    }
> +    shash_destroy(&data->meter_groups);
> +
> +    /* XXX Having to explicitly clean up macam here
> +     * is a bit strange. We don't explicitly initialize
> +     * macam in this module, but this is the logical place
> +     * to clean it up. Ideally, more IPAM logic can be factored
> +     * out of ovn-northd and this can be taken care of there
> +     * as well.
> +     */
> +    cleanup_macam();
> +
> +    destroy_datapaths_and_ports(&data->datapaths, &data->ports,
> +                                &data->lr_list);
> +}
> +
>  static void
> -ovnnb_db_run(struct northd_context *ctx,
> +ovnnb_db_run(struct northd_data *data,
> +             struct ovsdb_idl_txn *ovnnb_txn,
> +             struct ovsdb_idl_txn *ovnsb_txn,
>               struct ovsdb_idl_index *sbrec_chassis_by_name,
>               struct ovsdb_idl_index *sbrec_chassis_by_hostname,
>               struct ovsdb_idl_loop *sb_loop,
> -             struct hmap *datapaths, struct hmap *ports,
> -             struct ovs_list *lr_list,
> -             int64_t loop_start_time,
> -             const char *ovn_internal_version)
> +             int64_t loop_start_time)
>  {
> -    if (!ctx->ovnsb_txn || !ctx->ovnnb_txn) {
> +    if (!ovnsb_txn || !ovnnb_txn) {
>          return;
>      }
>      stopwatch_start(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
> -    struct hmap port_groups;
> -    struct hmap mcast_groups;
> -    struct hmap igmp_groups;
> -    struct shash meter_groups = SHASH_INITIALIZER(&meter_groups);
> -    struct hmap lbs;
> -    struct hmap bfd_connections = HMAP_INITIALIZER(&bfd_connections);
> -    bool ovn_internal_version_changed = true;
>
>      /* Sync ipsec configuration.
>       * Copy nb_cfg from northbound to southbound database.
>       * Also set up to update sb_cfg once our southbound transaction
commits. */
> -    const struct nbrec_nb_global *nb =
nbrec_nb_global_first(ctx->ovnnb_idl);
> +    const struct nbrec_nb_global *nb = nbrec_nb_global_table_first(
> +
data->input.nbrec_nb_global_table);
>      if (!nb) {
> -        nb = nbrec_nb_global_insert(ctx->ovnnb_txn);
> +        nb = nbrec_nb_global_insert(ovnnb_txn);
>      }
> -    const struct sbrec_sb_global *sb =
sbrec_sb_global_first(ctx->ovnsb_idl);
> +    const struct sbrec_sb_global *sb = sbrec_sb_global_table_first(
> +
data->input.sbrec_sb_global_table);
>      if (!sb) {
> -        sb = sbrec_sb_global_insert(ctx->ovnsb_txn);
> +        sb = sbrec_sb_global_insert(ovnsb_txn);
>      }
>      if (nb->ipsec != sb->ipsec) {
>          sbrec_sb_global_set_ipsec(sb, nb->ipsec);
> @@ -14469,17 +14560,19 @@ ovnnb_db_run(struct northd_context *ctx,
>          smap_replace(&options, "svc_monitor_mac", svc_monitor_mac);
>      }
>
> -    char *max_tunid = xasprintf("%d", get_ovn_max_dp_key_local(ctx));
> +    char *max_tunid = xasprintf("%d", get_ovn_max_dp_key_local(data));
>      smap_replace(&options, "max_tunid", max_tunid);
>      free(max_tunid);
>
> +    char *ovn_internal_version = ovn_get_internal_version();
>      if (!strcmp(ovn_internal_version,
>                  smap_get_def(&options, "northd_internal_version", ""))) {
> -        ovn_internal_version_changed = false;
> +        data->ovn_internal_version_changed = false;
>      } else {
>          smap_replace(&options, "northd_internal_version",
>                       ovn_internal_version);
>      }
> +    free(ovn_internal_version);
>
>      if (!smap_equal(&nb->options, &options)) {
>          nbrec_nb_global_verify_options(nb);
> @@ -14503,73 +14596,35 @@ ovnnb_db_run(struct northd_context *ctx,
>      check_lsp_is_up = !smap_get_bool(&nb->options,
>                                       "ignore_lsp_down", true);
>
> -    build_datapaths(ctx, datapaths, lr_list);
> -    build_ovn_lbs(ctx, datapaths, &lbs);
> -    build_lrouter_lbs(datapaths, &lbs);
> -    build_ports(ctx, sbrec_chassis_by_name, sbrec_chassis_by_hostname,
> -                datapaths, ports);
> -    build_ovn_lr_lbs(datapaths, &lbs);
> -    build_ovn_lb_svcs(ctx, ports, &lbs);
> -    build_ipam(datapaths, ports);
> -    build_port_group_lswitches(ctx, &port_groups, ports);
> -    build_lrouter_groups(ports, lr_list);
> -    build_ip_mcast(ctx, datapaths);
> -    build_mcast_groups(ctx, datapaths, ports, &mcast_groups,
&igmp_groups);
> -    build_meter_groups(ctx, &meter_groups);
> -    build_bfd_table(ctx, &bfd_connections, ports);
> +    build_datapaths(data, ovnsb_txn, &data->datapaths, &data->lr_list);
> +    build_ovn_lbs(data, ovnsb_txn, &data->datapaths, &data->lbs);
> +    build_lrouter_lbs(&data->datapaths, &data->lbs);
> +    build_ports(data, ovnsb_txn, sbrec_chassis_by_name,
> +                sbrec_chassis_by_hostname,
> +                &data->datapaths, &data->ports);
> +    build_ovn_lr_lbs(&data->datapaths, &data->lbs);
> +    build_ovn_lb_svcs(data, ovnsb_txn, &data->ports, &data->lbs);
> +    build_ipam(&data->datapaths, &data->ports);
> +    build_port_group_lswitches(data, &data->port_groups, &data->ports);
> +    build_lrouter_groups(&data->ports, &data->lr_list);
> +    build_ip_mcast(data, ovnsb_txn, &data->datapaths);
> +    build_mcast_groups(data, &data->datapaths, &data->ports,
> +                       &data->mcast_groups, &data->igmp_groups);
> +    build_meter_groups(data, &data->meter_groups);
> +    build_bfd_table(data, ovnsb_txn, &data->bfd_connections,
&data->ports);
>      stopwatch_stop(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
>      stopwatch_start(BUILD_LFLOWS_STOPWATCH_NAME, time_msec());
> -    build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
> -                 &igmp_groups, &meter_groups, &lbs, &bfd_connections,
> -                 ovn_internal_version_changed);
> +    build_lflows(data, ovnsb_txn);
>      stopwatch_stop(BUILD_LFLOWS_STOPWATCH_NAME, time_msec());
>      stopwatch_start(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
> -    ovn_update_ipv6_prefix(ports);
> -
> -    sync_address_sets(ctx, datapaths);
> -    sync_port_groups(ctx, &port_groups);
> -    sync_meters(ctx, &meter_groups);
> -    sync_dns_entries(ctx, datapaths);
> -    cleanup_stale_fdp_entries(ctx, datapaths);
> -
> -    struct ovn_northd_lb *lb;
> -    HMAP_FOR_EACH_POP (lb, hmap_node, &lbs) {
> -        ovn_northd_lb_destroy(lb);
> -    }
> -    hmap_destroy(&lbs);
> -
> -    struct ovn_igmp_group *igmp_group, *next_igmp_group;
> -
> -    HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node,
&igmp_groups) {
> -        ovn_igmp_group_destroy(&igmp_groups, igmp_group);
> -    }
> -
> -    struct ovn_port_group *pg, *next_pg;
> -    HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &port_groups) {
> -        ovn_port_group_destroy(&port_groups, pg);
> -    }
> -
> -    bfd_cleanup_connections(ctx, &bfd_connections);
> -
> -    hmap_destroy(&igmp_groups);
> -    hmap_destroy(&mcast_groups);
> -    hmap_destroy(&port_groups);
> -    hmap_destroy(&bfd_connections);
> -
> -    struct shash_node *node, *next;
> -    SHASH_FOR_EACH_SAFE (node, next, &meter_groups) {
> -        shash_delete(&meter_groups, node);
> -    }
> -    shash_destroy(&meter_groups);
> -
> -    /* XXX Having to explicitly clean up macam here
> -     * is a bit strange. We don't explicitly initialize
> -     * macam in this module, but this is the logical place
> -     * to clean it up. Ideally, more IPAM logic can be factored
> -     * out of ovn-northd and this can be taken care of there
> -     * as well.
> -     */
> -    cleanup_macam();
> +    ovn_update_ipv6_prefix(&data->ports);
> +
> +    sync_address_sets(data, ovnsb_txn, &data->datapaths);
> +    sync_port_groups(data, ovnsb_txn, &data->port_groups);
> +    sync_meters(data, ovnsb_txn, &data->meter_groups);
> +    sync_dns_entries(data, ovnsb_txn, &data->datapaths);
> +    cleanup_stale_fdp_entries(data, &data->datapaths);
> +    bfd_cleanup_connections(data, &data->bfd_connections);
>      stopwatch_stop(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
>
>  }
> @@ -14614,7 +14669,7 @@ struct ha_chassis_group_node {
>  };
>
>  static void
> -update_sb_ha_group_ref_chassis(struct northd_context *ctx,
> +update_sb_ha_group_ref_chassis(struct northd_data *data,
>                                 struct shash *ha_ref_chassis_map)
>  {
>      struct hmap ha_ch_grps = HMAP_INITIALIZER(&ha_ch_grps);
> @@ -14622,7 +14677,8 @@ update_sb_ha_group_ref_chassis(struct
northd_context *ctx,
>
>      /* Initialize a set of all ha_chassis_groups in SB. */
>      const struct sbrec_ha_chassis_group *ha_ch_grp;
> -    SBREC_HA_CHASSIS_GROUP_FOR_EACH (ha_ch_grp, ctx->ovnsb_idl) {
> +    SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_ch_grp,
> +
 data->input.sbrec_ha_chassis_group_table) {
>          ha_ch_grp_node = xzalloc(sizeof *ha_ch_grp_node);
>          ha_ch_grp_node->ha_ch_grp = ha_ch_grp;
>          hmap_insert(&ha_ch_grps, &ha_ch_grp_node->hmap_node,
> @@ -14686,7 +14742,7 @@ update_sb_ha_group_ref_chassis(struct
northd_context *ctx,
>   *  - 'ref_chassis' of hagrp1.
>   */
>  static void
> -build_ha_chassis_group_ref_chassis(struct northd_context *ctx,
> +build_ha_chassis_group_ref_chassis(struct northd_data *data,
>                                     const struct sbrec_port_binding *sb,
>                                     struct ovn_port *op,
>                                     struct shash *ha_ref_chassis_map)
> @@ -14712,7 +14768,7 @@ build_ha_chassis_group_ref_chassis(struct
northd_context *ctx,
>      SSET_FOR_EACH (ha_group_name, &lr_group->ha_chassis_groups) {
>          const struct sbrec_ha_chassis_group *sb_ha_chassis_grp;
>          sb_ha_chassis_grp = ha_chassis_group_lookup_by_name(
> -            ctx->sbrec_ha_chassis_grp_by_name, ha_group_name);
> +            data->input.sbrec_ha_chassis_grp_by_name, ha_group_name);
>
>          if (sb_ha_chassis_grp) {
>              struct ha_ref_chassis_info *ref_ch_info =
> @@ -14727,14 +14783,17 @@ build_ha_chassis_group_ref_chassis(struct
northd_context *ctx,
>   * this column is not empty, it means we need to set the corresponding
logical
>   * port as 'up' in the northbound DB. */
>  static void
> -handle_port_binding_changes(struct northd_context *ctx, struct hmap
*ports,
> +handle_port_binding_changes(struct northd_data *data,
> +                            struct ovsdb_idl_txn *ovnsb_txn,
> +                            struct hmap *ports,
>                              struct shash *ha_ref_chassis_map)
>  {
>      const struct sbrec_port_binding *sb;
>      bool build_ha_chassis_ref = false;
> -    if (ctx->ovnsb_txn) {
> +    if (ovnsb_txn) {
>          const struct sbrec_ha_chassis_group *ha_ch_grp;
> -        SBREC_HA_CHASSIS_GROUP_FOR_EACH (ha_ch_grp, ctx->ovnsb_idl) {
> +        SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_ch_grp,
> +
 data->input.sbrec_ha_chassis_group_table) {
>              if (ha_ch_grp->n_ha_chassis > 1) {
>                  struct ha_ref_chassis_info *ref_ch_info =
>                      xzalloc(sizeof *ref_ch_info);
> @@ -14745,7 +14804,8 @@ handle_port_binding_changes(struct northd_context
*ctx, struct hmap *ports,
>          }
>      }
>
> -    SBREC_PORT_BINDING_FOR_EACH(sb, ctx->ovnsb_idl) {
> +    SBREC_PORT_BINDING_TABLE_FOR_EACH (sb,
> +
data->input.sbrec_port_binding_table) {
>          struct ovn_port *op = ovn_port_find(ports, sb->logical_port);
>
>          if (!op || !op->nbsp) {
> @@ -14770,10 +14830,10 @@ handle_port_binding_changes(struct
northd_context *ctx, struct hmap *ports,
>              nbrec_logical_switch_port_set_up(op->nbsp, &up, 1);
>          }
>
> -        if (build_ha_chassis_ref && ctx->ovnsb_txn && sb->chassis) {
> +        if (build_ha_chassis_ref && ovnsb_txn && sb->chassis) {
>              /* Check and add the chassis which has claimed this 'sb'
>               * to the ha chassis group's ref_chassis if required. */
> -            build_ha_chassis_group_ref_chassis(ctx, sb, op,
> +            build_ha_chassis_group_ref_chassis(data, sb, op,
>                                                 ha_ref_chassis_map);
>          }
>      }
> @@ -14781,12 +14841,13 @@ handle_port_binding_changes(struct
northd_context *ctx, struct hmap *ports,
>
>  /* Updates the sb_cfg and hv_cfg columns in the northbound NB_Global
table. */
>  static void
> -update_northbound_cfg(struct northd_context *ctx,
> +update_northbound_cfg(struct northd_data *data,
>                        struct ovsdb_idl_loop *sb_loop,
>                        int64_t loop_start_time)
>  {
>      /* Update northbound sb_cfg if appropriate. */
> -    const struct nbrec_nb_global *nbg =
nbrec_nb_global_first(ctx->ovnnb_idl);
> +    const struct nbrec_nb_global *nbg = nbrec_nb_global_table_first(
> +                               data->input.nbrec_nb_global_table);
>      int64_t sb_cfg = sb_loop->cur_cfg;
>      if (nbg && sb_cfg && nbg->sb_cfg != sb_cfg) {
>          nbrec_nb_global_set_sb_cfg(nbg, sb_cfg);
> @@ -14799,7 +14860,8 @@ update_northbound_cfg(struct northd_context *ctx,
>          const struct sbrec_chassis_private *chassis_priv;
>          int64_t hv_cfg = nbg->nb_cfg;
>          int64_t hv_cfg_ts = 0;
> -        SBREC_CHASSIS_PRIVATE_FOR_EACH (chassis_priv, ctx->ovnsb_idl) {
> +        SBREC_CHASSIS_PRIVATE_TABLE_FOR_EACH (chassis_priv,
> +
 data->input.sbrec_chassis_private_table) {
>              const struct sbrec_chassis *chassis = chassis_priv->chassis;
>              if (chassis) {
>                  if (smap_get_bool(&chassis->other_config,
> @@ -14833,45 +14895,44 @@ update_northbound_cfg(struct northd_context
*ctx,
>
>  /* Handle a fairly small set of changes in the southbound database. */
>  static void
> -ovnsb_db_run(struct northd_context *ctx,
> +ovnsb_db_run(struct northd_data *data,
> +             struct ovsdb_idl_txn *ovnnb_txn,
> +             struct ovsdb_idl_txn *ovnsb_txn,
>               struct ovsdb_idl_loop *sb_loop,
>               struct hmap *ports,
>               int64_t loop_start_time)
>  {
> -    if (!ctx->ovnnb_txn ||
!ovsdb_idl_has_ever_connected(ctx->ovnsb_idl)) {
> +    if (!ovnnb_txn ||
> +        !ovsdb_idl_has_ever_connected(ovsdb_idl_txn_get_idl(ovnsb_txn)))
{
>          return;
>      }
>
>      struct shash ha_ref_chassis_map =
SHASH_INITIALIZER(&ha_ref_chassis_map);
> -    handle_port_binding_changes(ctx, ports, &ha_ref_chassis_map);
> -    update_northbound_cfg(ctx, sb_loop, loop_start_time);
> -    if (ctx->ovnsb_txn) {
> -        update_sb_ha_group_ref_chassis(ctx, &ha_ref_chassis_map);
> +    handle_port_binding_changes(data, ovnsb_txn, ports,
&ha_ref_chassis_map);
> +    update_northbound_cfg(data, sb_loop, loop_start_time);
> +    if (ovnsb_txn) {
> +        update_sb_ha_group_ref_chassis(data, &ha_ref_chassis_map);
>      }
>      shash_destroy(&ha_ref_chassis_map);
>  }
>
>  void
> -ovn_db_run(struct northd_context *ctx)
> +northd_run(struct northd_data *data,
> +           struct ovsdb_idl_txn *ovnnb_txn,
> +           struct ovsdb_idl_txn *ovnsb_txn,
> +           struct ovsdb_idl_loop *sb_loop)
>  {
> -    struct hmap datapaths, ports;
> -    struct ovs_list lr_list;
> -    ovs_list_init(&lr_list);
> -    hmap_init(&datapaths);
> -    hmap_init(&ports);
> -    use_parallel_build = ctx->use_parallel_build;
> -
>      int64_t start_time = time_wall_msec();
>
>      stopwatch_start(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());
> -    ovnnb_db_run(ctx, ctx->sbrec_chassis_by_name,
> -                 ctx->sbrec_chassis_by_hostname, ctx->ovnsb_idl_loop,
> -                 &datapaths, &ports, &lr_list, start_time,
> -                 ctx->ovn_internal_version);
> +    ovnnb_db_run(data, ovnnb_txn, ovnsb_txn,
> +                 data->input.sbrec_chassis_by_name,
> +                 data->input.sbrec_chassis_by_hostname,
> +                 sb_loop, start_time);
>      stopwatch_stop(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());
>      stopwatch_start(OVNSB_DB_RUN_STOPWATCH_NAME, time_msec());
> -    ovnsb_db_run(ctx, ctx->ovnsb_idl_loop, &ports, start_time);
> +    ovnsb_db_run(data, ovnnb_txn, ovnsb_txn,
> +                 sb_loop, &data->ports, start_time);
>      stopwatch_stop(OVNSB_DB_RUN_STOPWATCH_NAME, time_msec());
> -    destroy_datapaths_and_ports(&datapaths, &ports, &lr_list);
>  }
>
> diff --git a/northd/northd.h b/northd/northd.h
> index 4ebbe60af39b..5ceb2c6c6216 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -16,25 +16,76 @@
>
>  #include "ovsdb-idl.h"
>
> -struct northd_context {
> -    const char *ovnnb_db;
> -    const char *ovnsb_db;
> -    struct ovsdb_idl *ovnnb_idl;
> -    struct ovsdb_idl *ovnsb_idl;
> -    struct ovsdb_idl_loop *ovnnb_idl_loop;
> -    struct ovsdb_idl_loop *ovnsb_idl_loop;
> -    struct ovsdb_idl_txn *ovnnb_txn;
> -    struct ovsdb_idl_txn *ovnsb_txn;
> +#include "openvswitch/hmap.h"
> +
> +struct northd_input {
> +    /* Northbound table references */
> +    const struct nbrec_nb_global_table *nbrec_nb_global_table;
> +    const struct nbrec_logical_switch_table *nbrec_logical_switch;
> +    const struct nbrec_logical_router_table *nbrec_logical_router;
> +    const struct nbrec_load_balancer_table *nbrec_load_balancer_table;
> +    const struct nbrec_port_group_table *nbrec_port_group_table;
> +    const struct nbrec_bfd_table *nbrec_bfd_table;
> +    const struct nbrec_address_set_table *nbrec_address_set_table;
> +    const struct nbrec_meter_table *nbrec_meter_table;
> +    const struct nbrec_acl_table *nbrec_acl_table;
> +
> +    /* Southbound table references */
> +    const struct sbrec_sb_global_table *sbrec_sb_global_table;
> +    const struct sbrec_datapath_binding_table
*sbrec_datapath_binding_table;
> +    const struct sbrec_port_binding_table *sbrec_port_binding_table;
> +    const struct sbrec_mac_binding_table *sbrec_mac_binding_table;
> +    const struct sbrec_ha_chassis_group_table
*sbrec_ha_chassis_group_table;
> +    const struct sbrec_chassis_table *sbrec_chassis;
> +    const struct sbrec_fdb_table *sbrec_fdb_table;
> +    const struct sbrec_load_balancer_table *sbrec_load_balancer_table;
> +    const struct sbrec_service_monitor_table
*sbrec_service_monitor_table;
> +    const struct sbrec_bfd_table *sbrec_bfd_table;
> +    const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
> +    const struct sbrec_multicast_group_table
*sbrec_multicast_group_table;
> +    const struct sbrec_address_set_table *sbrec_address_set_table;
> +    const struct sbrec_port_group_table *sbrec_port_group_table;
> +    const struct sbrec_meter_table *sbrec_meter_table;
> +    const struct sbrec_dns_table *sbrec_dns_table;
> +    const struct sbrec_ip_multicast_table *sbrec_ip_multicast_table;
> +    const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
> +    const struct sbrec_chassis_private_table
*sbrec_chassis_private_table;
> +
> +    /* Indexes */
>      struct ovsdb_idl_index *sbrec_chassis_by_name;
>      struct ovsdb_idl_index *sbrec_chassis_by_hostname;
>      struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name;
>      struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp;
>      struct ovsdb_idl_index *sbrec_ip_mcast_by_dp;
> +};
> +
> +struct northd_data {
> +    /* Input data for 'en-northd'. This is data generated by 'en-northd's
> +     * input nodes. */
> +    struct northd_input input;
>
> -    const char *ovn_internal_version;
> -    bool use_parallel_build;
> +    /* Global state for 'en-northd'. */
> +    struct hmap datapaths;
> +    struct hmap ports;
> +    struct hmap port_groups;
> +    struct hmap mcast_groups;
> +    struct hmap igmp_groups;
> +    struct shash meter_groups;
> +    struct hmap lbs;
> +    struct hmap bfd_connections;
> +    struct ovs_list lr_list;
> +    bool ovn_internal_version_changed;
>  };
>
> -void ovn_db_run(struct northd_context *ctx);
> +void northd_run(struct northd_data *data,
> +                struct ovsdb_idl_txn *ovnnb_txn,
> +                struct ovsdb_idl_txn *ovnsb_txn,
> +                struct ovsdb_idl_loop *sb_loop);
> +void northd_destroy(struct northd_data *data);
> +void northd_init(struct northd_data *data);
> +void northd_indices_create(struct northd_data *data,
> +                           struct ovsdb_idl *ovnsb_idl);
> +void build_lflows(struct northd_data *data,
> +                  struct ovsdb_idl_txn *ovnsb_txn);
>
>  #endif /* NORTHD_H */
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index e9f6a4bc4c12..e291e26872ec 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -31,7 +31,6 @@
>  #include "ovsdb-idl.h"
>  #include "lib/ovn-l7.h"
>  #include "lib/ovn-nb-idl.h"
> -#include "lib/ovn-parallel-hmap.h"
>  #include "lib/ovn-sb-idl.h"
>  #include "openvswitch/poll-loop.h"
>  #include "simap.h"
> @@ -70,7 +69,6 @@ static const char *ssl_ca_cert_file;
>  #define DEFAULT_PROBE_INTERVAL_MSEC 5000
>  static int northd_probe_interval_nb = 0;
>  static int northd_probe_interval_sb = 0;
> -static bool use_parallel_build = true;
>
>  static const char *rbac_chassis_auth[] =
>      {"name"};
> @@ -313,12 +311,12 @@ ovn_rbac_validate_perm(const struct
sbrec_rbac_permission *perm)
>
>  static void
>  ovn_rbac_create_perm(struct rbac_perm_cfg *pcfg,
> -                     struct northd_context *ctx,
> +                     struct ovsdb_idl_txn *ovnsb_txn,
>                       const struct sbrec_rbac_role *rbac_role)
>  {
>      struct sbrec_rbac_permission *rbac_perm;
>
> -    rbac_perm = sbrec_rbac_permission_insert(ctx->ovnsb_txn);
> +    rbac_perm = sbrec_rbac_permission_insert(ovnsb_txn);
>      sbrec_rbac_permission_set_table(rbac_perm, pcfg->table);
>      sbrec_rbac_permission_set_authorization(rbac_perm,
>                                              pcfg->auth,
> @@ -332,7 +330,8 @@ ovn_rbac_create_perm(struct rbac_perm_cfg *pcfg,
>  }
>
>  static void
> -check_and_update_rbac(struct northd_context *ctx)
> +check_and_update_rbac(struct ovsdb_idl_txn *ovnsb_txn,
> +                      struct ovsdb_idl *ovnsb_idl)
>  {
>      const struct sbrec_rbac_role *rbac_role = NULL;
>      const struct sbrec_rbac_permission *perm_row, *perm_next;
> @@ -343,12 +342,12 @@ check_and_update_rbac(struct northd_context *ctx)
>          pcfg->row = NULL;
>      }
>
> -    SBREC_RBAC_PERMISSION_FOR_EACH_SAFE (perm_row, perm_next,
ctx->ovnsb_idl) {
> +    SBREC_RBAC_PERMISSION_FOR_EACH_SAFE (perm_row, perm_next, ovnsb_idl)
{
>          if (!ovn_rbac_validate_perm(perm_row)) {
>              sbrec_rbac_permission_delete(perm_row);
>          }
>      }
> -    SBREC_RBAC_ROLE_FOR_EACH_SAFE (role_row, role_row_next,
ctx->ovnsb_idl) {
> +    SBREC_RBAC_ROLE_FOR_EACH_SAFE (role_row, role_row_next, ovnsb_idl) {
>          if (strcmp(role_row->name, "ovn-controller")) {
>              sbrec_rbac_role_delete(role_row);
>          } else {
> @@ -357,19 +356,20 @@ check_and_update_rbac(struct northd_context *ctx)
>      }
>
>      if (!rbac_role) {
> -        rbac_role = sbrec_rbac_role_insert(ctx->ovnsb_txn);
> +        rbac_role = sbrec_rbac_role_insert(ovnsb_txn);
>          sbrec_rbac_role_set_name(rbac_role, "ovn-controller");
>      }
>
>      for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
>          if (!pcfg->row) {
> -            ovn_rbac_create_perm(pcfg, ctx, rbac_role);
> +            ovn_rbac_create_perm(pcfg, ovnsb_txn, rbac_role);
>          }
>      }
>  }
>
>  static void
> -check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
> +check_and_add_supported_dhcp_opts_to_sb_db(struct ovsdb_idl_txn
*ovnsb_txn,
> +                                           struct ovsdb_idl *ovnsb_idl)
>  {
>      struct hmap dhcp_opts_to_add = HMAP_INITIALIZER(&dhcp_opts_to_add);
>      for (size_t i = 0; (i < sizeof(supported_dhcp_opts) /
> @@ -379,7 +379,7 @@ check_and_add_supported_dhcp_opts_to_sb_db(struct
northd_context *ctx)
>      }
>
>      const struct sbrec_dhcp_options *opt_row, *opt_row_next;
> -    SBREC_DHCP_OPTIONS_FOR_EACH_SAFE (opt_row, opt_row_next,
ctx->ovnsb_idl) {
> +    SBREC_DHCP_OPTIONS_FOR_EACH_SAFE (opt_row, opt_row_next, ovnsb_idl) {
>          struct gen_opts_map *dhcp_opt =
>              dhcp_opts_find(&dhcp_opts_to_add, opt_row->name);
>          if (dhcp_opt) {
> @@ -397,7 +397,7 @@ check_and_add_supported_dhcp_opts_to_sb_db(struct
northd_context *ctx)
>      struct gen_opts_map *opt;
>      HMAP_FOR_EACH (opt, hmap_node, &dhcp_opts_to_add) {
>          struct sbrec_dhcp_options *sbrec_dhcp_option =
> -            sbrec_dhcp_options_insert(ctx->ovnsb_txn);
> +            sbrec_dhcp_options_insert(ovnsb_txn);
>          sbrec_dhcp_options_set_name(sbrec_dhcp_option, opt->name);
>          sbrec_dhcp_options_set_code(sbrec_dhcp_option, opt->code);
>          sbrec_dhcp_options_set_type(sbrec_dhcp_option, opt->type);
> @@ -407,7 +407,8 @@ check_and_add_supported_dhcp_opts_to_sb_db(struct
northd_context *ctx)
>  }
>
>  static void
> -check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
> +check_and_add_supported_dhcpv6_opts_to_sb_db(struct ovsdb_idl_txn
*ovnsb_txn,
> +                                             struct ovsdb_idl *ovnsb_idl)
>  {
>      struct hmap dhcpv6_opts_to_add =
HMAP_INITIALIZER(&dhcpv6_opts_to_add);
>      for (size_t i = 0; (i < sizeof(supported_dhcpv6_opts) /
> @@ -417,7 +418,7 @@ check_and_add_supported_dhcpv6_opts_to_sb_db(struct
northd_context *ctx)
>      }
>
>      const struct sbrec_dhcpv6_options *opt_row, *opt_row_next;
> -    SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next,
ctx->ovnsb_idl) {
> +    SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ovnsb_idl)
{
>          struct gen_opts_map *dhcp_opt =
>              dhcp_opts_find(&dhcpv6_opts_to_add, opt_row->name);
>          if (dhcp_opt) {
> @@ -430,7 +431,7 @@ check_and_add_supported_dhcpv6_opts_to_sb_db(struct
northd_context *ctx)
>      struct gen_opts_map *opt;
>      HMAP_FOR_EACH (opt, hmap_node, &dhcpv6_opts_to_add) {
>          struct sbrec_dhcpv6_options *sbrec_dhcpv6_option =
> -            sbrec_dhcpv6_options_insert(ctx->ovnsb_txn);
> +            sbrec_dhcpv6_options_insert(ovnsb_txn);
>          sbrec_dhcpv6_options_set_name(sbrec_dhcpv6_option, opt->name);
>          sbrec_dhcpv6_options_set_code(sbrec_dhcpv6_option, opt->code);
>          sbrec_dhcpv6_options_set_type(sbrec_dhcpv6_option, opt->type);
> @@ -641,8 +642,6 @@ main(int argc, char *argv[])
>
>      daemonize_complete();
>
> -    use_parallel_build = can_parallelize_hashes(false);
> -
>      /* We want to detect (almost) all changes to the ovn-nb db. */
>      struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
>          ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true));
> @@ -909,26 +908,10 @@ main(int argc, char *argv[])
>      ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
&sbrec_fdb_col_dp_key);
>      ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
&sbrec_fdb_col_port_key);
>
> -    struct ovsdb_idl_index *sbrec_chassis_by_name
> -        = chassis_index_create(ovnsb_idl_loop.idl);
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_hostname
> -        = chassis_hostname_index_create(ovnsb_idl_loop.idl);
> -
> -    struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name
> -        = ha_chassis_group_index_create(ovnsb_idl_loop.idl);
> -
> -    struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp
> -        = mcast_group_index_create(ovnsb_idl_loop.idl);
> -
> -    struct ovsdb_idl_index *sbrec_ip_mcast_by_dp
> -        = ip_mcast_index_create(ovnsb_idl_loop.idl);
> -
>      unixctl_command_register("sb-connection-status", "", 0, 0,
>                               ovn_conn_show, ovnsb_idl_loop.idl);
>
> -    char *ovn_internal_version = ovn_get_internal_version();
> -    VLOG_INFO("OVN internal version is : [%s]", ovn_internal_version);
> +    VLOG_INFO("OVN internal version is : [%s]",
ovn_get_internal_version());
>
>      stopwatch_create(NORTHD_LOOP_STOPWATCH_NAME, SW_MS);
>      stopwatch_create(OVNNB_DB_RUN_STOPWATCH_NAME, SW_MS);
> @@ -999,24 +982,6 @@ main(int argc, char *argv[])
>                  ovnsb_cond_seqno = new_ovnsb_cond_seqno;
>              }
>
> -            struct northd_context ctx = {
> -                .ovnnb_db = ovnnb_db,
> -                .ovnsb_db = ovnsb_db,
> -                .ovnnb_idl = ovnnb_idl_loop.idl,
> -                .ovnnb_idl_loop = &ovnnb_idl_loop,
> -                .ovnnb_txn = ovnnb_txn,
> -                .ovnsb_idl = ovnsb_idl_loop.idl,
> -                .ovnsb_idl_loop = &ovnsb_idl_loop,
> -                .ovnsb_txn = ovnsb_txn,
> -                .sbrec_chassis_by_name = sbrec_chassis_by_name,
> -                .sbrec_chassis_by_hostname = sbrec_chassis_by_hostname,
> -                .sbrec_ha_chassis_grp_by_name =
sbrec_ha_chassis_grp_by_name,
> -                .sbrec_mcast_group_by_name_dp =
sbrec_mcast_group_by_name_dp,
> -                .sbrec_ip_mcast_by_dp = sbrec_ip_mcast_by_dp,
> -                .use_parallel_build = use_parallel_build,
> -                .ovn_internal_version = ovn_internal_version,
> -            };
> -
>              if (!state.had_lock &&
ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
>                  VLOG_INFO("ovn-northd lock acquired. "
>                          "This ovn-northd instance is now active.");
> @@ -1030,12 +995,17 @@ main(int argc, char *argv[])
>              }
>
>              if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
> -                inc_proc_northd_run(&ctx, recompute);
> +                inc_proc_northd_run(ovnnb_txn, ovnsb_txn,
> +                                    &ovnsb_idl_loop,
> +                                    recompute);
>                  recompute = false;
> -                if (ctx.ovnsb_txn) {
> -                    check_and_add_supported_dhcp_opts_to_sb_db(&ctx);
> -                    check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);
> -                    check_and_update_rbac(&ctx);
> +                if (ovnsb_txn) {
> +                    check_and_add_supported_dhcp_opts_to_sb_db(
> +                                 ovnsb_txn, ovnsb_idl_loop.idl);
> +                    check_and_add_supported_dhcpv6_opts_to_sb_db(
> +                                 ovnsb_txn, ovnsb_idl_loop.idl);
> +                    check_and_update_rbac(
> +                                 ovnsb_txn, ovnsb_idl_loop.idl);
>                  }
>
>              }
> @@ -1114,7 +1084,6 @@ main(int argc, char *argv[])
>      }
>      inc_proc_northd_cleanup();
>
> -    free(ovn_internal_version);
>      unixctl_server_destroy(unixctl);
>      ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
>      ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
> --
> 2.27.0
>


More information about the dev mailing list