On Mon, Mar 27, 2023 at 4:05 AM Dumitru Ceara <[email protected]> wrote:
>
> Hi Han,
>
> Thanks for the northd refactor, that's great work!
>
> On 3/21/23 07:02, Han Zhou wrote:
> > Similar to the previous commit that splits switch ports and router
> > ports, this patch splits the two types of datapaths in different hmap
> > tables.  This is also mainly to help for future incremental processing
> > implementation. As a byproduct, the recompute performance is improved
> > slightly. (Together with the previous commit that splits ports, scale
> > test shows around ~4% improvment)
> >
> > Note: This patch didn't change the ovn_datapath structure, which is
> > used by both switches and routers. It may be a followup improvement in
> > the future if necessary, e.g. to reduce memory footprint.
> >
>
> I think, for better readability, it actually makes sense to do this part
> as soon as possible too.  That would allow us to avoid (clumsy) checks
like:
>
> static void
> destroy_router_external_ips(struct ovn_datapath *od)
> {
>     if (!od->nbr) {
>         return;
>     }
>
>     sset_destroy(&od->external_ips);
> }
>
> If you don't plan to work on it, please let me know and I can try to
> prepare a series that does that (and maybe split LSP/LRP ports too).

Thanks Dumitru. It would be great if you could help on this, but I don't
think it is urgent, so take your time and don't feel pushed.

One tricky issue for this is the join_datapaths() and join_logical_ports()
implementation. Since there is only one SB table for each (SB DP and SB
PB), now for efficiency (avoid iterating SB tables multiple times), I
didn't change the join_xxx functions, and used just one hmap for these
functions to work like before, and then move LRs/LRPs to a new hmap
afterwards. If using different data structures for LR/LS and LRP/LSP, they
will have to be separated in the first place. In addition, SB PB doesn't
even include information to tell if it is a LSP or LRP, which may need some
changes and some extra flags populated.

Such changes are also likely to end up with conflict updates with my
ongoing northd I-P work, so I'd suggest maybe wait for a while after my
initial I-P patches, to avoid tedious work of resolving conflicts.

>
> In the meantime I posted a minor follow up patch; please take a look
> when you have time:
>
>
https://patchwork.ozlabs.org/project/ovn/patch/[email protected]/

Acked.

Thanks,
Han

>
> Regards,
> Dumitru
>
> > Signed-off-by: Han Zhou <[email protected]>
> > ---
> >  lib/lb.c                   |  15 +-
> >  lib/lb.h                   |   6 +-
> >  northd/en-lflow.c          |   3 +-
> >  northd/en-sync-sb.c        |   2 +-
> >  northd/mac-binding-aging.c |   2 +-
> >  northd/northd.c            | 946 ++++++++++++++++++++-----------------
> >  northd/northd.h            |   6 +-
> >  7 files changed, 543 insertions(+), 437 deletions(-)
> >
> > diff --git a/lib/lb.c b/lib/lb.c
> > index 66c8152750a1..37e05c31f918 100644
> > --- a/lib/lb.c
> > +++ b/lib/lb.c
> > @@ -521,7 +521,7 @@ ovn_lb_get_health_check(const struct
nbrec_load_balancer *nbrec_lb,
> >
> >  struct ovn_northd_lb *
> >  ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb,
> > -                     size_t n_datapaths)
> > +                     size_t n_ls_datapaths, size_t n_lr_datapaths)
> >  {
> >      bool template = smap_get_bool(&nbrec_lb->options, "template",
false);
> >      bool is_udp = nullable_string_is_equal(nbrec_lb->protocol, "udp");
> > @@ -560,8 +560,8 @@ ovn_northd_lb_create(const struct
nbrec_load_balancer *nbrec_lb,
> >      }
> >      lb->affinity_timeout = affinity_timeout;
> >
> > -    lb->nb_ls_map = bitmap_allocate(n_datapaths);
> > -    lb->nb_lr_map = bitmap_allocate(n_datapaths);
> > +    lb->nb_ls_map = bitmap_allocate(n_ls_datapaths);
> > +    lb->nb_lr_map = bitmap_allocate(n_lr_datapaths);
> >
> >      sset_init(&lb->ips_v4);
> >      sset_init(&lb->ips_v6);
> > @@ -687,12 +687,13 @@ ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
> >
> >  /* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group
record
> >   * and a hash map of all existing 'struct ovn_northd_lb' objects.
Space will
> > - * be allocated for 'max_datapaths' logical switches and the same
amount of
> > + * be allocated for 'max_ls_datapaths' logical switches and
'max_lr_datapaths'
> >   * logical routers to which this LB Group is applied.  Can be filled
later
> >   * with ovn_lb_group_add_ls() and ovn_lb_group_add_lr() respectively.
*/
> >  struct ovn_lb_group *
> >  ovn_lb_group_create(const struct nbrec_load_balancer_group
*nbrec_lb_group,
> > -                    const struct hmap *lbs, size_t max_datapaths)
> > +                    const struct hmap *lbs, size_t max_ls_datapaths,
> > +                    size_t max_lr_datapaths)
> >  {
> >      struct ovn_lb_group *lb_group;
> >
> > @@ -700,8 +701,8 @@ ovn_lb_group_create(const struct
nbrec_load_balancer_group *nbrec_lb_group,
> >      lb_group->uuid = nbrec_lb_group->header_.uuid;
> >      lb_group->n_lbs = nbrec_lb_group->n_load_balancer;
> >      lb_group->lbs = xmalloc(lb_group->n_lbs * sizeof *lb_group->lbs);
> > -    lb_group->ls = xmalloc(max_datapaths * sizeof *lb_group->ls);
> > -    lb_group->lr = xmalloc(max_datapaths * sizeof *lb_group->lr);
> > +    lb_group->ls = xmalloc(max_ls_datapaths * sizeof *lb_group->ls);
> > +    lb_group->lr = xmalloc(max_lr_datapaths * sizeof *lb_group->lr);
> >      lb_group->lb_ips = ovn_lb_ip_set_create();
> >
> >      for (size_t i = 0; i < nbrec_lb_group->n_load_balancer; i++) {
> > diff --git a/lib/lb.h b/lib/lb.h
> > index ddd41703da44..ba48a0dac4f0 100644
> > --- a/lib/lb.h
> > +++ b/lib/lb.h
> > @@ -130,7 +130,8 @@ struct ovn_northd_lb_backend {
> >  };
> >
> >  struct ovn_northd_lb *ovn_northd_lb_create(const struct
nbrec_load_balancer *,
> > -                                           size_t n_datapaths);
> > +                                           size_t n_ls_datapaths,
> > +                                           size_t n_lr_datapaths);
> >  struct ovn_northd_lb *ovn_northd_lb_find(const struct hmap *,
> >                                           const struct uuid *);
> >  const struct smap *ovn_northd_lb_get_vips(const struct ovn_northd_lb
*);
> > @@ -157,7 +158,8 @@ struct ovn_lb_group {
> >  struct ovn_lb_group *ovn_lb_group_create(
> >      const struct nbrec_load_balancer_group *,
> >      const struct hmap *lbs,
> > -    size_t max_datapaths);
> > +    size_t max_ls_datapaths,
> > +    size_t max_lr_datapaths);
> >  void ovn_lb_group_destroy(struct ovn_lb_group *lb_group);
> >  struct ovn_lb_group *ovn_lb_group_find(const struct hmap *lb_groups,
> >                                         const struct uuid *);
> > diff --git a/northd/en-lflow.c b/northd/en-lflow.c
> > index 9e7a2145e520..995c7cc10e33 100644
> > --- a/northd/en-lflow.c
> > +++ b/northd/en-lflow.c
> > @@ -54,7 +54,8 @@ void en_lflow_run(struct engine_node *node, void
*data OVS_UNUSED)
> >                            engine_get_input("SB_multicast_group", node),
> >                           "sbrec_mcast_group_by_name");
> >
> > -    lflow_input.datapaths = &northd_data->datapaths;
> > +    lflow_input.ls_datapaths = &northd_data->ls_datapaths;
> > +    lflow_input.lr_datapaths = &northd_data->lr_datapaths;
> >      lflow_input.ls_ports = &northd_data->ls_ports;
> >      lflow_input.lr_ports = &northd_data->lr_ports;
> >      lflow_input.port_groups = &northd_data->port_groups;
> > diff --git a/northd/en-sync-sb.c b/northd/en-sync-sb.c
> > index 6e33901a8a29..d4f4e60eabd6 100644
> > --- a/northd/en-sync-sb.c
> > +++ b/northd/en-sync-sb.c
> > @@ -91,7 +91,7 @@ en_sync_to_sb_addr_set_run(struct engine_node *node,
void *data OVS_UNUSED)
> >
> >      sync_addr_sets(nb_address_set_table, nb_port_group_table,
> >                     sb_address_set_table, eng_ctx->ovnsb_idl_txn,
> > -                   &northd_data->datapaths);
> > +                   &northd_data->lr_datapaths);
> >
> >      engine_set_node_state(node, EN_UPDATED);
> >  }
> > diff --git a/northd/mac-binding-aging.c b/northd/mac-binding-aging.c
> > index 1c30e979e1ac..3bdf1c2ea8d9 100644
> > --- a/northd/mac-binding-aging.c
> > +++ b/northd/mac-binding-aging.c
> > @@ -109,7 +109,7 @@ en_mac_binding_aging_run(struct engine_node *node,
void *data OVS_UNUSED)
> >                                      "sbrec_mac_binding_by_datapath");
> >
> >      struct ovn_datapath *od;
> > -    HMAP_FOR_EACH (od, key_node, &northd_data->datapaths) {
> > +    HMAP_FOR_EACH (od, key_node, &northd_data->lr_datapaths) {
> >          if (od->sb && od->nbr) {
> >              mac_binding_aging_run_for_datapath(od->sb, od->nbr,
> >
sbrec_mac_binding_by_datapath,
> > diff --git a/northd/northd.c b/northd/northd.c
> > index 10b16e685adf..baee1786469a 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -947,16 +947,24 @@ ovn_datapath_is_stale(const struct ovn_datapath
*od)
> >  }
> >
> >  static struct ovn_datapath *
> > -ovn_datapath_from_sbrec(const struct hmap *datapaths,
> > +ovn_datapath_from_sbrec(const struct hmap *ls_datapaths,
> > +                        const struct hmap *lr_datapaths,
> >                          const struct sbrec_datapath_binding *sb)
> >  {
> >      struct uuid key;
> > +    const struct hmap *dps;
> >
> > -    if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
> > -        !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
> > +    if (smap_get_uuid(&sb->external_ids, "logical-switch", &key)) {
> > +        dps = ls_datapaths;
> > +    } else if (smap_get_uuid(&sb->external_ids, "logical-router",
&key)) {
> > +        dps = lr_datapaths;
> > +    } else {
> > +        return NULL;
> > +    }
> > +    if (!dps) {
> >          return NULL;
> >      }
> > -    struct ovn_datapath *od = ovn_datapath_find(datapaths, &key);
> > +    struct ovn_datapath *od = ovn_datapath_find(dps, &key);
> >      if (od && (od->sb == sb)) {
> >          return od;
> >      }
> > @@ -1375,9 +1383,15 @@ ovn_datapath_assign_requested_tnl_id(struct
northd_input *input_data,
> >      }
> >  }
> >
> > -/* Array of all datapaths, with 'od->index' being their index in the
array. */
> > -static struct ovn_datapath **datapaths_array = NULL;
> > -static size_t n_datapaths = 0; /* Size of the 'datapaths_array'. */
> > +/* Array of ls datapaths, with 'od->index' being their index in the
array. */
> > +static struct ovn_datapath **ls_datapaths_array = NULL;
> > +/* Size of the 'ls_datapaths_array'. */
> > +static size_t n_ls_datapaths = 0;
> > +
> > +/* Array of lr datapaths, with 'od->index' being their index in the
array. */
> > +static struct ovn_datapath **lr_datapaths_array = NULL;
> > +/* Size of the 'lr_datapaths_array'. */
> > +static size_t n_lr_datapaths = 0;
> >
> >  /* Updates the southbound Datapath_Binding table so that it contains
the
> >   * logical switches and routers specified by the northbound database.
> > @@ -1387,11 +1401,13 @@ static size_t n_datapaths = 0; /* Size of the
'datapaths_array'. */
> >  static void
> >  build_datapaths(struct northd_input *input_data,
> >                  struct ovsdb_idl_txn *ovnsb_txn,
> > -                struct hmap *datapaths,
> > +                struct hmap *ls_datapaths,
> > +                struct hmap *lr_datapaths,
> >                  struct ovs_list *lr_list)
> >  {
> >      struct ovs_list sb_only, nb_only, both;
> >
> > +    struct hmap *datapaths = ls_datapaths;
> >      join_datapaths(input_data, ovnsb_txn,
> >                     datapaths, &sb_only, &nb_only, &both, lr_list);
> >
> > @@ -1444,17 +1460,37 @@ build_datapaths(struct northd_input *input_data,
> >          ovn_datapath_destroy(datapaths, od);
> >      }
> >
> > +    /* Move lr datapaths to lr_datapaths, and ls datapaths will
> > +     * remain in datapaths/ls_datapaths. */
> > +    HMAP_FOR_EACH_SAFE (od, key_node, datapaths) {
> > +        if (!od->nbr) {
> > +            ovs_assert(od->nbs);
> > +            continue;
> > +        }
> > +        hmap_remove(datapaths, &od->key_node);
> > +        hmap_insert(lr_datapaths, &od->key_node, od->key_node.hash);
> > +    }
> > +
> >      /* Assign unique sequential indexes to all datapaths.  These are
not
> >       * visible outside of the northd loop, so, unlike the tunnel keys,
it
> >       * doesn't matter if they are different on every iteration. */
> >      size_t index = 0;
> >
> > -    n_datapaths = hmap_count(datapaths);
> > -    datapaths_array = xrealloc(datapaths_array,
> > -                               n_datapaths * sizeof *datapaths_array);
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > +    n_ls_datapaths = hmap_count(ls_datapaths);
> > +    ls_datapaths_array = xrealloc(ls_datapaths_array,
> > +                                  n_ls_datapaths * sizeof
*ls_datapaths_array);
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> > +        od->index = index;
> > +        ls_datapaths_array[index++] = od;
> > +    }
> > +
> > +    index = 0;
> > +    n_lr_datapaths = hmap_count(lr_datapaths);
> > +    lr_datapaths_array = xrealloc(lr_datapaths_array,
> > +                                  n_lr_datapaths * sizeof
*lr_datapaths_array);
> > +    HMAP_FOR_EACH (od, key_node, lr_datapaths) {
> >          od->index = index;
> > -        datapaths_array[index++] = od;
> > +        lr_datapaths_array[index++] = od;
> >      }
> >  }
> >
> > @@ -2243,7 +2279,7 @@ update_dynamic_addresses(struct
dynamic_address_update *update)
> >  }
> >
> >  static void
> > -build_ipam(struct hmap *datapaths, struct hmap *ls_ports)
> > +build_ipam(struct hmap *ls_datapaths, struct hmap *ls_ports)
> >  {
> >      /* IPAM generally stands for IP address management.  In
non-virtualized
> >       * world, MAC addresses come with the hardware.  But, with
virtualized
> > @@ -2257,10 +2293,8 @@ build_ipam(struct hmap *datapaths, struct hmap
*ls_ports)
> >      struct ovs_list updates;
> >
> >      ovs_list_init(&updates);
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > -        if (!od->nbs) {
> > -            continue;
> > -        }
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> > +        ovs_assert(od->nbs);
> >
> >          for (size_t i = 0; i < od->nbs->n_ports; i++) {
> >              const struct nbrec_logical_switch_port *nbsp =
od->nbs->ports[i];
> > @@ -2440,8 +2474,8 @@ tag_alloc_create_new_tag(struct hmap
*tag_alloc_table,
> >
> >  static void
> >  join_logical_ports(struct northd_input *input_data,
> > -                   struct hmap *datapaths, struct hmap *ports,
> > -                   struct hmap *chassis_qdisc_queues,
> > +                   struct hmap *ls_datapaths, struct hmap
*lr_datapaths,
> > +                   struct hmap *ports, struct hmap
*chassis_qdisc_queues,
> >                     struct hmap *tag_alloc_table, struct ovs_list
*sb_only,
> >                     struct ovs_list *nb_only, struct ovs_list *both)
> >  {
> > @@ -2458,225 +2492,226 @@ join_logical_ports(struct northd_input
*input_data,
> >      }
> >
> >      struct ovn_datapath *od;
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > -        if (od->nbs) {
> > -            size_t n_allocated_localnet_ports = 0;
> > -            for (size_t i = 0; i < od->nbs->n_ports; i++) {
> > -                const struct nbrec_logical_switch_port *nbsp
> > -                    = od->nbs->ports[i];
> > -                struct ovn_port *op = ovn_port_find_bound(ports,
nbsp->name);
> > -                if (op && (op->od || op->nbsp || op->nbrp)) {
> > -                    static struct vlog_rate_limit rl
> > -                        = VLOG_RATE_LIMIT_INIT(5, 1);
> > -                    VLOG_WARN_RL(&rl, "duplicate logical port %s",
nbsp->name);
> > -                    continue;
> > -                } else if (op && (!op->sb || op->sb->datapath ==
od->sb)) {
> > -                    /*
> > -                     * Handle cases where lport type was explicitly
changed
> > -                     * in the NBDB, in such cases:
> > -                     * 1. remove the current sbrec of the affected
lport from
> > -                     *    the port_binding table.
> > -                     *
> > -                     * 2. create a new sbrec with the same
logical_port as the
> > -                     *    deleted lport and add it to the nb_only list
which
> > -                     *    will make the northd handle this lport as a
new
> > -                     *    created one and recompute everything that is
needed
> > -                     *    for this lport.
> > -                     *
> > -                     * This change will affect container/virtual lport
type
> > -                     * changes only for now, this change is needed in
> > -                     * contaier/virtual lport cases to avoid port type
> > -                     * conflicts in the ovn-controller when the user
clears
> > -                     * the parent_port field in the container lport or
updated
> > -                     * the lport type.
> > -                     *
> > -                     */
> > -                    bool update_sbrec = false;
> > -                    if (op->sb && lsp_is_type_changed(op->sb, nbsp,
> > -                                                      &update_sbrec)
> > -                                   && update_sbrec) {
> > -                        ovs_list_remove(&op->list);
> > -                        sbrec_port_binding_delete(op->sb);
> > -                        ovn_port_destroy(ports, op);
> > -                        op = ovn_port_create(ports, nbsp->name, nbsp,
> > -                                             NULL, NULL);
> > -                        ovs_list_push_back(nb_only, &op->list);
> > -                    } else {
> > -                        ovn_port_set_nb(op, nbsp, NULL);
> > -                        ovs_list_remove(&op->list);
> > -
> > -                        uint32_t queue_id =
smap_get_int(&op->sb->options,
> > -
"qdisc_queue_id", 0);
> > -                        if (queue_id && op->sb->chassis) {
> > -                            add_chassis_queue(
> > -                                 chassis_qdisc_queues,
> > -                                 &op->sb->chassis->header_.uuid,
> > -                                 queue_id);
> > -                        }
> > -
> > -                        ovs_list_push_back(both, &op->list);
> > -
> > -                        /* This port exists due to a SB binding, but
should
> > -                         * not have been initialized fully. */
> > -                        ovs_assert(!op->n_lsp_addrs &&
!op->n_ps_addrs);
> > -                    }
> > -                } else {
> > -                    op = ovn_port_create(ports, nbsp->name, nbsp,
NULL, NULL);
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> > +        ovs_assert(od->nbs);
> > +        size_t n_allocated_localnet_ports = 0;
> > +        for (size_t i = 0; i < od->nbs->n_ports; i++) {
> > +            const struct nbrec_logical_switch_port *nbsp
> > +                = od->nbs->ports[i];
> > +            struct ovn_port *op = ovn_port_find_bound(ports,
nbsp->name);
> > +            if (op && (op->od || op->nbsp || op->nbrp)) {
> > +                static struct vlog_rate_limit rl
> > +                    = VLOG_RATE_LIMIT_INIT(5, 1);
> > +                VLOG_WARN_RL(&rl, "duplicate logical port %s",
nbsp->name);
> > +                continue;
> > +            } else if (op && (!op->sb || op->sb->datapath == od->sb)) {
> > +                /*
> > +                 * Handle cases where lport type was explicitly changed
> > +                 * in the NBDB, in such cases:
> > +                 * 1. remove the current sbrec of the affected lport
from
> > +                 *    the port_binding table.
> > +                 *
> > +                 * 2. create a new sbrec with the same logical_port as
the
> > +                 *    deleted lport and add it to the nb_only list
which
> > +                 *    will make the northd handle this lport as a new
> > +                 *    created one and recompute everything that is
needed
> > +                 *    for this lport.
> > +                 *
> > +                 * This change will affect container/virtual lport type
> > +                 * changes only for now, this change is needed in
> > +                 * contaier/virtual lport cases to avoid port type
> > +                 * conflicts in the ovn-controller when the user clears
> > +                 * the parent_port field in the container lport or
updated
> > +                 * the lport type.
> > +                 *
> > +                 */
> > +                bool update_sbrec = false;
> > +                if (op->sb && lsp_is_type_changed(op->sb, nbsp,
> > +                                                  &update_sbrec)
> > +                               && update_sbrec) {
> > +                    ovs_list_remove(&op->list);
> > +                    sbrec_port_binding_delete(op->sb);
> > +                    ovn_port_destroy(ports, op);
> > +                    op = ovn_port_create(ports, nbsp->name, nbsp,
> > +                                         NULL, NULL);
> >                      ovs_list_push_back(nb_only, &op->list);
> > -                }
> > +                } else {
> > +                    ovn_port_set_nb(op, nbsp, NULL);
> > +                    ovs_list_remove(&op->list);
> >
> > -                if (lsp_is_localnet(nbsp)) {
> > -                   if (od->n_localnet_ports >=
n_allocated_localnet_ports) {
> > -                       od->localnet_ports = x2nrealloc(
> > -                           od->localnet_ports,
&n_allocated_localnet_ports,
> > -                           sizeof *od->localnet_ports);
> > -                   }
> > -                   od->localnet_ports[od->n_localnet_ports++] = op;
> > -                }
> > +                    uint32_t queue_id = smap_get_int(&op->sb->options,
> > +                                                     "qdisc_queue_id",
0);
> > +                    if (queue_id && op->sb->chassis) {
> > +                        add_chassis_queue(
> > +                             chassis_qdisc_queues,
> > +                             &op->sb->chassis->header_.uuid,
> > +                             queue_id);
> > +                    }
> >
> > -                if (lsp_is_vtep(nbsp)) {
> > -                    od->has_vtep_lports = true;
> > -                }
> > +                    ovs_list_push_back(both, &op->list);
> >
> > -                op->lsp_addrs
> > -                    = xmalloc(sizeof *op->lsp_addrs *
nbsp->n_addresses);
> > -                for (size_t j = 0; j < nbsp->n_addresses; j++) {
> > -                    if (!strcmp(nbsp->addresses[j], "unknown")) {
> > -                        op->has_unknown = true;
> > -                        continue;
> > -                    }
> > -                    if (!strcmp(nbsp->addresses[j], "router")) {
> > -                        continue;
> > -                    }
> > -                    if (is_dynamic_lsp_address(nbsp->addresses[j])) {
> > -                        continue;
> > -                    } else if
(!extract_lsp_addresses(nbsp->addresses[j],
> > -
&op->lsp_addrs[op->n_lsp_addrs])) {
> > -                        static struct vlog_rate_limit rl
> > -                            = VLOG_RATE_LIMIT_INIT(1, 1);
> > -                        VLOG_INFO_RL(&rl, "invalid syntax '%s' in
logical "
> > -                                          "switch port addresses. No
MAC "
> > -                                          "address found",
> > -                                          op->nbsp->addresses[j]);
> > -                        continue;
> > -                    }
> > -                    op->n_lsp_addrs++;
> > -                }
> > -                op->n_lsp_non_router_addrs = op->n_lsp_addrs;
> > -
> > -                op->ps_addrs
> > -                    = xmalloc(sizeof *op->ps_addrs *
nbsp->n_port_security);
> > -                for (size_t j = 0; j < nbsp->n_port_security; j++) {
> > -                    if (!extract_lsp_addresses(nbsp->port_security[j],
> > -
&op->ps_addrs[op->n_ps_addrs])) {
> > -                        static struct vlog_rate_limit rl
> > -                            = VLOG_RATE_LIMIT_INIT(1, 1);
> > -                        VLOG_INFO_RL(&rl, "invalid syntax '%s' in port
"
> > -                                          "security. No MAC address
found",
> > -                                          op->nbsp->port_security[j]);
> > -                        continue;
> > -                    }
> > -                    op->n_ps_addrs++;
> > +                    /* This port exists due to a SB binding, but should
> > +                     * not have been initialized fully. */
> > +                    ovs_assert(!op->n_lsp_addrs && !op->n_ps_addrs);
> >                  }
> > +            } else {
> > +                op = ovn_port_create(ports, nbsp->name, nbsp, NULL,
NULL);
> > +                ovs_list_push_back(nb_only, &op->list);
> > +            }
> >
> > -                op->od = od;
> > -                ovs_list_push_back(&od->port_list, &op->dp_node);
> > -                tag_alloc_add_existing_tags(tag_alloc_table, nbsp);
> > +            if (lsp_is_localnet(nbsp)) {
> > +               if (od->n_localnet_ports >= n_allocated_localnet_ports)
{
> > +                   od->localnet_ports = x2nrealloc(
> > +                       od->localnet_ports, &n_allocated_localnet_ports,
> > +                       sizeof *od->localnet_ports);
> > +               }
> > +               od->localnet_ports[od->n_localnet_ports++] = op;
> >              }
> > -        } else {
> > -            size_t n_allocated_l3dgw_ports = 0;
> > -            for (size_t i = 0; i < od->nbr->n_ports; i++) {
> > -                const struct nbrec_logical_router_port *nbrp
> > -                    = od->nbr->ports[i];
> >
> > -                struct lport_addresses lrp_networks;
> > -                if (!extract_lrp_networks(nbrp, &lrp_networks)) {
> > -                    static struct vlog_rate_limit rl
> > -                        = VLOG_RATE_LIMIT_INIT(5, 1);
> > -                    VLOG_WARN_RL(&rl, "bad 'mac' %s", nbrp->mac);
> > +            if (lsp_is_vtep(nbsp)) {
> > +                od->has_vtep_lports = true;
> > +            }
> > +
> > +            op->lsp_addrs
> > +                = xmalloc(sizeof *op->lsp_addrs * nbsp->n_addresses);
> > +            for (size_t j = 0; j < nbsp->n_addresses; j++) {
> > +                if (!strcmp(nbsp->addresses[j], "unknown")) {
> > +                    op->has_unknown = true;
> >                      continue;
> >                  }
> > -
> > -                if (!lrp_networks.n_ipv4_addrs &&
!lrp_networks.n_ipv6_addrs) {
> > +                if (!strcmp(nbsp->addresses[j], "router")) {
> >                      continue;
> >                  }
> > -
> > -                struct ovn_port *op = ovn_port_find_bound(ports,
nbrp->name);
> > -                if (op && (op->od || op->nbsp || op->nbrp)) {
> > +                if (is_dynamic_lsp_address(nbsp->addresses[j])) {
> > +                    continue;
> > +                } else if (!extract_lsp_addresses(nbsp->addresses[j],
> > +
&op->lsp_addrs[op->n_lsp_addrs])) {
> >                      static struct vlog_rate_limit rl
> > -                        = VLOG_RATE_LIMIT_INIT(5, 1);
> > -                    VLOG_WARN_RL(&rl, "duplicate logical router port
%s",
> > -                                 nbrp->name);
> > -                    destroy_lport_addresses(&lrp_networks);
> > +                        = VLOG_RATE_LIMIT_INIT(1, 1);
> > +                    VLOG_INFO_RL(&rl, "invalid syntax '%s' in logical "
> > +                                      "switch port addresses. No MAC "
> > +                                      "address found",
> > +                                      op->nbsp->addresses[j]);
> >                      continue;
> > -                } else if (op && (!op->sb || op->sb->datapath ==
od->sb)) {
> > -                    ovn_port_set_nb(op, NULL, nbrp);
> > -                    ovs_list_remove(&op->list);
> > -                    ovs_list_push_back(both, &op->list);
> > +                }
> > +                op->n_lsp_addrs++;
> > +            }
> > +            op->n_lsp_non_router_addrs = op->n_lsp_addrs;
> >
> > -                    /* This port exists but should not have been
> > -                     * initialized fully. */
> > -                    ovs_assert(!op->lrp_networks.n_ipv4_addrs
> > -                               && !op->lrp_networks.n_ipv6_addrs);
> > -                } else {
> > -                    op = ovn_port_create(ports, nbrp->name, NULL,
nbrp, NULL);
> > -                    ovs_list_push_back(nb_only, &op->list);
> > +            op->ps_addrs
> > +                = xmalloc(sizeof *op->ps_addrs *
nbsp->n_port_security);
> > +            for (size_t j = 0; j < nbsp->n_port_security; j++) {
> > +                if (!extract_lsp_addresses(nbsp->port_security[j],
> > +
&op->ps_addrs[op->n_ps_addrs])) {
> > +                    static struct vlog_rate_limit rl
> > +                        = VLOG_RATE_LIMIT_INIT(1, 1);
> > +                    VLOG_INFO_RL(&rl, "invalid syntax '%s' in port "
> > +                                      "security. No MAC address found",
> > +                                      op->nbsp->port_security[j]);
> > +                    continue;
> >                  }
> > +                op->n_ps_addrs++;
> > +            }
> >
> > -                op->lrp_networks = lrp_networks;
> > -                op->od = od;
> > -                ovs_list_push_back(&od->port_list, &op->dp_node);
> > +            op->od = od;
> > +            ovs_list_push_back(&od->port_list, &op->dp_node);
> > +            tag_alloc_add_existing_tags(tag_alloc_table, nbsp);
> > +        }
> > +    }
> > +    HMAP_FOR_EACH (od, key_node, lr_datapaths) {
> > +        ovs_assert(od->nbr);
> > +        size_t n_allocated_l3dgw_ports = 0;
> > +        for (size_t i = 0; i < od->nbr->n_ports; i++) {
> > +            const struct nbrec_logical_router_port *nbrp
> > +                = od->nbr->ports[i];
> >
> > -                if (!od->redirect_bridged) {
> > -                    const char *redirect_type =
> > -                        smap_get(&nbrp->options, "redirect-type");
> > -                    od->redirect_bridged =
> > -                        redirect_type && !strcasecmp(redirect_type,
"bridged");
> > -                }
> > +            struct lport_addresses lrp_networks;
> > +            if (!extract_lrp_networks(nbrp, &lrp_networks)) {
> > +                static struct vlog_rate_limit rl
> > +                    = VLOG_RATE_LIMIT_INIT(5, 1);
> > +                VLOG_WARN_RL(&rl, "bad 'mac' %s", nbrp->mac);
> > +                continue;
> > +            }
> >
> > -                if (op->nbrp->ha_chassis_group ||
> > -                    op->nbrp->n_gateway_chassis) {
> > -                    /* Additional "derived" ovn_port crp represents the
> > -                     * instance of op on the gateway chassis. */
> > -                    const char *gw_chassis =
smap_get(&op->od->nbr->options,
> > -                                                   "chassis");
> > -                    if (gw_chassis) {
> > -                        static struct vlog_rate_limit rl
> > -                            = VLOG_RATE_LIMIT_INIT(1, 1);
> > -                        VLOG_WARN_RL(&rl, "Bad configuration:
distributed "
> > -                                     "gateway port configured on port
%s "
> > -                                     "on L3 gateway router",
nbrp->name);
> > -                        continue;
> > -                    }
> > +            if (!lrp_networks.n_ipv4_addrs &&
!lrp_networks.n_ipv6_addrs) {
> > +                continue;
> > +            }
> >
> > -                    char *redirect_name =
> > -                        ovn_chassis_redirect_name(nbrp->name);
> > -                    struct ovn_port *crp = ovn_port_find(ports,
redirect_name);
> > -                    if (crp && crp->sb && crp->sb->datapath == od->sb)
{
> > -                        ovn_port_set_nb(crp, NULL, nbrp);
> > -                        ovs_list_remove(&crp->list);
> > -                        ovs_list_push_back(both, &crp->list);
> > -                    } else {
> > -                        crp = ovn_port_create(ports, redirect_name,
> > -                                              NULL, nbrp, NULL);
> > -                        ovs_list_push_back(nb_only, &crp->list);
> > -                    }
> > -                    crp->l3dgw_port = op;
> > -                    op->cr_port = crp;
> > -                    crp->od = od;
> > -                    free(redirect_name);
> > +            struct ovn_port *op = ovn_port_find_bound(ports,
nbrp->name);
> > +            if (op && (op->od || op->nbsp || op->nbrp)) {
> > +                static struct vlog_rate_limit rl
> > +                    = VLOG_RATE_LIMIT_INIT(5, 1);
> > +                VLOG_WARN_RL(&rl, "duplicate logical router port %s",
> > +                             nbrp->name);
> > +                destroy_lport_addresses(&lrp_networks);
> > +                continue;
> > +            } else if (op && (!op->sb || op->sb->datapath == od->sb)) {
> > +                ovn_port_set_nb(op, NULL, nbrp);
> > +                ovs_list_remove(&op->list);
> > +                ovs_list_push_back(both, &op->list);
> > +
> > +                /* This port exists but should not have been
> > +                 * initialized fully. */
> > +                ovs_assert(!op->lrp_networks.n_ipv4_addrs
> > +                           && !op->lrp_networks.n_ipv6_addrs);
> > +            } else {
> > +                op = ovn_port_create(ports, nbrp->name, NULL, nbrp,
NULL);
> > +                ovs_list_push_back(nb_only, &op->list);
> > +            }
> >
> > -                    /* Add to l3dgw_ports in od, for later use during
flow
> > -                     * creation. */
> > -                    if (od->n_l3dgw_ports == n_allocated_l3dgw_ports) {
> > -                        od->l3dgw_ports = x2nrealloc(od->l3dgw_ports,
> > -
&n_allocated_l3dgw_ports,
> > -                                                     sizeof
*od->l3dgw_ports);
> > -                    }
> > -                    od->l3dgw_ports[od->n_l3dgw_ports++] = op;
> > +            op->lrp_networks = lrp_networks;
> > +            op->od = od;
> > +            ovs_list_push_back(&od->port_list, &op->dp_node);
> >
> > -                    assign_routable_addresses(op);
> > +            if (!od->redirect_bridged) {
> > +                const char *redirect_type =
> > +                    smap_get(&nbrp->options, "redirect-type");
> > +                od->redirect_bridged =
> > +                    redirect_type && !strcasecmp(redirect_type,
"bridged");
> > +            }
> > +
> > +            if (op->nbrp->ha_chassis_group ||
> > +                op->nbrp->n_gateway_chassis) {
> > +                /* Additional "derived" ovn_port crp represents the
> > +                 * instance of op on the gateway chassis. */
> > +                const char *gw_chassis =
smap_get(&op->od->nbr->options,
> > +                                               "chassis");
> > +                if (gw_chassis) {
> > +                    static struct vlog_rate_limit rl
> > +                        = VLOG_RATE_LIMIT_INIT(1, 1);
> > +                    VLOG_WARN_RL(&rl, "Bad configuration: distributed "
> > +                                 "gateway port configured on port %s "
> > +                                 "on L3 gateway router", nbrp->name);
> > +                    continue;
> >                  }
> > +
> > +                char *redirect_name =
> > +                    ovn_chassis_redirect_name(nbrp->name);
> > +                struct ovn_port *crp = ovn_port_find(ports,
redirect_name);
> > +                if (crp && crp->sb && crp->sb->datapath == od->sb) {
> > +                    ovn_port_set_nb(crp, NULL, nbrp);
> > +                    ovs_list_remove(&crp->list);
> > +                    ovs_list_push_back(both, &crp->list);
> > +                } else {
> > +                    crp = ovn_port_create(ports, redirect_name,
> > +                                          NULL, nbrp, NULL);
> > +                    ovs_list_push_back(nb_only, &crp->list);
> > +                }
> > +                crp->l3dgw_port = op;
> > +                op->cr_port = crp;
> > +                crp->od = od;
> > +                free(redirect_name);
> > +
> > +                /* Add to l3dgw_ports in od, for later use during flow
> > +                 * creation. */
> > +                if (od->n_l3dgw_ports == n_allocated_l3dgw_ports) {
> > +                    od->l3dgw_ports = x2nrealloc(od->l3dgw_ports,
> > +
&n_allocated_l3dgw_ports,
> > +                                                 sizeof
*od->l3dgw_ports);
> > +                }
> > +                od->l3dgw_ports[od->n_l3dgw_ports++] = op;
> > +
> > +                assign_routable_addresses(op);
> >              }
> >          }
> >      }
> > @@ -3704,14 +3739,14 @@ ovn_port_update_sbrec(struct northd_input
*input_data,
> >   * deleted. */
> >  static void
> >  cleanup_mac_bindings(struct northd_input *input_data,
> > -                     struct hmap *datapaths,
> > +                     struct hmap *lr_datapaths,
> >                       struct hmap *lr_ports)
> >  {
> >      const struct sbrec_mac_binding *b;
> >      SBREC_MAC_BINDING_TABLE_FOR_EACH_SAFE (b,
> >                               input_data->sbrec_mac_binding_table) {
> >          const struct ovn_datapath *od =
> > -            ovn_datapath_from_sbrec(datapaths, b->datapath);
> > +            ovn_datapath_from_sbrec(NULL, lr_datapaths, b->datapath);
> >
> >          if (!od || ovn_datapath_is_stale(od) ||
> >                  !ovn_port_find(lr_ports, b->logical_port)) {
> > @@ -3735,14 +3770,14 @@ cleanup_sb_ha_chassis_groups(struct
northd_input *input_data,
> >
> >  static void
> >  cleanup_stale_fdb_entries(struct northd_input *input_data,
> > -                          struct hmap *datapaths)
> > +                          struct hmap *ls_datapaths)
> >  {
> >      const struct sbrec_fdb *fdb_e;
> >      SBREC_FDB_TABLE_FOR_EACH_SAFE (fdb_e,
> >                           input_data->sbrec_fdb_table) {
> >          bool delete = true;
> >          struct ovn_datapath *od
> > -            = ovn_datapath_find_by_key(datapaths, fdb_e->dp_key);
> > +            = ovn_datapath_find_by_key(ls_datapaths, fdb_e->dp_key);
> >          if (od) {
> >              if (ovn_tnlid_present(&od->port_tnlids, fdb_e->port_key)) {
> >                  delete = false;
> > @@ -3988,8 +4023,8 @@ build_lrouter_lb_ips(struct ovn_lb_ip_set *lb_ips,
> >  }
> >
> >  static void
> > -build_lbs(struct northd_input *input_data, struct hmap *datapaths,
> > -          struct hmap *lbs, struct hmap *lb_groups)
> > +build_lbs(struct northd_input *input_data, struct hmap *ls_datapaths,
> > +          struct hmap *lr_datapaths, struct hmap *lbs, struct hmap
*lb_groups)
> >  {
> >      const struct nbrec_load_balancer_group *nbrec_lb_group;
> >      struct ovn_lb_group *lb_group;
> > @@ -4002,7 +4037,8 @@ build_lbs(struct northd_input *input_data, struct
hmap *datapaths,
> >      NBREC_LOAD_BALANCER_TABLE_FOR_EACH (nbrec_lb,
> >                                 input_data->nbrec_load_balancer_table) {
> >          struct ovn_northd_lb *lb_nb = ovn_northd_lb_create(nbrec_lb,
> > -
n_datapaths);
> > +
n_ls_datapaths,
> > +
n_lr_datapaths);
> >          hmap_insert(lbs, &lb_nb->hmap_node,
> >                      uuid_hash(&nbrec_lb->header_.uuid));
> >      }
> > @@ -4010,7 +4046,8 @@ build_lbs(struct northd_input *input_data, struct
hmap *datapaths,
> >      NBREC_LOAD_BALANCER_GROUP_TABLE_FOR_EACH (nbrec_lb_group,
> >
input_data->nbrec_load_balancer_group_table) {
> >          lb_group = ovn_lb_group_create(nbrec_lb_group, lbs,
> > -                                       hmap_count(datapaths));
> > +                                       hmap_count(ls_datapaths),
> > +                                       hmap_count(lr_datapaths));
> >
> >          for (size_t i = 0; i < lb_group->n_lbs; i++) {
> >              build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
> > @@ -4021,7 +4058,7 @@ build_lbs(struct northd_input *input_data, struct
hmap *datapaths,
> >      }
> >
> >      struct ovn_datapath *od;
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> >          if (!od->nbs) {
> >              continue;
> >          }
> > @@ -4048,7 +4085,7 @@ build_lbs(struct northd_input *input_data, struct
hmap *datapaths,
> >          }
> >      }
> >
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > +    HMAP_FOR_EACH (od, key_node, lr_datapaths) {
> >          if (!od->nbr) {
> >              continue;
> >          }
> > @@ -4198,11 +4235,11 @@ build_lrouter_lb_reachable_ips(struct
ovn_datapath *od,
> >  }
> >
> >  static void
> > -build_lrouter_lbs_check(const struct hmap *datapaths)
> > +build_lrouter_lbs_check(const struct hmap *lr_datapaths)
> >  {
> >      struct ovn_datapath *od;
> >
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > +    HMAP_FOR_EACH (od, key_node, lr_datapaths) {
> >          if (!od->nbr) {
> >              continue;
> >          }
> > @@ -4221,12 +4258,12 @@ build_lrouter_lbs_check(const struct hmap
*datapaths)
> >  }
> >
> >  static void
> > -build_lrouter_lbs_reachable_ips(struct hmap *datapaths, struct hmap
*lbs,
> > +build_lrouter_lbs_reachable_ips(struct hmap *lr_datapaths, struct hmap
*lbs,
> >                                  struct hmap *lb_groups)
> >  {
> >      struct ovn_datapath *od;
> >
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > +    HMAP_FOR_EACH (od, key_node, lr_datapaths) {
> >          if (!od->nbr) {
> >              continue;
> >          }
> > @@ -4263,8 +4300,8 @@ build_lswitch_lbs_from_lrouter(struct hmap *lbs,
struct hmap *lb_groups)
> >      size_t index;
> >
> >      HMAP_FOR_EACH (lb, hmap_node, lbs) {
> > -        BITMAP_FOR_EACH_1 (index, n_datapaths, lb->nb_lr_map) {
> > -            struct ovn_datapath *od = datapaths_array[index];
> > +        BITMAP_FOR_EACH_1 (index, n_lr_datapaths, lb->nb_lr_map) {
> > +            struct ovn_datapath *od = lr_datapaths_array[index];
> >              ovn_northd_lb_add_ls(lb, od->n_ls_peers, od->ls_peers);
> >          }
> >      }
> > @@ -4288,8 +4325,8 @@ build_lb_count_dps(struct hmap *lbs)
> >      struct ovn_northd_lb *lb;
> >
> >      HMAP_FOR_EACH (lb, hmap_node, lbs) {
> > -        lb->n_nb_lr = bitmap_count1(lb->nb_lr_map, n_datapaths);
> > -        lb->n_nb_ls = bitmap_count1(lb->nb_ls_map, n_datapaths);
> > +        lb->n_nb_lr = bitmap_count1(lb->nb_lr_map, n_lr_datapaths);
> > +        lb->n_nb_ls = bitmap_count1(lb->nb_ls_map, n_ls_datapaths);
> >      }
> >  }
> >
> > @@ -4298,13 +4335,13 @@ build_lb_count_dps(struct hmap *lbs)
> >   * networks to have been parsed.
> >   */
> >  static void
> > -build_lb_port_related_data(struct hmap *datapaths, struct hmap
*ls_ports,
> > +build_lb_port_related_data(struct hmap *lr_datapaths, struct hmap
*ls_ports,
> >                             struct hmap *lbs, struct hmap *lb_groups,
> >                             struct northd_input *input_data,
> >                             struct ovsdb_idl_txn *ovnsb_txn)
> >  {
> > -    build_lrouter_lbs_check(datapaths);
> > -    build_lrouter_lbs_reachable_ips(datapaths, lbs, lb_groups);
> > +    build_lrouter_lbs_check(lr_datapaths);
> > +    build_lrouter_lbs_reachable_ips(lr_datapaths, lbs, lb_groups);
> >      build_lb_svcs(input_data, ovnsb_txn, ls_ports, lbs);
> >      build_lswitch_lbs_from_lrouter(lbs, lb_groups);
> >  }
> > @@ -4318,12 +4355,13 @@ struct ovn_dp_group {
> >
> >  static struct ovn_dp_group *
> >  ovn_dp_group_find(const struct hmap *dp_groups,
> > -                  const unsigned long *dpg_bitmap, uint32_t hash)
> > +                  const unsigned long *dpg_bitmap, size_t bitmap_len,
> > +                  uint32_t hash)
> >  {
> >      struct ovn_dp_group *dpg;
> >
> >      HMAP_FOR_EACH_WITH_HASH (dpg, node, hash, dp_groups) {
> > -        if (bitmap_equal(dpg->bitmap, dpg_bitmap, n_datapaths)) {
> > +        if (bitmap_equal(dpg->bitmap, dpg_bitmap, bitmap_len)) {
> >              return dpg;
> >          }
> >      }
> > @@ -4332,14 +4370,16 @@ ovn_dp_group_find(const struct hmap *dp_groups,
> >
> >  static struct sbrec_logical_dp_group *
> >  ovn_sb_insert_logical_dp_group(struct ovsdb_idl_txn *ovnsb_txn,
> > -                               const unsigned long *dpg_bitmap)
> > +                               const unsigned long *dpg_bitmap,
> > +                               size_t bitmap_len,
> > +                               struct ovn_datapath **datapaths_array)
> >  {
> >      struct sbrec_logical_dp_group *dp_group;
> >      const struct sbrec_datapath_binding **sb;
> >      size_t n = 0, index;
> >
> > -    sb = xmalloc(bitmap_count1(dpg_bitmap, n_datapaths) * sizeof *sb);
> > -    BITMAP_FOR_EACH_1 (index, n_datapaths, dpg_bitmap) {
> > +    sb = xmalloc(bitmap_count1(dpg_bitmap, bitmap_len) * sizeof *sb);
> > +    BITMAP_FOR_EACH_1 (index, bitmap_len, dpg_bitmap) {
> >          sb[n++] = datapaths_array[index]->sb;
> >      }
> >      dp_group = sbrec_logical_dp_group_insert(ovnsb_txn);
> > @@ -4355,7 +4395,7 @@ ovn_sb_insert_logical_dp_group(struct
ovsdb_idl_txn *ovnsb_txn,
> >   */
> >  static void
> >  sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn
*ovnsb_txn,
> > -         struct hmap *datapaths, struct hmap *lbs)
> > +         struct hmap *ls_datapaths, struct hmap *lbs)
> >  {
> >      struct hmap dp_groups = HMAP_INITIALIZER(&dp_groups);
> >      struct ovn_northd_lb *lb;
> > @@ -4401,11 +4441,11 @@ sync_lbs(struct northd_input *input_data,
struct ovsdb_idl_txn *ovnsb_txn,
> >          struct ovn_dp_group *dpg = xzalloc(sizeof *dpg);
> >          size_t i, n = 0;
> >
> > -        dpg->bitmap = bitmap_allocate(n_datapaths);
> > +        dpg->bitmap = bitmap_allocate(n_ls_datapaths);
> >          for (i = 0; i < dp_group->n_datapaths; i++) {
> >              struct ovn_datapath *datapath_od;
> >
> > -            datapath_od = ovn_datapath_from_sbrec(datapaths,
> > +            datapath_od = ovn_datapath_from_sbrec(ls_datapaths, NULL,
> >
 dp_group->datapaths[i]);
> >              if (!datapath_od || ovn_datapath_is_stale(datapath_od)) {
> >                  break;
> > @@ -4416,7 +4456,8 @@ sync_lbs(struct northd_input *input_data, struct
ovsdb_idl_txn *ovnsb_txn,
> >          if (i == dp_group->n_datapaths) {
> >              uint32_t hash = hash_int(n, 0);
> >
> > -            if (!ovn_dp_group_find(&dp_groups, dpg->bitmap, hash)) {
> > +            if (!ovn_dp_group_find(&dp_groups, dpg->bitmap,
n_ls_datapaths,
> > +                                   hash)) {
> >                  dpg->dp_group = dp_group;
> >                  hmap_insert(&dp_groups, &dpg->node, hash);
> >                  continue;
> > @@ -4459,12 +4500,15 @@ sync_lbs(struct northd_input *input_data,
struct ovsdb_idl_txn *ovnsb_txn,
> >          uint32_t hash;
> >
> >          hash = hash_int(lb->n_nb_ls, 0);
> > -        dpg = ovn_dp_group_find(&dp_groups, lb->nb_ls_map, hash);
> > +        dpg = ovn_dp_group_find(&dp_groups, lb->nb_ls_map,
n_ls_datapaths,
> > +                                hash);
> >          if (!dpg) {
> >              dpg = xzalloc(sizeof *dpg);
> >              dpg->dp_group = ovn_sb_insert_logical_dp_group(ovnsb_txn,
> > -
lb->nb_ls_map);
> > -            dpg->bitmap = bitmap_clone(lb->nb_ls_map, n_datapaths);
> > +
lb->nb_ls_map,
> > +
n_ls_datapaths,
> > +
ls_datapaths_array);
> > +            dpg->bitmap = bitmap_clone(lb->nb_ls_map, n_ls_datapaths);
> >              hmap_insert(&dp_groups, &dpg->node, hash);
> >          }
> >
> > @@ -4490,10 +4534,8 @@ sync_lbs(struct northd_input *input_data, struct
ovsdb_idl_txn *ovnsb_txn,
> >       * schema for compatibility reasons.  Reset it to empty, just in
case.
> >       */
> >      struct ovn_datapath *od;
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > -        if (!od->nbs) {
> > -            continue;
> > -        }
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> > +        ovs_assert(od->nbs);
> >
> >          if (od->sb->n_load_balancers) {
> >              sbrec_datapath_binding_set_load_balancers(od->sb, NULL, 0);
> > @@ -4572,8 +4614,8 @@ build_ports(struct northd_input *input_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 *ls_ports,
> > -            struct hmap *lr_ports)
> > +            struct hmap *ls_datapaths, struct hmap *lr_datapaths,
> > +            struct hmap *ls_ports, struct hmap *lr_ports)
> >  {
> >      struct ovs_list sb_only, nb_only, both;
> >      struct hmap tag_alloc_table = HMAP_INITIALIZER(&tag_alloc_table);
> > @@ -4586,8 +4628,8 @@ build_ports(struct northd_input *input_data,
> >      /* Borrow ls_ports for joining NB and SB for both LSPs and LRPs.
> >       * We will split them later. */
> >      struct hmap *ports = ls_ports;
> > -    join_logical_ports(input_data,
> > -                       datapaths, ports, &chassis_qdisc_queues,
> > +    join_logical_ports(input_data, ls_datapaths, lr_datapaths,
> > +                       ports, &chassis_qdisc_queues,
> >                         &tag_alloc_table, &sb_only, &nb_only, &both);
> >
> >      /* Purge stale Mac_Bindings if ports are deleted. */
> > @@ -4669,7 +4711,7 @@ build_ports(struct northd_input *input_data,
> >      }
> >
> >      if (remove_mac_bindings) {
> > -        cleanup_mac_bindings(input_data, datapaths, lr_ports);
> > +        cleanup_mac_bindings(input_data, lr_datapaths, lr_ports);
> >      }
> >
> >      tag_alloc_destroy(&tag_alloc_table);
> > @@ -5101,11 +5143,11 @@ static int parallelization_state = STATE_NULL;
> >
> >  static void
> >  ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
> > -               enum ovn_stage stage, uint16_t priority,
> > +               size_t dp_bitmap_len, enum ovn_stage stage, uint16_t
priority,
> >                 char *match, char *actions, char *io_port, char
*ctrl_meter,
> >                 char *stage_hint, const char *where)
> >  {
> > -    lflow->dpg_bitmap = bitmap_allocate(n_datapaths);
> > +    lflow->dpg_bitmap = bitmap_allocate(dp_bitmap_len);
> >      lflow->od = od;
> >      lflow->stage = stage;
> >      lflow->priority = priority;
> > @@ -5219,14 +5261,15 @@ static thread_local size_t thread_lflow_counter
= 0;
> >  static void
> >  ovn_dp_group_add_with_reference(struct ovn_lflow *lflow_ref,
> >                                  const struct ovn_datapath *od,
> > -                                const unsigned long *dp_bitmap)
> > +                                const unsigned long *dp_bitmap,
> > +                                size_t bitmap_len)
> >      OVS_REQUIRES(fake_hash_mutex)
> >  {
> >      if (od) {
> >          bitmap_set1(lflow_ref->dpg_bitmap, od->index);
> >      }
> >      if (dp_bitmap) {
> > -        bitmap_or(lflow_ref->dpg_bitmap, dp_bitmap, n_datapaths);
> > +        bitmap_or(lflow_ref->dpg_bitmap, dp_bitmap, bitmap_len);
> >      }
> >  }
> >
> > @@ -5246,10 +5289,13 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
struct ovn_datapath *od,
> >      struct ovn_lflow *old_lflow;
> >      struct ovn_lflow *lflow;
> >
> > +    size_t n_datapaths = ovn_stage_to_datapath_type(stage) ==
DP_SWITCH ?
> > +                            n_ls_datapaths : n_lr_datapaths;
> > +
> >      old_lflow = ovn_lflow_find(lflow_map, NULL, stage, priority, match,
> >                                 actions, ctrl_meter, hash);
> >      if (old_lflow) {
> > -        ovn_dp_group_add_with_reference(old_lflow, od, dp_bitmap);
> > +        ovn_dp_group_add_with_reference(old_lflow, od, dp_bitmap,
n_datapaths);
> >          return;
> >      }
> >
> > @@ -5257,13 +5303,13 @@ do_ovn_lflow_add(struct hmap *lflow_map, const
struct ovn_datapath *od,
> >      /* While adding new logical flows we're not setting single
datapath, but
> >       * collecting a group.  'od' will be updated later for all flows
with only
> >       * one datapath in a group, so it could be hashed correctly. */
> > -    ovn_lflow_init(lflow, NULL, stage, priority,
> > +    ovn_lflow_init(lflow, NULL, n_datapaths, stage, priority,
> >                     xstrdup(match), xstrdup(actions),
> >                     io_port ? xstrdup(io_port) : NULL,
> >                     nullable_xstrdup(ctrl_meter),
> >                     ovn_lflow_hint(stage_hint), where);
> >
> > -    ovn_dp_group_add_with_reference(lflow, od, dp_bitmap);
> > +    ovn_dp_group_add_with_reference(lflow, od, dp_bitmap, n_datapaths);
> >
> >      if (parallelization_state != STATE_USE_PARALLELIZATION) {
> >          hmap_insert(lflow_map, &lflow->hmap_node, hash);
> > @@ -5772,9 +5818,7 @@ static void
> >  build_lswitch_learn_fdb_od(
> >          struct ovn_datapath *od, struct hmap *lflows)
> >  {
> > -    if (!od->nbs) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbs);
> >      ovn_lflow_add(lflows, od, S_SWITCH_IN_LOOKUP_FDB, 0, "1", "next;");
> >      ovn_lflow_add(lflows, od, S_SWITCH_IN_PUT_FDB, 0, "1", "next;");
> >  }
> > @@ -5786,9 +5830,7 @@ static void
> >  build_lswitch_output_port_sec_od(struct ovn_datapath *od,
> >                                struct hmap *lflows)
> >  {
> > -    if (!od->nbs) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbs);
> >      ovn_lflow_add(lflows, od, S_SWITCH_OUT_CHECK_PORT_SEC, 100,
> >                    "eth.mcast", REGBIT_PORT_SEC_DROP" = 0; next;");
> >      ovn_lflow_add(lflows, od, S_SWITCH_OUT_CHECK_PORT_SEC, 0, "1",
> > @@ -7166,7 +7208,7 @@ build_lb_affinity_lr_flows(struct hmap *lflows,
struct ovn_northd_lb *lb,
> >                             char *lb_action, const unsigned long
*dp_bitmap)
> >  {
> >      if (!lb->affinity_timeout ||
> > -        bitmap_is_all_zeros(dp_bitmap, n_datapaths)) {
> > +        bitmap_is_all_zeros(dp_bitmap, n_lr_datapaths)) {
> >          return;
> >      }
> >
> > @@ -7476,9 +7518,7 @@ static void
> >  build_lswitch_lb_affinity_default_flows(struct ovn_datapath *od,
> >                                          struct hmap *lflows)
> >  {
> > -    if (!od->nbs) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbs);
> >      ovn_lflow_add(lflows, od, S_SWITCH_IN_LB_AFF_CHECK, 0, "1",
"next;");
> >      ovn_lflow_add(lflows, od, S_SWITCH_IN_LB_AFF_LEARN, 0, "1",
"next;");
> >  }
> > @@ -7487,9 +7527,7 @@ static void
> >  build_lrouter_lb_affinity_default_flows(struct ovn_datapath *od,
> >                                          struct hmap *lflows)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_LB_AFF_CHECK, 0, "1",
"next;");
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_LB_AFF_LEARN, 0, "1",
"next;");
> >  }
> > @@ -7539,9 +7577,9 @@ build_lb_rules(struct hmap *lflows, struct
ovn_northd_lb *lb,
> >          if (reject) {
> >              size_t index;
> >
> > -            dp_non_meter = bitmap_clone(lb->nb_ls_map, n_datapaths);
> > -            BITMAP_FOR_EACH_1 (index, n_datapaths, lb->nb_ls_map) {
> > -                struct ovn_datapath *od = datapaths_array[index];
> > +            dp_non_meter = bitmap_clone(lb->nb_ls_map, n_ls_datapaths);
> > +            BITMAP_FOR_EACH_1 (index, n_ls_datapaths, lb->nb_ls_map) {
> > +                struct ovn_datapath *od = ls_datapaths_array[index];
> >
> >                  meter = copp_meter_get(COPP_REJECT, od->nbs->copp,
> >                                         meter_groups);
> > @@ -7717,8 +7755,8 @@ build_vtep_hairpin(struct ovn_datapath *od,
struct hmap *lflows)
> >  static void
> >  build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows)
> >  {
> > -
> > -    if (!od->nbs || !od->nbs->n_forwarding_groups) {
> > +    ovs_assert(od->nbs);
> > +    if (!od->nbs->n_forwarding_groups) {
> >          return;
> >      }
> >      struct ds match = DS_EMPTY_INITIALIZER;
> > @@ -8399,9 +8437,7 @@ build_lswitch_lflows_pre_acl_and_acl(struct
ovn_datapath *od,
> >                                       struct hmap *lflows,
> >                                       const struct shash *meter_groups)
> >  {
> > -    if (!od->nbs) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbs);
> >      ls_get_acl_flags(od);
> >
> >      build_pre_acls(od, port_groups, lflows);
> > @@ -8421,9 +8457,7 @@ static void
> >  build_lswitch_lflows_admission_control(struct ovn_datapath *od,
> >                                         struct hmap *lflows)
> >  {
> > -    if (!od->nbs) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbs);
> >      /* Logical VLANs not supported. */
> >      if (!is_vlan_transparent(od)) {
> >          /* Block logical VLANs. */
> > @@ -8786,9 +8820,8 @@ static void
> >  build_lswitch_arp_nd_responder_default(struct ovn_datapath *od,
> >                                         struct hmap *lflows)
> >  {
> > -    if (od->nbs) {
> > -        ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1",
"next;");
> > -    }
> > +    ovs_assert(od->nbs);
> > +    ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;");
> >  }
> >
> >  /* Ingress table 19: ARP/ND responder for service monitor source ip.
> > @@ -8921,9 +8954,7 @@ static void
> >  build_lswitch_dhcp_and_dns_defaults(struct ovn_datapath *od,
> >                                          struct hmap *lflows)
> >  {
> > -    if (!od->nbs) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbs);
> >      ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_OPTIONS, 0, "1",
"next;");
> >      ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 0, "1",
"next;");
> >      ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 0, "1", "next;");
> > @@ -8939,7 +8970,8 @@ build_lswitch_dns_lookup_and_response(struct
ovn_datapath *od,
> >                                        struct hmap *lflows,
> >                                        const struct shash *meter_groups)
> >  {
> > -    if (!od->nbs || !ls_has_dns_records(od->nbs)) {
> > +    ovs_assert(od->nbs);
> > +    if (!ls_has_dns_records(od->nbs)) {
> >          return;
> >      }
> >      ovn_lflow_metered(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 100,
> > @@ -8986,9 +9018,7 @@ build_lswitch_destination_lookup_bmcast(struct
ovn_datapath *od,
> >                                          struct ds *actions,
> >                                          const struct shash
*meter_groups)
> >  {
> > -    if (!od->nbs) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbs);
> >
> >      ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 110,
> >                    "eth.dst == $svc_monitor_mac",
> > @@ -10650,14 +10680,14 @@ build_gw_lrouter_nat_flows_for_lb(struct
lrouter_nat_lb_flows_ctx *ctx,
> >      bool build_non_meter = false;
> >      size_t index;
> >
> > -    if (bitmap_is_all_zeros(dp_bitmap, n_datapaths)) {
> > +    if (bitmap_is_all_zeros(dp_bitmap, n_lr_datapaths)) {
> >          return;
> >      }
> >
> >      if (ctx->reject) {
> > -        dp_non_meter = bitmap_clone(dp_bitmap, n_datapaths);
> > -        BITMAP_FOR_EACH_1 (index, n_datapaths, dp_bitmap) {
> > -            struct ovn_datapath *od = datapaths_array[index];
> > +        dp_non_meter = bitmap_clone(dp_bitmap, n_lr_datapaths);
> > +        BITMAP_FOR_EACH_1 (index, n_lr_datapaths, dp_bitmap) {
> > +            struct ovn_datapath *od = lr_datapaths_array[index];
> >              const char *meter;
> >
> >              meter = copp_meter_get(COPP_REJECT, od->nbr->copp,
> > @@ -10795,15 +10825,15 @@ build_lrouter_nat_flows_for_lb(struct
ovn_lb_vip *lb_vip,
> >      unsigned long *dp_bitmap[LROUTER_NAT_LB_FLOW_MAX + 2];
> >
> >      for (size_t i = 0; i < LROUTER_NAT_LB_FLOW_MAX + 2; i++) {
> > -        dp_bitmap[i] = bitmap_allocate(n_datapaths);
> > +        dp_bitmap[i] = bitmap_allocate(n_lr_datapaths);
> >      }
> >
> >      /* Group gw router since we do not have datapath dependency in
> >       * lflow generation for them.
> >       */
> >      size_t index;
> > -    BITMAP_FOR_EACH_1 (index, n_datapaths, lb->nb_lr_map) {
> > -        struct ovn_datapath *od = datapaths_array[index];
> > +    BITMAP_FOR_EACH_1 (index, n_lr_datapaths, lb->nb_lr_map) {
> > +        struct ovn_datapath *od = lr_datapaths_array[index];
> >          enum lrouter_nat_lb_flow_type type;
> >
> >          if (lb->skip_snat) {
> > @@ -10897,8 +10927,8 @@ build_lswitch_flows_for_lb(struct ovn_northd_lb
*lb, struct hmap *lflows,
> >          }
> >
> >          size_t index;
> > -        BITMAP_FOR_EACH_1 (index, n_datapaths, lb->nb_ls_map) {
> > -            struct ovn_datapath *od = datapaths_array[index];
> > +        BITMAP_FOR_EACH_1 (index, n_ls_datapaths, lb->nb_ls_map) {
> > +            struct ovn_datapath *od = ls_datapaths_array[index];
> >
> >              ovn_lflow_add_with_hint__(lflows, od,
> >                                        S_SWITCH_IN_PRE_LB, 130,
ds_cstr(match),
> > @@ -11001,8 +11031,8 @@ build_lrouter_flows_for_lb(struct ovn_northd_lb
*lb, struct hmap *lflows,
> >              continue;
> >          }
> >
> > -        BITMAP_FOR_EACH_1 (index, n_datapaths, lb->nb_lr_map) {
> > -            struct ovn_datapath *od = datapaths_array[index];
> > +        BITMAP_FOR_EACH_1 (index, n_lr_datapaths, lb->nb_lr_map) {
> > +            struct ovn_datapath *od = lr_datapaths_array[index];
> >
> >              ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT,
> >                                        130, ds_cstr(match),
ds_cstr(action),
> > @@ -11015,8 +11045,8 @@ build_lrouter_flows_for_lb(struct ovn_northd_lb
*lb, struct hmap *lflows,
> >      }
> >
> >      if (lb->skip_snat) {
> > -        BITMAP_FOR_EACH_1 (index, n_datapaths, lb->nb_lr_map) {
> > -            struct ovn_datapath *od = datapaths_array[index];
> > +        BITMAP_FOR_EACH_1 (index, n_lr_datapaths, lb->nb_lr_map) {
> > +            struct ovn_datapath *od = lr_datapaths_array[index];
> >
> >              ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 120,
> >                            "flags.skip_snat_for_lb == 1 && ip",
"next;");
> > @@ -11619,9 +11649,7 @@ static void
> >  build_adm_ctrl_flows_for_lrouter(
> >          struct ovn_datapath *od, struct hmap *lflows)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      /* Logical VLANs not supported.
> >       * Broadcast/multicast source address is invalid. */
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 100,
> > @@ -11776,9 +11804,7 @@ build_neigh_learning_flows_for_lrouter(
> >          struct ds *match, struct ds *actions,
> >          const struct shash *meter_groups)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >
> >      /* Learn MAC bindings from ARP/IPv6 ND.
> >       *
> > @@ -12066,10 +12092,9 @@ build_ND_RA_flows_for_lrouter_port(
> >  static void
> >  build_ND_RA_flows_for_lrouter(struct ovn_datapath *od, struct hmap
*lflows)
> >  {
> > -    if (od->nbr) {
> > -        ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_OPTIONS, 0, "1",
"next;");
> > -        ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_RESPONSE, 0, "1",
"next;");
> > -    }
> > +    ovs_assert(od->nbr);
> > +    ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_OPTIONS, 0, "1",
"next;");
> > +    ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_RESPONSE, 0, "1",
"next;");
> >  }
> >
> >  /* Logical router ingress table IP_ROUTING_PRE:
> > @@ -12078,10 +12103,9 @@ static void
> >  build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
> >                                         struct hmap *lflows)
> >  {
> > -    if (od->nbr) {
> > -        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING_PRE, 0, "1",
> > -                      REG_ROUTE_TABLE_ID" = 0; next;");
> > -    }
> > +    ovs_assert(od->nbr);
> > +    ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING_PRE, 0, "1",
> > +                  REG_ROUTE_TABLE_ID" = 0; next;");
> >  }
> >
> >  /* Logical router ingress table IP_ROUTING : IP Routing.
> > @@ -12172,9 +12196,7 @@ build_static_route_flows_for_lrouter(
> >          struct hmap *lflows, const struct hmap *lr_ports,
> >          const struct hmap *bfd_connections)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      ovn_lflow_add_default_drop(lflows, od,
S_ROUTER_IN_IP_ROUTING_ECMP);
> >      ovn_lflow_add_default_drop(lflows, od, S_ROUTER_IN_IP_ROUTING);
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING_ECMP, 150,
> > @@ -12238,9 +12260,7 @@ build_mcast_lookup_flows_for_lrouter(
> >          struct ovn_datapath *od, struct hmap *lflows,
> >          struct ds *match, struct ds *actions)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >
> >      /* Drop IPv6 multicast traffic that shouldn't be forwarded,
> >       * i.e., router solicitation and router advertisement.
> > @@ -12341,9 +12361,7 @@ build_ingress_policy_flows_for_lrouter(
> >          struct ovn_datapath *od, struct hmap *lflows,
> >          const struct hmap *lr_ports)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      /* This is a catch-all rule. It has the lowest priority (0)
> >       * does a match-all("1") and pass-through (next) */
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_POLICY, 0, "1",
> > @@ -12376,9 +12394,7 @@ static void
> >  build_arp_resolve_flows_for_lrouter(
> >          struct ovn_datapath *od, struct hmap *lflows)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      /* Multicast packets already have the outport set so just advance
to
> >       * next table (priority 500). */
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 500,
> > @@ -12808,9 +12824,7 @@ build_check_pkt_len_flows_for_lrouter(
> >          struct ds *match, struct ds *actions,
> >          const struct shash *meter_groups)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >
> >      /* Packets are allowed by default. */
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 0, "1",
> > @@ -12835,9 +12849,7 @@ build_gateway_redirect_flows_for_lrouter(
> >          struct ovn_datapath *od, struct hmap *lflows,
> >          struct ds *match, struct ds *actions)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
> >          if (l3dgw_port_has_associated_vtep_lports(od->l3dgw_ports[i]))
{
> >              /* Skip adding redirect lflow for vtep-enabled l3dgw ports.
> > @@ -12924,9 +12936,7 @@ build_arp_request_flows_for_lrouter(
> >          struct ds *match, struct ds *actions,
> >          const struct shash *meter_groups)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      for (int i = 0; i < od->nbr->n_static_routes; i++) {
> >          const struct nbrec_logical_router_static_route *route;
> >
> > @@ -13044,9 +13054,7 @@ static void
> >  build_misc_local_traffic_drop_flows_for_lrouter(
> >          struct ovn_datapath *od, struct hmap *lflows)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      /* Allow IGMP and MLD packets (with TTL = 1) if the router is
> >       * configured to flood them statically on some ports.
> >       */
> > @@ -13300,9 +13308,7 @@ build_lrouter_arp_nd_for_datapath(struct
ovn_datapath *od,
> >                                    struct hmap *lflows,
> >                                    const struct shash *meter_groups)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >      /* Priority-90-92 flows handle ARP requests and ND packets. Most
are
> >       * per logical port but DNAT addresses can be handled per datapath
> >       * for non gateway router ports.
> > @@ -14265,9 +14271,7 @@ build_lrouter_nat_defrag_and_lb(struct
ovn_datapath *od, struct hmap *lflows,
> >                                  const struct shash *meter_groups,
> >                                  const struct chassis_features
*features)
> >  {
> > -    if (!od->nbr) {
> > -        return;
> > -    }
> > +    ovs_assert(od->nbr);
> >
> >      /* Packets are allowed by default. */
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 0, "1", "next;");
> > @@ -14590,7 +14594,8 @@ build_lrouter_nat_defrag_and_lb(struct
ovn_datapath *od, struct hmap *lflows,
> >
> >
> >  struct lswitch_flow_build_info {
> > -    const struct hmap *datapaths;
> > +    const struct hmap *ls_datapaths;
> > +    const struct hmap *lr_datapaths;
> >      const struct hmap *ls_ports;
> >      const struct hmap *lr_ports;
> >      const struct hmap *port_groups;
> > @@ -14608,17 +14613,16 @@ struct lswitch_flow_build_info {
> >  };
> >
> >  /* Helper function to combine all lflow generation which is iterated by
> > - * datapath.
> > + * logical switch datapath.
> >   *
> >   * When extending the function new "work data" must be added to the lsi
> >   * struct, not passed as an argument.
> >   */
> > -
> >  static void
> > -build_lswitch_and_lrouter_iterate_by_od(struct ovn_datapath *od,
> > +build_lswitch_and_lrouter_iterate_by_ls(struct ovn_datapath *od,
> >                                          struct lswitch_flow_build_info
*lsi)
> >  {
> > -    /* Build Logical Switch Flows. */
> > +    ovs_assert(od->nbs);
> >      build_lswitch_lflows_pre_acl_and_acl(od, lsi->port_groups,
> >                                           lsi->features,
> >                                           lsi->lflows,
> > @@ -14634,8 +14638,16 @@ build_lswitch_and_lrouter_iterate_by_od(struct
ovn_datapath *od,
> >                                              lsi->meter_groups);
> >      build_lswitch_output_port_sec_od(od, lsi->lflows);
> >      build_lswitch_lb_affinity_default_flows(od, lsi->lflows);
> > +}
> >
> > -    /* Build Logical Router Flows. */
> > +/* Helper function to combine all lflow generation which is iterated by
> > + * logical router datapath.
> > + */
> > +static void
> > +build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
> > +                                        struct lswitch_flow_build_info
*lsi)
> > +{
> > +    ovs_assert(od->nbr);
> >      build_adm_ctrl_flows_for_lrouter(od, lsi->lflows);
> >      build_neigh_learning_flows_for_lrouter(od, lsi->lflows,
&lsi->match,
> >                                             &lsi->actions,
lsi->meter_groups);
> > @@ -14748,14 +14760,27 @@ build_lflows_thread(void *arg)
> >          if (lsi) {
> >              /* Iterate over bucket ThreadID, ThreadID+size, ... */
> >              for (bnum = control->id;
> > -                    bnum <= lsi->datapaths->mask;
> > +                    bnum <= lsi->ls_datapaths->mask;
> > +                    bnum += control->pool->size)
> > +            {
> > +                HMAP_FOR_EACH_IN_PARALLEL (od, key_node, bnum,
> > +                                           lsi->ls_datapaths) {
> > +                    if (stop_parallel_processing()) {
> > +                        return NULL;
> > +                    }
> > +                    build_lswitch_and_lrouter_iterate_by_ls(od, lsi);
> > +                }
> > +            }
> > +            for (bnum = control->id;
> > +                    bnum <= lsi->lr_datapaths->mask;
> >                      bnum += control->pool->size)
> >              {
> > -                HMAP_FOR_EACH_IN_PARALLEL (od, key_node, bnum,
lsi->datapaths) {
> > +                HMAP_FOR_EACH_IN_PARALLEL (od, key_node, bnum,
> > +                                           lsi->lr_datapaths) {
> >                      if (stop_parallel_processing()) {
> >                          return NULL;
> >                      }
> > -                    build_lswitch_and_lrouter_iterate_by_od(od, lsi);
> > +                    build_lswitch_and_lrouter_iterate_by_lr(od, lsi);
> >                  }
> >              }
> >              for (bnum = control->id;
> > @@ -14859,7 +14884,8 @@ fix_flow_map_size(struct hmap *lflow_map,
> >  }
> >
> >  static void
> > -build_lswitch_and_lrouter_flows(const struct hmap *datapaths,
> > +build_lswitch_and_lrouter_flows(const struct hmap *ls_datapaths,
> > +                                const struct hmap *lr_datapaths,
> >                                  const struct hmap *ls_ports,
> >                                  const struct hmap *lr_ports,
> >                                  const struct hmap *port_groups,
> > @@ -14887,7 +14913,8 @@ build_lswitch_and_lrouter_flows(const struct
hmap *datapaths,
> >               * on a per-bucket level.
> >               */
> >              lsiv[index].lflows = lflows;
> > -            lsiv[index].datapaths = datapaths;
> > +            lsiv[index].ls_datapaths = ls_datapaths;
> > +            lsiv[index].lr_datapaths = lr_datapaths;
> >              lsiv[index].ls_ports = ls_ports;
> >              lsiv[index].lr_ports = lr_ports;
> >              lsiv[index].port_groups = port_groups;
> > @@ -14920,7 +14947,8 @@ build_lswitch_and_lrouter_flows(const struct
hmap *datapaths,
> >          struct ovn_northd_lb *lb;
> >          struct ovn_igmp_group *igmp_group;
> >          struct lswitch_flow_build_info lsi = {
> > -            .datapaths = datapaths,
> > +            .ls_datapaths = ls_datapaths,
> > +            .lr_datapaths = lr_datapaths,
> >              .ls_ports = ls_ports,
> >              .lr_ports = lr_ports,
> >              .port_groups = port_groups,
> > @@ -14940,8 +14968,11 @@ build_lswitch_and_lrouter_flows(const struct
hmap *datapaths,
> >           * will move here and will be reogranized by iterator type.
> >           */
> >          stopwatch_start(LFLOWS_DATAPATHS_STOPWATCH_NAME, time_msec());
> > -        HMAP_FOR_EACH (od, key_node, datapaths) {
> > -            build_lswitch_and_lrouter_iterate_by_od(od, &lsi);
> > +        HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> > +            build_lswitch_and_lrouter_iterate_by_ls(od, &lsi);
> > +        }
> > +        HMAP_FOR_EACH (od, key_node, lr_datapaths) {
> > +            build_lswitch_and_lrouter_iterate_by_lr(od, &lsi);
> >          }
> >          stopwatch_stop(LFLOWS_DATAPATHS_STOPWATCH_NAME, time_msec());
> >          stopwatch_start(LFLOWS_PORTS_STOPWATCH_NAME, time_msec());
> > @@ -14978,7 +15009,7 @@ build_lswitch_and_lrouter_flows(const struct
hmap *datapaths,
> >      }
> >
> >      free(svc_check_match);
> > -    build_lswitch_flows(datapaths, lflows);
> > +    build_lswitch_flows(ls_datapaths, lflows);
> >  }
> >
> >  static void
> > @@ -14986,12 +15017,14 @@ ovn_sb_set_lflow_logical_dp_group(
> >      struct ovsdb_idl_txn *ovnsb_txn,
> >      struct hmap *dp_groups,
> >      const struct sbrec_logical_flow *sbflow,
> > -    const unsigned long *dpg_bitmap)
> > +    const unsigned long *dpg_bitmap,
> > +    size_t bitmap_len,
> > +    struct ovn_datapath **datapaths_array)
> >  {
> >      struct ovn_dp_group *dpg;
> >      size_t n_ods;
> >
> > -    n_ods = bitmap_count1(dpg_bitmap, n_datapaths);
> > +    n_ods = bitmap_count1(dpg_bitmap, bitmap_len);
> >
> >      if (!n_ods) {
> >          sbrec_logical_flow_set_logical_dp_group(sbflow, NULL);
> > @@ -15000,11 +15033,14 @@ ovn_sb_set_lflow_logical_dp_group(
> >
> >      ovs_assert(n_ods != 1);
> >
> > -    dpg = ovn_dp_group_find(dp_groups, dpg_bitmap, hash_int(n_ods, 0));
> > +    dpg = ovn_dp_group_find(dp_groups, dpg_bitmap, bitmap_len,
> > +                            hash_int(n_ods, 0));
> >      ovs_assert(dpg != NULL);
> >
> >      if (!dpg->dp_group) {
> > -        dpg->dp_group = ovn_sb_insert_logical_dp_group(ovnsb_txn,
dpg->bitmap);
> > +        dpg->dp_group = ovn_sb_insert_logical_dp_group(ovnsb_txn,
dpg->bitmap,
> > +                                                       bitmap_len,
> > +
datapaths_array);
> >      }
> >      sbrec_logical_flow_set_logical_dp_group(sbflow, dpg->dp_group);
> >  }
> > @@ -15031,7 +15067,7 @@ void run_update_worker_pool(int n_threads)
> >
> >  static void
> >  build_mcast_groups(struct lflow_input *data,
> > -                   const struct hmap *datapaths,
> > +                   const struct hmap *ls_datapaths,
> >                     const struct hmap *ls_ports,
> >                     const struct hmap *lr_ports,
> >                     struct hmap *mcast_groups,
> > @@ -15046,13 +15082,14 @@ void build_lflows(struct lflow_input
*input_data,
> >      struct hmap mcast_groups;
> >      struct hmap igmp_groups;
> >
> > -    build_mcast_groups(input_data, input_data->datapaths,
> > +    build_mcast_groups(input_data, input_data->ls_datapaths,
> >                         input_data->ls_ports, input_data->lr_ports,
> >                         &mcast_groups, &igmp_groups);
> >
> >      fast_hmap_size_for(&lflows, max_seen_lflow_size);
> >
> > -    build_lswitch_and_lrouter_flows(input_data->datapaths,
> > +    build_lswitch_and_lrouter_flows(input_data->ls_datapaths,
> > +                                    input_data->lr_datapaths,
> >                                      input_data->ls_ports,
> >                                      input_data->lr_ports,
> >                                      input_data->port_groups, &lflows,
> > @@ -15076,7 +15113,8 @@ void build_lflows(struct lflow_input
*input_data,
> >
> >      stopwatch_start(LFLOWS_DP_GROUPS_STOPWATCH_NAME, time_msec());
> >      /* Collecting all unique datapath groups. */
> > -    struct hmap dp_groups = HMAP_INITIALIZER(&dp_groups);
> > +    struct hmap ls_dp_groups = HMAP_INITIALIZER(&ls_dp_groups);
> > +    struct hmap lr_dp_groups = HMAP_INITIALIZER(&lr_dp_groups);
> >      struct hmap single_dp_lflows;
> >
> >      /* Single dp_flows will never grow bigger than lflows,
> > @@ -15092,6 +15130,19 @@ void build_lflows(struct lflow_input
*input_data,
> >          struct ovn_dp_group *dpg;
> >          uint32_t hash, n_ods;
> >
> > +        struct hmap *dp_groups;
> > +        size_t n_datapaths;
> > +        struct ovn_datapath **datapaths_array;
> > +        if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH) {
> > +            n_datapaths = n_ls_datapaths;
> > +            dp_groups = &ls_dp_groups;
> > +            datapaths_array = ls_datapaths_array;
> > +        } else {
> > +            n_datapaths = n_lr_datapaths;
> > +            dp_groups = &lr_dp_groups;
> > +            datapaths_array = lr_datapaths_array;
> > +        }
> > +
> >          n_ods = bitmap_count1(lflow->dpg_bitmap, n_datapaths);
> >
> >          ovs_assert(n_ods);
> > @@ -15117,11 +15168,12 @@ void build_lflows(struct lflow_input
*input_data,
> >          }
> >
> >          hash = hash_int(n_ods, 0);
> > -        dpg = ovn_dp_group_find(&dp_groups, lflow->dpg_bitmap, hash);
> > +        dpg = ovn_dp_group_find(dp_groups, lflow->dpg_bitmap,
n_datapaths,
> > +                                hash);
> >          if (!dpg) {
> >              dpg = xzalloc(sizeof *dpg);
> >              dpg->bitmap = bitmap_clone(lflow->dpg_bitmap, n_datapaths);
> > -            hmap_insert(&dp_groups, &dpg->node, hash);
> > +            hmap_insert(dp_groups, &dpg->node, hash);
> >          }
> >          lflow->dpg = dpg;
> >      }
> > @@ -15147,7 +15199,9 @@ void build_lflows(struct lflow_input
*input_data,
> >          struct sbrec_datapath_binding *dp = sbflow->logical_datapath;
> >          if (dp) {
> >              logical_datapath_od = ovn_datapath_from_sbrec(
> > -                                            input_data->datapaths, dp);
> > +                                            input_data->ls_datapaths,
> > +                                            input_data->lr_datapaths,
> > +                                            dp);
> >              if (logical_datapath_od
> >                  && ovn_datapath_is_stale(logical_datapath_od)) {
> >                  logical_datapath_od = NULL;
> > @@ -15155,7 +15209,9 @@ void build_lflows(struct lflow_input
*input_data,
> >          }
> >          for (i = 0; dp_group && i < dp_group->n_datapaths; i++) {
> >              logical_datapath_od = ovn_datapath_from_sbrec(
> > -                             input_data->datapaths,
dp_group->datapaths[i]);
> > +                                            input_data->ls_datapaths,
> > +                                            input_data->lr_datapaths,
> > +                                            dp_group->datapaths[i]);
> >              if (logical_datapath_od
> >                  && !ovn_datapath_is_stale(logical_datapath_od)) {
> >                  break;
> > @@ -15179,6 +15235,18 @@ void build_lflows(struct lflow_input
*input_data,
> >              sbflow->priority, sbflow->match, sbflow->actions,
> >              sbflow->controller_meter, sbflow->hash);
> >          if (lflow) {
> > +            struct hmap *dp_groups;
> > +            size_t n_datapaths;
> > +            struct ovn_datapath **datapaths_array;
> > +            if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH)
{
> > +                n_datapaths = n_ls_datapaths;
> > +                dp_groups = &ls_dp_groups;
> > +                datapaths_array = ls_datapaths_array;
> > +            } else {
> > +                n_datapaths = n_lr_datapaths;
> > +                dp_groups = &lr_dp_groups;
> > +                datapaths_array = lr_datapaths_array;
> > +            }
> >              if (input_data->ovn_internal_version_changed) {
> >                  const char *stage_name =
smap_get_def(&sbflow->external_ids,
> >                                                    "stage-name", "");
> > @@ -15231,7 +15299,8 @@ void build_lflows(struct lflow_input
*input_data,
> >                  /* Check all logical datapaths from the group. */
> >                  for (i = 0; i < dp_group->n_datapaths; i++) {
> >                      od = ovn_datapath_from_sbrec(
> > -                            input_data->datapaths,
dp_group->datapaths[i]);
> > +                            input_data->ls_datapaths,
input_data->lr_datapaths,
> > +                            dp_group->datapaths[i]);
> >                      if (!od || ovn_datapath_is_stale(od)) {
> >                          continue;
> >                      }
> > @@ -15244,8 +15313,10 @@ void build_lflows(struct lflow_input
*input_data,
> >              }
> >
> >              if (update_dp_group) {
> > -                ovn_sb_set_lflow_logical_dp_group(ovnsb_txn,
&dp_groups,
> > -                                                  sbflow,
lflow->dpg_bitmap);
> > +                ovn_sb_set_lflow_logical_dp_group(ovnsb_txn, dp_groups,
> > +                                                  sbflow,
lflow->dpg_bitmap,
> > +                                                  n_datapaths,
> > +                                                  datapaths_array);
> >              } else if (lflow->dpg && !lflow->dpg->dp_group) {
> >                  /* Setting relation between unique datapath group and
> >                   * Sb DB datapath goup. */
> > @@ -15263,12 +15334,26 @@ void build_lflows(struct lflow_input
*input_data,
> >          const char *pipeline =
ovn_stage_get_pipeline_name(lflow->stage);
> >          uint8_t table = ovn_stage_get_table(lflow->stage);
> >
> > +        struct hmap *dp_groups;
> > +        size_t n_datapaths;
> > +        struct ovn_datapath **datapaths_array;
> > +        if (ovn_stage_to_datapath_type(lflow->stage) == DP_SWITCH) {
> > +            n_datapaths = n_ls_datapaths;
> > +            dp_groups = &ls_dp_groups;
> > +            datapaths_array = ls_datapaths_array;
> > +        } else {
> > +            n_datapaths = n_lr_datapaths;
> > +            dp_groups = &lr_dp_groups;
> > +            datapaths_array = lr_datapaths_array;
> > +        }
> > +
> >          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(ovnsb_txn, &dp_groups,
> > -                                          sbflow, lflow->dpg_bitmap);
> > +        ovn_sb_set_lflow_logical_dp_group(ovnsb_txn, dp_groups,
> > +                                          sbflow, lflow->dpg_bitmap,
> > +                                          n_datapaths,
datapaths_array);
> >          sbrec_logical_flow_set_pipeline(sbflow, pipeline);
> >          sbrec_logical_flow_set_table_id(sbflow, table);
> >          sbrec_logical_flow_set_priority(sbflow, lflow->priority);
> > @@ -15309,18 +15394,25 @@ void build_lflows(struct lflow_input
*input_data,
> >
> >      stopwatch_stop(LFLOWS_TO_SB_STOPWATCH_NAME, time_msec());
> >      struct ovn_dp_group *dpg;
> > -    HMAP_FOR_EACH_POP (dpg, node, &dp_groups) {
> > +    HMAP_FOR_EACH_POP (dpg, node, &ls_dp_groups) {
> >          bitmap_free(dpg->bitmap);
> >          free(dpg);
> >      }
> > -    hmap_destroy(&dp_groups);
> > +    hmap_destroy(&ls_dp_groups);
> > +    HMAP_FOR_EACH_POP (dpg, node, &lr_dp_groups) {
> > +        bitmap_free(dpg->bitmap);
> > +        free(dpg);
> > +    }
> > +    hmap_destroy(&lr_dp_groups);
> >
> >      /* Push changes to the Multicast_Group table to database. */
> >      const struct sbrec_multicast_group *sbmc;
> >      SBREC_MULTICAST_GROUP_TABLE_FOR_EACH_SAFE (sbmc,
> >
 input_data->sbrec_multicast_group_table) {
> >          struct ovn_datapath *od = ovn_datapath_from_sbrec(
> > -                               input_data->datapaths, sbmc->datapath);
> > +
input_data->ls_datapaths,
> > +
input_data->lr_datapaths,
> > +                                               sbmc->datapath);
> >
> >          if (!od || ovn_datapath_is_stale(od)) {
> >              sbrec_multicast_group_delete(sbmc);
> > @@ -15705,12 +15797,13 @@ get_dns_info_from_hmap(struct hmap *dns_map,
struct uuid *uuid)
> >  static void
> >  sync_dns_entries(struct northd_input *input_data,
> >                   struct ovsdb_idl_txn *ovnsb_txn,
> > -                 struct hmap *datapaths)
> > +                 struct hmap *ls_datapaths)
> >  {
> >      struct hmap dns_map = HMAP_INITIALIZER(&dns_map);
> >      struct ovn_datapath *od;
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > -        if (!od->nbs || !od->nbs->n_dns_records) {
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> > +        ovs_assert(od->nbs);
> > +        if (!od->nbs->n_dns_records) {
> >              continue;
> >          }
> >
> > @@ -15828,9 +15921,9 @@ sync_template_vars(struct northd_input
*input_data,
> >  }
> >
> >  static void
> > -destroy_datapaths_and_ports(struct hmap *datapaths, struct hmap
*ls_ports,
> > -                            struct hmap *lr_ports,
> > -                            struct ovs_list *lr_list)
> > +destroy_datapaths_and_ports(struct hmap *ls_datapaths,
> > +                            struct hmap *lr_datapaths, struct hmap
*ls_ports,
> > +                            struct hmap *lr_ports, struct ovs_list
*lr_list)
> >  {
> >      struct ovn_datapath *router_dp;
> >      LIST_FOR_EACH_POP (router_dp, lr_list, lr_list) {
> > @@ -15848,10 +15941,15 @@ destroy_datapaths_and_ports(struct hmap
*datapaths, struct hmap *ls_ports,
> >      }
> >
> >      struct ovn_datapath *dp;
> > -    HMAP_FOR_EACH_SAFE (dp, key_node, datapaths) {
> > -        ovn_datapath_destroy(datapaths, dp);
> > +    HMAP_FOR_EACH_SAFE (dp, key_node, ls_datapaths) {
> > +        ovn_datapath_destroy(ls_datapaths, dp);
> > +    }
> > +    hmap_destroy(ls_datapaths);
> > +
> > +    HMAP_FOR_EACH_SAFE (dp, key_node, lr_datapaths) {
> > +        ovn_datapath_destroy(lr_datapaths, dp);
> >      }
> > -    hmap_destroy(datapaths);
> > +    hmap_destroy(lr_datapaths);
> >
> >      struct ovn_port *port;
> >      HMAP_FOR_EACH_SAFE (port, key_node, ls_ports) {
> > @@ -15868,14 +15966,12 @@ destroy_datapaths_and_ports(struct hmap
*datapaths, struct hmap *ls_ports,
> >  static void
> >  build_ip_mcast(struct northd_input *input_data,
> >                 struct ovsdb_idl_txn *ovnsb_txn,
> > -               struct hmap *datapaths)
> > +               struct hmap *ls_datapaths)
> >  {
> >      struct ovn_datapath *od;
> >
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > -        if (!od->nbs) {
> > -            continue;
> > -        }
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> > +        ovs_assert(od->nbs);
> >
> >          const struct sbrec_ip_multicast *ip_mcast =
> >              ip_mcast_lookup(input_data->sbrec_ip_mcast_by_dp, od->sb);
> > @@ -15891,7 +15987,7 @@ build_ip_mcast(struct northd_input *input_data,
> >
> >      SBREC_IP_MULTICAST_TABLE_FOR_EACH_SAFE (sb,
> >
input_data->sbrec_ip_multicast_table) {
> > -        od = ovn_datapath_from_sbrec(datapaths, sb->datapath);
> > +        od = ovn_datapath_from_sbrec(ls_datapaths, NULL, sb->datapath);
> >          if (!od || ovn_datapath_is_stale(od)) {
> >              sbrec_ip_multicast_delete(sb);
> >          }
> > @@ -15900,7 +15996,7 @@ build_ip_mcast(struct northd_input *input_data,
> >
> >  static void
> >  build_mcast_groups(struct lflow_input *input_data,
> > -                   const struct hmap *datapaths,
> > +                   const struct hmap *ls_datapaths,
> >                     const struct hmap *ls_ports,
> >                     const struct hmap *lr_ports,
> >                     struct hmap *mcast_groups,
> > @@ -15912,7 +16008,7 @@ build_mcast_groups(struct lflow_input
*input_data,
> >      hmap_init(igmp_groups);
> >      struct ovn_datapath *od;
> >
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> >          init_mcast_flow_count(od);
> >      }
> >
> > @@ -15976,7 +16072,7 @@ build_mcast_groups(struct lflow_input
*input_data,
> >          }
> >
> >          /* If the datapath value is stale, purge the group. */
> > -        od = ovn_datapath_from_sbrec(datapaths, sb_igmp->datapath);
> > +        od = ovn_datapath_from_sbrec(ls_datapaths, NULL,
sb_igmp->datapath);
> >
> >          if (!od || ovn_datapath_is_stale(od)) {
> >              sbrec_igmp_group_delete(sb_igmp);
> > @@ -16021,7 +16117,7 @@ build_mcast_groups(struct lflow_input
*input_data,
> >       * IGMP groups are based on the groups learnt by their multicast
enabled
> >       * peers.
> >       */
> > -    HMAP_FOR_EACH (od, key_node, datapaths) {
> > +    HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> >
> >          if (ovs_list_is_empty(&od->mcast_info.groups)) {
> >              continue;
> > @@ -16192,7 +16288,8 @@ build_static_mac_binding_table(struct
northd_input *input_data,
> >  void
> >  northd_init(struct northd_data *data)
> >  {
> > -    hmap_init(&data->datapaths);
> > +    hmap_init(&data->ls_datapaths);
> > +    hmap_init(&data->lr_datapaths);
> >      hmap_init(&data->ls_ports);
> >      hmap_init(&data->lr_ports);
> >      hmap_init(&data->port_groups);
> > @@ -16247,8 +16344,9 @@ northd_destroy(struct northd_data *data)
> >       */
> >      cleanup_macam();
> >
> > -    destroy_datapaths_and_ports(&data->datapaths, &data->ls_ports,
> > -                                &data->lr_ports, &data->lr_list);
> > +    destroy_datapaths_and_ports(&data->ls_datapaths,
&data->lr_datapaths,
> > +                                &data->ls_ports, &data->lr_ports,
> > +                                &data->lr_list);
> >      destroy_debug_config();
> >  }
> >
> > @@ -16336,20 +16434,22 @@ ovnnb_db_run(struct northd_input *input_data,
> >
> >      init_debug_config(nb);
> >
> > -    build_datapaths(input_data, ovnsb_txn, &data->datapaths,
&data->lr_list);
> > -    build_lbs(input_data, &data->datapaths, &data->lbs,
&data->lb_groups);
> > +    build_datapaths(input_data, ovnsb_txn, &data->ls_datapaths,
> > +                    &data->lr_datapaths, &data->lr_list);
> > +    build_lbs(input_data, &data->ls_datapaths, &data->lr_datapaths,
&data->lbs,
> > +              &data->lb_groups);
> >      build_ports(input_data, ovnsb_txn, sbrec_chassis_by_name,
> > -                sbrec_chassis_by_hostname, &data->datapaths,
> > -                &data->ls_ports, &data->lr_ports);
> > -    build_lb_port_related_data(&data->datapaths, &data->ls_ports,
> > +                sbrec_chassis_by_hostname, &data->ls_datapaths,
> > +                &data->lr_datapaths, &data->ls_ports, &data->lr_ports);
> > +    build_lb_port_related_data(&data->lr_datapaths, &data->ls_ports,
> >                                 &data->lbs, &data->lb_groups,
input_data,
> >                                 ovnsb_txn);
> >      build_lb_count_dps(&data->lbs);
> > -    build_ipam(&data->datapaths, &data->ls_ports);
> > +    build_ipam(&data->ls_datapaths, &data->ls_ports);
> >      build_port_group_lswitches(input_data, &data->port_groups,
> >                                 &data->ls_ports);
> >      build_lrouter_groups(&data->lr_ports, &data->lr_list);
> > -    build_ip_mcast(input_data, ovnsb_txn, &data->datapaths);
> > +    build_ip_mcast(input_data, ovnsb_txn, &data->ls_datapaths);
> >      build_meter_groups(input_data, &data->meter_groups);
> >      build_static_mac_binding_table(input_data, ovnsb_txn,
&data->lr_ports);
> >      stopwatch_stop(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
> > @@ -16357,14 +16457,14 @@ ovnnb_db_run(struct northd_input *input_data,
> >      ovn_update_ipv6_options(&data->lr_ports);
> >      ovn_update_ipv6_prefix(&data->lr_ports);
> >
> > -    sync_lbs(input_data, ovnsb_txn, &data->datapaths, &data->lbs);
> > +    sync_lbs(input_data, ovnsb_txn, &data->ls_datapaths, &data->lbs);
> >      sync_port_groups(input_data, ovnsb_txn, &data->port_groups);
> >      sync_meters(input_data, ovnsb_txn, &data->meter_groups);
> >      sync_mirrors(input_data, ovnsb_txn);
> > -    sync_dns_entries(input_data, ovnsb_txn, &data->datapaths);
> > +    sync_dns_entries(input_data, ovnsb_txn, &data->ls_datapaths);
> >      sync_template_vars(input_data, ovnsb_txn);
> >
> > -    cleanup_stale_fdb_entries(input_data, &data->datapaths);
> > +    cleanup_stale_fdb_entries(input_data, &data->ls_datapaths);
> >      stopwatch_stop(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
> >
> >      /* Set up SB_Global (depends on chassis features). */
> > diff --git a/northd/northd.h b/northd/northd.h
> > index cdb521d46566..cfd36e32c9a7 100644
> > --- a/northd/northd.h
> > +++ b/northd/northd.h
> > @@ -76,7 +76,8 @@ struct chassis_features {
> >
> >  struct northd_data {
> >      /* Global state for 'en-northd'. */
> > -    struct hmap datapaths;
> > +    struct hmap ls_datapaths;
> > +    struct hmap lr_datapaths;
> >      struct hmap ls_ports;
> >      struct hmap lr_ports;
> >      struct hmap port_groups;
> > @@ -102,7 +103,8 @@ struct lflow_input {
> >      /* Indexes */
> >      struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp;
> >
> > -    const struct hmap *datapaths;
> > +    const struct hmap *ls_datapaths;
> > +    const struct hmap *lr_datapaths;
> >      const struct hmap *ls_ports;
> >      const struct hmap *lr_ports;
> >      const struct hmap *port_groups;
>
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to