diff mbox series

[ovs-dev,3/5] northd: Move port group processing to its separate module.

Message ID 169167149634.2447623.7362496199433519342.stgit@dceara.remote.csb
State Accepted
Headers show
Series Add port group incremental processing in northd. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed

Commit Message

Dumitru Ceara Aug. 10, 2023, 12:44 p.m. UTC
No functional differences in this commit, just abstract out the
processing a bit.

Signed-off-by: Dumitru Ceara <dceara@redhat.com>
---
 northd/automake.mk     |    2 
 northd/en-port-group.c |  237 ++++++++++++++++++++++++++++++++++++++++++++++
 northd/en-port-group.h |   63 ++++++++++++
 northd/northd.c        |  246 ++++++++----------------------------------------
 northd/northd.h        |   26 +----
 5 files changed, 350 insertions(+), 224 deletions(-)
 create mode 100644 northd/en-port-group.c
 create mode 100644 northd/en-port-group.h

Comments

Ales Musil Aug. 22, 2023, 6:24 a.m. UTC | #1
On Thu, Aug 10, 2023 at 2:45 PM Dumitru Ceara <dceara@redhat.com> wrote:

> No functional differences in this commit, just abstract out the
> processing a bit.
>
> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
> ---
>  northd/automake.mk     |    2
>  northd/en-port-group.c |  237
> ++++++++++++++++++++++++++++++++++++++++++++++
>  northd/en-port-group.h |   63 ++++++++++++
>  northd/northd.c        |  246
> ++++++++----------------------------------------
>  northd/northd.h        |   26 +----
>  5 files changed, 350 insertions(+), 224 deletions(-)
>  create mode 100644 northd/en-port-group.c
>  create mode 100644 northd/en-port-group.h
>
> diff --git a/northd/automake.mk b/northd/automake.mk
> index b17f1fdb54..0fc634b4b4 100644
> --- a/northd/automake.mk
> +++ b/northd/automake.mk
> @@ -14,6 +14,8 @@ northd_ovn_northd_SOURCES = \
>         northd/en-lflow.h \
>         northd/en-northd-output.c \
>         northd/en-northd-output.h \
> +       northd/en-port-group.c \
> +       northd/en-port-group.h \
>         northd/en-sync-sb.c \
>         northd/en-sync-sb.h \
>         northd/en-sync-from-sb.c \
> diff --git a/northd/en-port-group.c b/northd/en-port-group.c
> new file mode 100644
> index 0000000000..b83926c351
> --- /dev/null
> +++ b/northd/en-port-group.c
> @@ -0,0 +1,237 @@
> +/*
> + * Copyright (c) 2023, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include "openvswitch/vlog.h"
> +
> +#include "en-port-group.h"
> +#include "northd.h"
> +
> +VLOG_DEFINE_THIS_MODULE(en_port_group);
> +
> +static struct ls_port_group *ls_port_group_create(
> +    struct ls_port_group_table *,
> +    const struct nbrec_logical_switch *,
> +    const struct sbrec_datapath_binding *);
> +
> +static void ls_port_group_destroy(struct ls_port_group_table *,
> +                                  struct ls_port_group *);
> +
> +static struct ls_port_group_record *ls_port_group_record_add(
> +    struct ls_port_group *,
> +    const struct nbrec_port_group *,
> +    const char *port_name);
> +
> +static void ls_port_group_record_destroy(
> +    struct ls_port_group *,
> +    struct ls_port_group_record *);
> +
> +void
> +ls_port_group_table_init(struct ls_port_group_table *table)
> +{
> +    *table = (struct ls_port_group_table) {
> +        .entries = HMAP_INITIALIZER(&table->entries),
> +    };
> +}
> +
> +void
> +ls_port_group_table_clear(struct ls_port_group_table *table)
> +{
> +    struct ls_port_group *ls_pg;
> +    HMAP_FOR_EACH_SAFE (ls_pg, key_node, &table->entries) {
> +        ls_port_group_destroy(table, ls_pg);
> +    }
> +}
> +
> +void
> +ls_port_group_table_destroy(struct ls_port_group_table *table)
> +{
> +    ls_port_group_table_clear(table);
> +    hmap_destroy(&table->entries);
> +}
> +
> +struct ls_port_group *
> +ls_port_group_table_find(const struct ls_port_group_table *table,
> +                         const struct nbrec_logical_switch *nbs)
> +{
> +    struct ls_port_group *ls_pg;
> +
> +    HMAP_FOR_EACH_WITH_HASH (ls_pg, key_node,
> uuid_hash(&nbs->header_.uuid),
> +                             &table->entries) {
> +        if (nbs == ls_pg->nbs) {
> +            return ls_pg;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +void
> +ls_port_group_table_build(struct ls_port_group_table *ls_port_groups,
> +                          const struct nbrec_port_group_table *pg_table,
> +                          const struct hmap *ls_ports)
> +{
> +    const struct nbrec_port_group *nb_pg;
> +    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg, pg_table) {
> +        for (size_t i = 0; i < nb_pg->n_ports; i++) {
> +            const char *port_name = nb_pg->ports[i]->name;
> +            const struct ovn_datapath *od =
> +                northd_get_datapath_for_port(ls_ports, port_name);
> +
> +            if (!od) {
> +                static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(1, 1);
> +                VLOG_ERR_RL(&rl, "lport %s in port group %s not found.",
> +                            port_name, nb_pg->name);
> +                continue;
> +            }
> +
> +            if (!od->nbs) {
> +                static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(1, 1);
> +                VLOG_WARN_RL(&rl, "lport %s in port group %s has no
> lswitch.",
> +                             nb_pg->ports[i]->name,
> +                             nb_pg->name);
> +                continue;
> +            }
> +
> +            struct ls_port_group *ls_pg =
> +                ls_port_group_table_find(ls_port_groups, od->nbs);
> +            if (!ls_pg) {
> +                ls_pg = ls_port_group_create(ls_port_groups, od->nbs,
> od->sb);
> +            }
> +            ls_port_group_record_add(ls_pg, nb_pg, port_name);
> +        }
> +    }
> +}
> +
> +/* Each port group in Port_Group table in OVN_Northbound has a
> corresponding
> + * entry in Port_Group table in OVN_Southbound. In OVN_Northbound the
> entries
> + * contains lport uuids, while in OVN_Southbound we store the lport names.
> + */
> +void
> +ls_port_group_table_sync(
> +    const struct ls_port_group_table *ls_port_groups,
> +    const struct sbrec_port_group_table *sbrec_port_group_table,
> +    struct ovsdb_idl_txn *ovnsb_txn)
> +{
> +    struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
> +
> +    const struct sbrec_port_group *sb_port_group;
> +    SBREC_PORT_GROUP_TABLE_FOR_EACH (sb_port_group,
> sbrec_port_group_table) {
> +        shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
> +    }
> +
> +    struct ds sb_name = DS_EMPTY_INITIALIZER;
> +
> +    struct ls_port_group *ls_pg;
> +    HMAP_FOR_EACH (ls_pg, key_node, &ls_port_groups->entries) {
> +        struct ls_port_group_record *ls_pg_rec;
> +
> +        HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> +            get_sb_port_group_name(ls_pg_rec->nb_pg->name,
> +                                   ls_pg->sb_datapath_key,
> +                                   &sb_name);
> +            sb_port_group = shash_find_and_delete(&sb_port_groups,
> +                                                  ds_cstr(&sb_name));
> +            if (!sb_port_group) {
> +                sb_port_group = sbrec_port_group_insert(ovnsb_txn);
> +                sbrec_port_group_set_name(sb_port_group,
> ds_cstr(&sb_name));
> +            }
> +
> +            const char **nb_port_names = sset_array(&ls_pg_rec->ports);
> +            sbrec_port_group_set_ports(sb_port_group,
> +                                       nb_port_names,
> +                                       sset_count(&ls_pg_rec->ports));
> +            free(nb_port_names);
> +        }
> +    }
> +    ds_destroy(&sb_name);
> +
> +    struct shash_node *node;
> +    SHASH_FOR_EACH_SAFE (node, &sb_port_groups) {
> +        sbrec_port_group_delete(node->data);
> +        shash_delete(&sb_port_groups, node);
> +    }
> +    shash_destroy(&sb_port_groups);
> +}
> +
> +static struct ls_port_group *
> +ls_port_group_create(struct ls_port_group_table *ls_port_groups,
> +                     const struct nbrec_logical_switch *nbs,
> +                     const struct sbrec_datapath_binding *dp)
> +{
> +    struct ls_port_group *ls_pg = xmalloc(sizeof *ls_pg);
> +
> +    *ls_pg = (struct ls_port_group) {
> +        .nbs = nbs,
> +        .sb_datapath_key = dp->tunnel_key,
> +        .nb_pgs = HMAP_INITIALIZER(&ls_pg->nb_pgs),
> +    };
> +    hmap_insert(&ls_port_groups->entries, &ls_pg->key_node,
> +                uuid_hash(&nbs->header_.uuid));
> +    return ls_pg;
> +}
> +
> +static void
> +ls_port_group_destroy(struct ls_port_group_table *ls_port_groups,
> +                      struct ls_port_group *ls_pg)
> +{
> +    if (ls_pg) {
> +        struct ls_port_group_record *ls_pg_rec;
> +        HMAP_FOR_EACH_SAFE (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> +            ls_port_group_record_destroy(ls_pg, ls_pg_rec);
> +        }
> +        hmap_destroy(&ls_pg->nb_pgs);
> +        hmap_remove(&ls_port_groups->entries, &ls_pg->key_node);
> +        free(ls_pg);
> +    }
> +}
> +
> +static struct ls_port_group_record *
> +ls_port_group_record_add(struct ls_port_group *ls_pg,
> +                         const struct nbrec_port_group *nb_pg,
> +                         const char *port_name)
> +{
> +    struct ls_port_group_record *ls_pg_rec = NULL;
> +    size_t hash = uuid_hash(&nb_pg->header_.uuid);
> +
> +    HMAP_FOR_EACH_WITH_HASH (ls_pg_rec, key_node, hash, &ls_pg->nb_pgs) {
> +        if (ls_pg_rec->nb_pg == nb_pg) {
> +            goto done;
> +        }
> +    }
> +
> +    ls_pg_rec = xzalloc(sizeof *ls_pg_rec);
> +    *ls_pg_rec = (struct ls_port_group_record) {
> +        .nb_pg = nb_pg,
> +        .ports = SSET_INITIALIZER(&ls_pg_rec->ports),
> +    };
> +    hmap_insert(&ls_pg->nb_pgs, &ls_pg_rec->key_node, hash);
> +done:
> +    sset_add(&ls_pg_rec->ports, port_name);
> +    return ls_pg_rec;
> +}
> +
> +static void
> +ls_port_group_record_destroy(struct ls_port_group *ls_pg,
> +                             struct ls_port_group_record *ls_pg_rec)
> +{
> +    if (ls_pg_rec) {
> +        hmap_remove(&ls_pg->nb_pgs, &ls_pg_rec->key_node);
> +        sset_destroy(&ls_pg_rec->ports);
> +        free(ls_pg_rec);
> +    }
> +}
> +
> diff --git a/northd/en-port-group.h b/northd/en-port-group.h
> new file mode 100644
> index 0000000000..2c8e01f51f
> --- /dev/null
> +++ b/northd/en-port-group.h
> @@ -0,0 +1,63 @@
> +/*
> + * Copyright (c) 2023, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +#ifndef EN_PORT_GROUP_H
> +#define EN_PORT_GROUP_H 1
> +
> +#include <stdint.h>
> +
> +#include "lib/inc-proc-eng.h"
> +#include "lib/ovn-nb-idl.h"
> +#include "lib/ovn-sb-idl.h"
> +#include "openvswitch/hmap.h"
> +
> +#include "sset.h"
> +
> +/* Per logical switch port group information. */
> +struct ls_port_group_table {
> +    struct hmap entries; /* Stores struct ls_port_group. */
> +};
> +
> +struct ls_port_group {
> +    struct hmap_node key_node;  /* Index on 'nbs->header_.uuid'. */
> +
> +    const struct nbrec_logical_switch *nbs;
> +    int64_t sb_datapath_key; /* SB.Datapath_Binding.tunnel_key. */
> +
> +    /* Port groups with ports attached to 'nbs'. */
> +    struct hmap nb_pgs; /* Stores struct ls_port_group_record. */
> +};
> +
> +struct ls_port_group_record {
> +    struct hmap_node key_node;  /* Index on 'nb_pg->header_.uuid'. */
> +
> +    const struct nbrec_port_group *nb_pg;
> +    struct sset ports;          /* Subset of 'nb_pg' ports in this
> record. */
> +};
> +
> +void ls_port_group_table_init(struct ls_port_group_table *);
> +void ls_port_group_table_clear(struct ls_port_group_table *);
> +void ls_port_group_table_destroy(struct ls_port_group_table *);
> +struct ls_port_group *ls_port_group_table_find(
> +    const struct ls_port_group_table *,
> +    const struct nbrec_logical_switch *);
> +
> +void ls_port_group_table_build(struct ls_port_group_table *ls_port_groups,
> +                               const struct nbrec_port_group_table *,
> +                               const struct hmap *ls_ports);
> +void ls_port_group_table_sync(const struct ls_port_group_table
> *ls_port_groups,
> +                              const struct sbrec_port_group_table *,
> +                              struct ovsdb_idl_txn *ovnsb_txn);
> +#endif /* EN_PORT_GROUP_H */
> diff --git a/northd/northd.c b/northd/northd.c
> index 73fab3af7e..04da75fa96 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -21,6 +21,7 @@
>  #include "bitmap.h"
>  #include "coverage.h"
>  #include "dirs.h"
> +#include "en-port-group.h"
>  #include "ipam.h"
>  #include "openvswitch/dynamic-string.h"
>  #include "hash.h"
> @@ -6237,89 +6238,6 @@ build_dhcpv6_action(struct ovn_port *op, struct
> in6_addr *offer_ip,
>      return true;
>  }
>
> -static struct ls_port_group_record *
> -ls_port_group_record_add(struct hmap *nb_pgs,
> -                         const struct nbrec_port_group *nb_pg,
> -                         const char *port_name)
> -{
> -    struct ls_port_group_record *ls_pg_rec = NULL;
> -    size_t hash = uuid_hash(&nb_pg->header_.uuid);
> -
> -    HMAP_FOR_EACH_WITH_HASH (ls_pg_rec, key_node, hash, nb_pgs) {
> -        if (ls_pg_rec->nb_pg == nb_pg) {
> -            goto done;
> -        }
> -    }
> -
> -    ls_pg_rec = xzalloc(sizeof *ls_pg_rec);
> -    *ls_pg_rec = (struct ls_port_group_record) {
> -        .nb_pg = nb_pg,
> -        .ports = SSET_INITIALIZER(&ls_pg_rec->ports),
> -    };
> -    hmap_insert(nb_pgs, &ls_pg_rec->key_node, hash);
> -done:
> -    sset_add(&ls_pg_rec->ports, port_name);
> -    return ls_pg_rec;
> -}
> -
> -static void
> -ls_port_group_record_destroy(struct hmap *nb_pgs,
> -                             struct ls_port_group_record *ls_pg_rec)
> -{
> -    if (ls_pg_rec) {
> -        hmap_remove(nb_pgs, &ls_pg_rec->key_node);
> -        sset_destroy(&ls_pg_rec->ports);
> -        free(ls_pg_rec);
> -    }
> -}
> -
> -
> -static struct ls_port_group *
> -ls_port_group_create(struct hmap *ls_port_groups,
> -                     const struct nbrec_logical_switch *nbs,
> -                     const struct sbrec_datapath_binding *dp)
> -{
> -    struct ls_port_group *ls_pg = xmalloc(sizeof *ls_pg);
> -
> -    *ls_pg = (struct ls_port_group) {
> -        .nbs = nbs,
> -        .sb_datapath_key = dp->tunnel_key,
> -        .nb_pgs = HMAP_INITIALIZER(&ls_pg->nb_pgs),
> -    };
> -    hmap_insert(ls_port_groups, &ls_pg->key_node,
> -                uuid_hash(&nbs->header_.uuid));
> -    return ls_pg;
> -}
> -
> -static void
> -ls_port_group_destroy(struct hmap *ls_port_groups, struct ls_port_group
> *ls_pg)
> -{
> -    if (ls_pg) {
> -        struct ls_port_group_record *ls_pg_rec;
> -        HMAP_FOR_EACH_SAFE (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> -            ls_port_group_record_destroy(&ls_pg->nb_pgs, ls_pg_rec);
> -        }
> -        hmap_destroy(&ls_pg->nb_pgs);
> -        hmap_remove(ls_port_groups, &ls_pg->key_node);
> -        free(ls_pg);
> -    }
> -}
> -
> -static struct ls_port_group *
> -ls_port_group_find(const struct hmap *ls_port_groups,
> -                   const struct nbrec_logical_switch *nbs)
> -{
> -    struct ls_port_group *ls_pg;
> -
> -    HMAP_FOR_EACH_WITH_HASH (ls_pg, key_node,
> uuid_hash(&nbs->header_.uuid),
> -                             ls_port_groups) {
> -        if (nbs == ls_pg->nbs) {
> -            return ls_pg;
> -        }
> -    }
> -    return NULL;
> -}
> -
>  static bool
>  od_set_acl_flags(struct ovn_datapath *od, struct nbrec_acl **acls,
>                   size_t n_acls)
> @@ -6351,7 +6269,8 @@ od_set_acl_flags(struct ovn_datapath *od, struct
> nbrec_acl **acls,
>  }
>
>  static void
> -ls_get_acl_flags(struct ovn_datapath *od, const struct hmap
> *ls_port_groups)
> +ls_get_acl_flags(struct ovn_datapath *od,
> +                 const struct ls_port_group_table *ls_port_groups)
>  {
>      od->has_acls = false;
>      od->has_stateful_acl = false;
> @@ -6361,15 +6280,13 @@ ls_get_acl_flags(struct ovn_datapath *od, const
> struct hmap *ls_port_groups)
>          return;
>      }
>
> -    const struct ls_port_group *ls_pg;
> -
> -    ls_pg = ls_port_group_find(ls_port_groups, od->nbs);
> +    const struct ls_port_group *ls_pg =
> +        ls_port_group_table_find(ls_port_groups, od->nbs);
>      if (!ls_pg) {
>          return;
>      }
>
> -
> -    struct ls_port_group_record *ls_pg_rec;
> +    const struct ls_port_group_record *ls_pg_rec;
>      HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
>          if (od_set_acl_flags(od, ls_pg_rec->nb_pg->acls,
>                               ls_pg_rec->nb_pg->n_acls)) {
> @@ -6578,7 +6495,7 @@ build_stateless_filter(struct ovn_datapath *od,
>
>  static void
>  build_stateless_filters(struct ovn_datapath *od,
> -                        const struct hmap *ls_port_groups,
> +                        const struct ls_port_group_table *ls_port_groups,
>                          struct hmap *lflows)
>  {
>      for (size_t i = 0; i < od->nbs->n_acls; i++) {
> @@ -6588,8 +6505,8 @@ build_stateless_filters(struct ovn_datapath *od,
>          }
>      }
>
> -    const struct ls_port_group *ls_pg = ls_port_group_find(ls_port_groups,
> -                                                           od->nbs);
> +    const struct ls_port_group *ls_pg =
> +        ls_port_group_table_find(ls_port_groups, od->nbs);
>      if (!ls_pg) {
>          return;
>      }
> @@ -6607,7 +6524,8 @@ build_stateless_filters(struct ovn_datapath *od,
>  }
>
>  static void
> -build_pre_acls(struct ovn_datapath *od, const struct hmap *ls_port_groups,
> +build_pre_acls(struct ovn_datapath *od,
> +               const struct ls_port_group_table *ls_port_groups,
>                 struct hmap *lflows)
>  {
>      /* Ingress and Egress Pre-ACL Table (Priority 0): Packets are
> @@ -7301,44 +7219,6 @@ ovn_update_ipv6_options(struct hmap *lr_ports)
>      }
>  }
>
> -static void
> -build_port_group_lswitches(
> -    const struct nbrec_port_group_table *nbrec_port_group_table,
> -    struct hmap *ls_pgs, struct hmap *ls_ports)
> -{
> -    hmap_init(ls_pgs);
> -
> -    const struct nbrec_port_group *nb_pg;
> -    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg, nbrec_port_group_table) {
> -        for (size_t i = 0; i < nb_pg->n_ports; i++) {
> -            struct ovn_port *op = ovn_port_find(ls_ports,
> -                                                nb_pg->ports[i]->name);
> -            if (!op) {
> -                static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(1, 1);
> -                VLOG_ERR_RL(&rl, "lport %s in port group %s not found.",
> -                            nb_pg->ports[i]->name,
> -                            nb_pg->name);
> -                continue;
> -            }
> -
> -            if (!op->od->nbs) {
> -                static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(1, 1);
> -                VLOG_WARN_RL(&rl, "lport %s in port group %s has no
> lswitch.",
> -                             nb_pg->ports[i]->name,
> -                             nb_pg->name);
> -                continue;
> -            }
> -
> -            struct ls_port_group *ls_pg =
> -                ls_port_group_find(ls_pgs, op->od->nbs);
> -            if (!ls_pg) {
> -                ls_pg = ls_port_group_create(ls_pgs, op->od->nbs,
> op->od->sb);
> -            }
> -            ls_port_group_record_add(&ls_pg->nb_pgs, nb_pg, op->key);
> -        }
> -    }
> -}
> -
>  #define IPV6_CT_OMIT_MATCH "nd || nd_ra || nd_rs || mldv1 || mldv2"
>
>  static void
> @@ -7491,7 +7371,8 @@ build_acl_log_related_flows(struct ovn_datapath *od,
> struct hmap *lflows,
>
>  static void
>  build_acls(struct ovn_datapath *od, const struct chassis_features
> *features,
> -           struct hmap *lflows, const struct hmap *ls_port_groups,
> +           struct hmap *lflows,
> +           const struct ls_port_group_table *ls_port_groups,
>             const struct shash *meter_groups)
>  {
>      const char *default_acl_action = default_acl_drop
> @@ -7678,8 +7559,8 @@ build_acls(struct ovn_datapath *od, const struct
> chassis_features *features,
>                       meter_groups, &match, &actions);
>      }
>
> -    const struct ls_port_group *ls_pg = ls_port_group_find(ls_port_groups,
> -                                                           od->nbs);
> +    const struct ls_port_group *ls_pg =
> +        ls_port_group_table_find(ls_port_groups, od->nbs);
>      if (ls_pg) {
>          const struct ls_port_group_record *ls_pg_rec;
>          HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> @@ -9188,11 +9069,12 @@ build_lswitch_lflows_l2_unknown(struct
> ovn_datapath *od,
>  /* Build pre-ACL and ACL tables for both ingress and egress.
>   * Ingress tables 3 through 10.  Egress tables 0 through 7. */
>  static void
> -build_lswitch_lflows_pre_acl_and_acl(struct ovn_datapath *od,
> -                                     const struct hmap *ls_port_groups,
> -                                     const struct chassis_features
> *features,
> -                                     struct hmap *lflows,
> -                                     const struct shash *meter_groups)
> +build_lswitch_lflows_pre_acl_and_acl(
> +    struct ovn_datapath *od,
> +    const struct ls_port_group_table *ls_port_groups,
> +    const struct chassis_features *features,
> +    struct hmap *lflows,
> +    const struct shash *meter_groups)
>  {
>      ovs_assert(od->nbs);
>      ls_get_acl_flags(od, ls_port_groups);
> @@ -15502,7 +15384,7 @@ struct lswitch_flow_build_info {
>      const struct ovn_datapaths *lr_datapaths;
>      const struct hmap *ls_ports;
>      const struct hmap *lr_ports;
> -    const struct hmap *ls_port_groups;
> +    const struct ls_port_group_table *ls_port_groups;
>      struct hmap *lflows;
>      struct hmap *igmp_groups;
>      const struct shash *meter_groups;
> @@ -15800,7 +15682,7 @@ build_lswitch_and_lrouter_flows(const struct
> ovn_datapaths *ls_datapaths,
>                                  const struct ovn_datapaths *lr_datapaths,
>                                  const struct hmap *ls_ports,
>                                  const struct hmap *lr_ports,
> -                                const struct hmap *ls_port_groups,
> +                                const struct ls_port_group_table *ls_pgs,
>                                  struct hmap *lflows,
>                                  struct hmap *igmp_groups,
>                                  const struct shash *meter_groups,
> @@ -15828,7 +15710,7 @@ build_lswitch_and_lrouter_flows(const struct
> ovn_datapaths *ls_datapaths,
>              lsiv[index].lr_datapaths = lr_datapaths;
>              lsiv[index].ls_ports = ls_ports;
>              lsiv[index].lr_ports = lr_ports;
> -            lsiv[index].ls_port_groups = ls_port_groups;
> +            lsiv[index].ls_port_groups = ls_pgs;
>              lsiv[index].igmp_groups = igmp_groups;
>              lsiv[index].meter_groups = meter_groups;
>              lsiv[index].lbs = lbs;
> @@ -15861,7 +15743,7 @@ build_lswitch_and_lrouter_flows(const struct
> ovn_datapaths *ls_datapaths,
>              .lr_datapaths = lr_datapaths,
>              .ls_ports = ls_ports,
>              .lr_ports = lr_ports,
> -            .ls_port_groups = ls_port_groups,
> +            .ls_port_groups = ls_pgs,
>              .lflows = lflows,
>              .igmp_groups = igmp_groups,
>              .meter_groups = meter_groups,
> @@ -16546,56 +16428,6 @@ bool lflow_handle_northd_ls_changes(struct
> ovsdb_idl_txn *ovnsb_txn,
>
>  }
>
> -/* Each port group in Port_Group table in OVN_Northbound has a
> corresponding
> - * entry in Port_Group table in OVN_Southbound. In OVN_Northbound the
> entries
> - * contains lport uuids, while in OVN_Southbound we store the lport names.
> - */
> -static void
> -sync_port_groups(struct ovsdb_idl_txn *ovnsb_txn,
> -                 const struct sbrec_port_group_table
> *sbrec_port_group_table,
> -                 struct hmap *ls_pgs)
> -{
> -    struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
> -
> -    const struct sbrec_port_group *sb_port_group;
> -    SBREC_PORT_GROUP_TABLE_FOR_EACH (sb_port_group,
> sbrec_port_group_table) {
> -        shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
> -    }
> -
> -    struct ds sb_name = DS_EMPTY_INITIALIZER;
> -
> -    struct ls_port_group *ls_pg;
> -    HMAP_FOR_EACH (ls_pg, key_node, ls_pgs) {
> -        struct ls_port_group_record *ls_pg_rec;
> -
> -        HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> -            get_sb_port_group_name(ls_pg_rec->nb_pg->name,
> -                                   ls_pg->sb_datapath_key,
> -                                   &sb_name);
> -            sb_port_group = shash_find_and_delete(&sb_port_groups,
> -                                                  ds_cstr(&sb_name));
> -            if (!sb_port_group) {
> -                sb_port_group = sbrec_port_group_insert(ovnsb_txn);
> -                sbrec_port_group_set_name(sb_port_group,
> ds_cstr(&sb_name));
> -            }
> -
> -            const char **nb_port_names = sset_array(&ls_pg_rec->ports);
> -            sbrec_port_group_set_ports(sb_port_group,
> -                                       nb_port_names,
> -                                       sset_count(&ls_pg_rec->ports));
> -            free(nb_port_names);
> -        }
> -    }
> -    ds_destroy(&sb_name);
> -
> -    struct shash_node *node;
> -    SHASH_FOR_EACH_SAFE (node, &sb_port_groups) {
> -        sbrec_port_group_delete(node->data);
> -        shash_delete(&sb_port_groups, node);
> -    }
> -    shash_destroy(&sb_port_groups);
> -}
> -
>  struct band_entry {
>      int64_t rate;
>      int64_t burst_size;
> @@ -17406,7 +17238,7 @@ northd_init(struct northd_data *data)
>      ovn_datapaths_init(&data->lr_datapaths);
>      hmap_init(&data->ls_ports);
>      hmap_init(&data->lr_ports);
> -    hmap_init(&data->ls_port_groups);
> +    ls_port_group_table_init(&data->ls_port_groups);
>      shash_init(&data->meter_groups);
>      hmap_init(&data->lbs);
>      hmap_init(&data->lb_groups);
> @@ -17438,11 +17270,7 @@ northd_destroy(struct northd_data *data)
>      }
>      hmap_destroy(&data->lb_groups);
>
> -    struct ls_port_group *ls_pg;
> -    HMAP_FOR_EACH_SAFE (ls_pg, key_node, &data->ls_port_groups) {
> -        ls_port_group_destroy(&data->ls_port_groups, ls_pg);
> -    }
> -    hmap_destroy(&data->ls_port_groups);
> +    ls_port_group_table_destroy(&data->ls_port_groups);
>
>      struct shash_node *node;
>      SHASH_FOR_EACH_SAFE (node, &data->meter_groups) {
> @@ -17582,9 +17410,9 @@ ovnnb_db_run(struct northd_input *input_data,
>                         ods_size(&data->ls_datapaths),
>                         ods_size(&data->lr_datapaths));
>      build_ipam(&data->ls_datapaths.datapaths, &data->ls_ports);
> -    build_port_group_lswitches(input_data->nbrec_port_group_table,
> -                               &data->ls_port_groups,
> -                               &data->ls_ports);
> +    ls_port_group_table_build(&data->ls_port_groups,
> +                              input_data->nbrec_port_group_table,
> +                              &data->ls_ports);
>      build_lrouter_groups(&data->lr_ports, &data->lr_list);
>      build_ip_mcast(ovnsb_txn, input_data->sbrec_ip_multicast_table,
>                     input_data->sbrec_ip_mcast_by_dp,
> @@ -17602,8 +17430,9 @@ ovnnb_db_run(struct northd_input *input_data,
>
>      sync_lbs(ovnsb_txn, input_data->sbrec_load_balancer_table,
>               &data->ls_datapaths, &data->lbs);
> -    sync_port_groups(ovnsb_txn, input_data->sbrec_port_group_table,
> -                     &data->ls_port_groups);
> +    ls_port_group_table_sync(&data->ls_port_groups,
> +                             input_data->sbrec_port_group_table,
> +                             ovnsb_txn);
>      sync_meters(ovnsb_txn, input_data->nbrec_meter_table,
>                  input_data->nbrec_acl_table,
> input_data->sbrec_meter_table,
>                  &data->meter_groups);
> @@ -17898,3 +17727,12 @@ northd_get_svc_monitor_mac(void)
>  {
>      return svc_monitor_mac;
>  }
> +
> +const struct ovn_datapath *
> +northd_get_datapath_for_port(const struct hmap *ls_ports,
> +                             const char *port_name)
> +{
> +    const struct ovn_port *op = ovn_port_find(ls_ports, port_name);
> +
> +    return op ? op->od : NULL;
> +}
> diff --git a/northd/northd.h b/northd/northd.h
> index 38bc7f50f1..da93a7c6a5 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -19,6 +19,7 @@
>  #include "lib/ovn-util.h"
>  #include "lib/ovs-atomic.h"
>  #include "lib/sset.h"
> +#include "northd/en-port-group.h"
>  #include "northd/ipam.h"
>  #include "openvswitch/hmap.h"
>
> @@ -108,7 +109,7 @@ struct northd_data {
>      struct ovn_datapaths lr_datapaths;
>      struct hmap ls_ports;
>      struct hmap lr_ports;
> -    struct hmap ls_port_groups;         /* Stores struct ls_port_group. */
> +    struct ls_port_group_table ls_port_groups;
>      struct shash meter_groups;
>      struct hmap lbs;
>      struct hmap lb_groups;
> @@ -144,7 +145,7 @@ struct lflow_input {
>      const struct ovn_datapaths *lr_datapaths;
>      const struct hmap *ls_ports;
>      const struct hmap *lr_ports;
> -    const struct hmap *ls_port_groups;
> +    const struct ls_port_group_table *ls_port_groups;
>      const struct shash *meter_groups;
>      const struct hmap *lbs;
>      const struct hmap *bfd_connections;
> @@ -314,24 +315,6 @@ struct ovn_datapath {
>      struct hmap ports;
>  };
>
> -/* Per logical switch port group information. */
> -struct ls_port_group {
> -    struct hmap_node key_node;  /* Index on 'nbs->header_.uuid'. */
> -
> -    const struct nbrec_logical_switch *nbs;
> -    int64_t sb_datapath_key; /* SB.Datapath_Binding.tunnel_key. */
> -
> -    /* Port groups with ports attached to 'nbs'. */
> -    struct hmap nb_pgs; /* Stores struct ls_port_group_record. */
> -};
> -
> -struct ls_port_group_record {
> -    struct hmap_node key_node;  /* Index on 'nb_pg->header_.uuid'. */
> -
> -    const struct nbrec_port_group *nb_pg;
> -    struct sset ports;          /* Subset of 'nb_pg' ports in this
> record. */
> -};
> -
>  void ovnnb_db_run(struct northd_input *input_data,
>                    struct northd_data *data,
>                    struct ovsdb_idl_txn *ovnnb_txn,
> @@ -369,4 +352,7 @@ void bfd_cleanup_connections(const struct
> nbrec_bfd_table *,
>  void run_update_worker_pool(int n_threads);
>
>  const char *northd_get_svc_monitor_mac(void);
> +
> +const struct ovn_datapath *northd_get_datapath_for_port(
> +    const struct hmap *ls_ports, const char *port_name);
>  #endif /* NORTHD_H */
>
>
Looks good to me, thanks.

Acked-by: Ales Musil <amusil@redhat.com>
Han Zhou Aug. 23, 2023, 6:10 a.m. UTC | #2
On Thu, Aug 10, 2023 at 5:45 AM Dumitru Ceara <dceara@redhat.com> wrote:
>
> No functional differences in this commit, just abstract out the
> processing a bit.
>
> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
> ---
>  northd/automake.mk     |    2
>  northd/en-port-group.c |  237
++++++++++++++++++++++++++++++++++++++++++++++
>  northd/en-port-group.h |   63 ++++++++++++
>  northd/northd.c        |  246
++++++++----------------------------------------
>  northd/northd.h        |   26 +----
>  5 files changed, 350 insertions(+), 224 deletions(-)
>  create mode 100644 northd/en-port-group.c
>  create mode 100644 northd/en-port-group.h
>
> diff --git a/northd/automake.mk b/northd/automake.mk
> index b17f1fdb54..0fc634b4b4 100644
> --- a/northd/automake.mk
> +++ b/northd/automake.mk
> @@ -14,6 +14,8 @@ northd_ovn_northd_SOURCES = \
>         northd/en-lflow.h \
>         northd/en-northd-output.c \
>         northd/en-northd-output.h \
> +       northd/en-port-group.c \
> +       northd/en-port-group.h \
>         northd/en-sync-sb.c \
>         northd/en-sync-sb.h \
>         northd/en-sync-from-sb.c \
> diff --git a/northd/en-port-group.c b/northd/en-port-group.c
> new file mode 100644
> index 0000000000..b83926c351
> --- /dev/null
> +++ b/northd/en-port-group.c
> @@ -0,0 +1,237 @@
> +/*
> + * Copyright (c) 2023, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include "openvswitch/vlog.h"
> +
> +#include "en-port-group.h"
> +#include "northd.h"
> +
> +VLOG_DEFINE_THIS_MODULE(en_port_group);
> +
> +static struct ls_port_group *ls_port_group_create(
> +    struct ls_port_group_table *,
> +    const struct nbrec_logical_switch *,
> +    const struct sbrec_datapath_binding *);
> +
> +static void ls_port_group_destroy(struct ls_port_group_table *,
> +                                  struct ls_port_group *);
> +
> +static struct ls_port_group_record *ls_port_group_record_add(
> +    struct ls_port_group *,
> +    const struct nbrec_port_group *,
> +    const char *port_name);
> +
> +static void ls_port_group_record_destroy(
> +    struct ls_port_group *,
> +    struct ls_port_group_record *);
> +
> +void
> +ls_port_group_table_init(struct ls_port_group_table *table)
> +{
> +    *table = (struct ls_port_group_table) {
> +        .entries = HMAP_INITIALIZER(&table->entries),
> +    };
> +}
> +
> +void
> +ls_port_group_table_clear(struct ls_port_group_table *table)
> +{
> +    struct ls_port_group *ls_pg;
> +    HMAP_FOR_EACH_SAFE (ls_pg, key_node, &table->entries) {
> +        ls_port_group_destroy(table, ls_pg);
> +    }
> +}
> +
> +void
> +ls_port_group_table_destroy(struct ls_port_group_table *table)
> +{
> +    ls_port_group_table_clear(table);
> +    hmap_destroy(&table->entries);
> +}
> +
> +struct ls_port_group *
> +ls_port_group_table_find(const struct ls_port_group_table *table,
> +                         const struct nbrec_logical_switch *nbs)
> +{
> +    struct ls_port_group *ls_pg;
> +
> +    HMAP_FOR_EACH_WITH_HASH (ls_pg, key_node,
uuid_hash(&nbs->header_.uuid),
> +                             &table->entries) {
> +        if (nbs == ls_pg->nbs) {
> +            return ls_pg;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +void
> +ls_port_group_table_build(struct ls_port_group_table *ls_port_groups,
> +                          const struct nbrec_port_group_table *pg_table,
> +                          const struct hmap *ls_ports)
> +{
> +    const struct nbrec_port_group *nb_pg;
> +    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg, pg_table) {
> +        for (size_t i = 0; i < nb_pg->n_ports; i++) {
> +            const char *port_name = nb_pg->ports[i]->name;
> +            const struct ovn_datapath *od =
> +                northd_get_datapath_for_port(ls_ports, port_name);
> +
> +            if (!od) {
> +                static struct vlog_rate_limit rl =
VLOG_RATE_LIMIT_INIT(1, 1);
> +                VLOG_ERR_RL(&rl, "lport %s in port group %s not found.",
> +                            port_name, nb_pg->name);
> +                continue;
> +            }
> +
> +            if (!od->nbs) {
> +                static struct vlog_rate_limit rl =
VLOG_RATE_LIMIT_INIT(1, 1);
> +                VLOG_WARN_RL(&rl, "lport %s in port group %s has no
lswitch.",
> +                             nb_pg->ports[i]->name,
> +                             nb_pg->name);
> +                continue;
> +            }
> +
> +            struct ls_port_group *ls_pg =
> +                ls_port_group_table_find(ls_port_groups, od->nbs);
> +            if (!ls_pg) {
> +                ls_pg = ls_port_group_create(ls_port_groups, od->nbs,
od->sb);
> +            }
> +            ls_port_group_record_add(ls_pg, nb_pg, port_name);
> +        }
> +    }
> +}
> +
> +/* Each port group in Port_Group table in OVN_Northbound has a
corresponding
> + * entry in Port_Group table in OVN_Southbound. In OVN_Northbound the
entries
> + * contains lport uuids, while in OVN_Southbound we store the lport
names.
> + */
> +void
> +ls_port_group_table_sync(
> +    const struct ls_port_group_table *ls_port_groups,
> +    const struct sbrec_port_group_table *sbrec_port_group_table,
> +    struct ovsdb_idl_txn *ovnsb_txn)
> +{
> +    struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
> +
> +    const struct sbrec_port_group *sb_port_group;
> +    SBREC_PORT_GROUP_TABLE_FOR_EACH (sb_port_group,
sbrec_port_group_table) {
> +        shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
> +    }
> +
> +    struct ds sb_name = DS_EMPTY_INITIALIZER;
> +
> +    struct ls_port_group *ls_pg;
> +    HMAP_FOR_EACH (ls_pg, key_node, &ls_port_groups->entries) {
> +        struct ls_port_group_record *ls_pg_rec;
> +
> +        HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> +            get_sb_port_group_name(ls_pg_rec->nb_pg->name,
> +                                   ls_pg->sb_datapath_key,
> +                                   &sb_name);
> +            sb_port_group = shash_find_and_delete(&sb_port_groups,
> +                                                  ds_cstr(&sb_name));
> +            if (!sb_port_group) {
> +                sb_port_group = sbrec_port_group_insert(ovnsb_txn);
> +                sbrec_port_group_set_name(sb_port_group,
ds_cstr(&sb_name));
> +            }
> +
> +            const char **nb_port_names = sset_array(&ls_pg_rec->ports);
> +            sbrec_port_group_set_ports(sb_port_group,
> +                                       nb_port_names,
> +                                       sset_count(&ls_pg_rec->ports));
> +            free(nb_port_names);
> +        }
> +    }
> +    ds_destroy(&sb_name);
> +
> +    struct shash_node *node;
> +    SHASH_FOR_EACH_SAFE (node, &sb_port_groups) {
> +        sbrec_port_group_delete(node->data);
> +        shash_delete(&sb_port_groups, node);
> +    }
> +    shash_destroy(&sb_port_groups);
> +}
> +
> +static struct ls_port_group *
> +ls_port_group_create(struct ls_port_group_table *ls_port_groups,
> +                     const struct nbrec_logical_switch *nbs,
> +                     const struct sbrec_datapath_binding *dp)
> +{
> +    struct ls_port_group *ls_pg = xmalloc(sizeof *ls_pg);
> +
> +    *ls_pg = (struct ls_port_group) {
> +        .nbs = nbs,
> +        .sb_datapath_key = dp->tunnel_key,
> +        .nb_pgs = HMAP_INITIALIZER(&ls_pg->nb_pgs),
> +    };
> +    hmap_insert(&ls_port_groups->entries, &ls_pg->key_node,
> +                uuid_hash(&nbs->header_.uuid));
> +    return ls_pg;
> +}
> +
> +static void
> +ls_port_group_destroy(struct ls_port_group_table *ls_port_groups,
> +                      struct ls_port_group *ls_pg)
> +{
> +    if (ls_pg) {
> +        struct ls_port_group_record *ls_pg_rec;
> +        HMAP_FOR_EACH_SAFE (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> +            ls_port_group_record_destroy(ls_pg, ls_pg_rec);
> +        }
> +        hmap_destroy(&ls_pg->nb_pgs);
> +        hmap_remove(&ls_port_groups->entries, &ls_pg->key_node);
> +        free(ls_pg);
> +    }
> +}
> +
> +static struct ls_port_group_record *
> +ls_port_group_record_add(struct ls_port_group *ls_pg,
> +                         const struct nbrec_port_group *nb_pg,
> +                         const char *port_name)
> +{
> +    struct ls_port_group_record *ls_pg_rec = NULL;
> +    size_t hash = uuid_hash(&nb_pg->header_.uuid);
> +
> +    HMAP_FOR_EACH_WITH_HASH (ls_pg_rec, key_node, hash, &ls_pg->nb_pgs) {
> +        if (ls_pg_rec->nb_pg == nb_pg) {
> +            goto done;
> +        }
> +    }
> +
> +    ls_pg_rec = xzalloc(sizeof *ls_pg_rec);
> +    *ls_pg_rec = (struct ls_port_group_record) {
> +        .nb_pg = nb_pg,
> +        .ports = SSET_INITIALIZER(&ls_pg_rec->ports),
> +    };
> +    hmap_insert(&ls_pg->nb_pgs, &ls_pg_rec->key_node, hash);
> +done:
> +    sset_add(&ls_pg_rec->ports, port_name);
> +    return ls_pg_rec;
> +}
> +
> +static void
> +ls_port_group_record_destroy(struct ls_port_group *ls_pg,
> +                             struct ls_port_group_record *ls_pg_rec)
> +{
> +    if (ls_pg_rec) {
> +        hmap_remove(&ls_pg->nb_pgs, &ls_pg_rec->key_node);
> +        sset_destroy(&ls_pg_rec->ports);
> +        free(ls_pg_rec);
> +    }
> +}
> +
> diff --git a/northd/en-port-group.h b/northd/en-port-group.h
> new file mode 100644
> index 0000000000..2c8e01f51f
> --- /dev/null
> +++ b/northd/en-port-group.h
> @@ -0,0 +1,63 @@
> +/*
> + * Copyright (c) 2023, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +#ifndef EN_PORT_GROUP_H
> +#define EN_PORT_GROUP_H 1
> +
> +#include <stdint.h>
> +
> +#include "lib/inc-proc-eng.h"
> +#include "lib/ovn-nb-idl.h"
> +#include "lib/ovn-sb-idl.h"
> +#include "openvswitch/hmap.h"
> +
> +#include "sset.h"
> +
> +/* Per logical switch port group information. */
> +struct ls_port_group_table {
> +    struct hmap entries; /* Stores struct ls_port_group. */
> +};
> +
> +struct ls_port_group {
> +    struct hmap_node key_node;  /* Index on 'nbs->header_.uuid'. */
> +
> +    const struct nbrec_logical_switch *nbs;
> +    int64_t sb_datapath_key; /* SB.Datapath_Binding.tunnel_key. */
> +
> +    /* Port groups with ports attached to 'nbs'. */
> +    struct hmap nb_pgs; /* Stores struct ls_port_group_record. */
> +};
> +
> +struct ls_port_group_record {
> +    struct hmap_node key_node;  /* Index on 'nb_pg->header_.uuid'. */
> +
> +    const struct nbrec_port_group *nb_pg;
> +    struct sset ports;          /* Subset of 'nb_pg' ports in this
record. */
> +};
> +
> +void ls_port_group_table_init(struct ls_port_group_table *);
> +void ls_port_group_table_clear(struct ls_port_group_table *);
> +void ls_port_group_table_destroy(struct ls_port_group_table *);
> +struct ls_port_group *ls_port_group_table_find(
> +    const struct ls_port_group_table *,
> +    const struct nbrec_logical_switch *);
> +
> +void ls_port_group_table_build(struct ls_port_group_table
*ls_port_groups,
> +                               const struct nbrec_port_group_table *,
> +                               const struct hmap *ls_ports);
> +void ls_port_group_table_sync(const struct ls_port_group_table
*ls_port_groups,
> +                              const struct sbrec_port_group_table *,
> +                              struct ovsdb_idl_txn *ovnsb_txn);
> +#endif /* EN_PORT_GROUP_H */
> diff --git a/northd/northd.c b/northd/northd.c
> index 73fab3af7e..04da75fa96 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -21,6 +21,7 @@
>  #include "bitmap.h"
>  #include "coverage.h"
>  #include "dirs.h"
> +#include "en-port-group.h"
>  #include "ipam.h"
>  #include "openvswitch/dynamic-string.h"
>  #include "hash.h"
> @@ -6237,89 +6238,6 @@ build_dhcpv6_action(struct ovn_port *op, struct
in6_addr *offer_ip,
>      return true;
>  }
>
> -static struct ls_port_group_record *
> -ls_port_group_record_add(struct hmap *nb_pgs,
> -                         const struct nbrec_port_group *nb_pg,
> -                         const char *port_name)
> -{
> -    struct ls_port_group_record *ls_pg_rec = NULL;
> -    size_t hash = uuid_hash(&nb_pg->header_.uuid);
> -
> -    HMAP_FOR_EACH_WITH_HASH (ls_pg_rec, key_node, hash, nb_pgs) {
> -        if (ls_pg_rec->nb_pg == nb_pg) {
> -            goto done;
> -        }
> -    }
> -
> -    ls_pg_rec = xzalloc(sizeof *ls_pg_rec);
> -    *ls_pg_rec = (struct ls_port_group_record) {
> -        .nb_pg = nb_pg,
> -        .ports = SSET_INITIALIZER(&ls_pg_rec->ports),
> -    };
> -    hmap_insert(nb_pgs, &ls_pg_rec->key_node, hash);
> -done:
> -    sset_add(&ls_pg_rec->ports, port_name);
> -    return ls_pg_rec;
> -}
> -
> -static void
> -ls_port_group_record_destroy(struct hmap *nb_pgs,
> -                             struct ls_port_group_record *ls_pg_rec)
> -{
> -    if (ls_pg_rec) {
> -        hmap_remove(nb_pgs, &ls_pg_rec->key_node);
> -        sset_destroy(&ls_pg_rec->ports);
> -        free(ls_pg_rec);
> -    }
> -}
> -
> -
> -static struct ls_port_group *
> -ls_port_group_create(struct hmap *ls_port_groups,
> -                     const struct nbrec_logical_switch *nbs,
> -                     const struct sbrec_datapath_binding *dp)
> -{
> -    struct ls_port_group *ls_pg = xmalloc(sizeof *ls_pg);
> -
> -    *ls_pg = (struct ls_port_group) {
> -        .nbs = nbs,
> -        .sb_datapath_key = dp->tunnel_key,
> -        .nb_pgs = HMAP_INITIALIZER(&ls_pg->nb_pgs),
> -    };
> -    hmap_insert(ls_port_groups, &ls_pg->key_node,
> -                uuid_hash(&nbs->header_.uuid));
> -    return ls_pg;
> -}
> -
> -static void
> -ls_port_group_destroy(struct hmap *ls_port_groups, struct ls_port_group
*ls_pg)
> -{
> -    if (ls_pg) {
> -        struct ls_port_group_record *ls_pg_rec;
> -        HMAP_FOR_EACH_SAFE (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> -            ls_port_group_record_destroy(&ls_pg->nb_pgs, ls_pg_rec);
> -        }
> -        hmap_destroy(&ls_pg->nb_pgs);
> -        hmap_remove(ls_port_groups, &ls_pg->key_node);
> -        free(ls_pg);
> -    }
> -}
> -
> -static struct ls_port_group *
> -ls_port_group_find(const struct hmap *ls_port_groups,
> -                   const struct nbrec_logical_switch *nbs)
> -{
> -    struct ls_port_group *ls_pg;
> -
> -    HMAP_FOR_EACH_WITH_HASH (ls_pg, key_node,
uuid_hash(&nbs->header_.uuid),
> -                             ls_port_groups) {
> -        if (nbs == ls_pg->nbs) {
> -            return ls_pg;
> -        }
> -    }
> -    return NULL;
> -}
> -
>  static bool
>  od_set_acl_flags(struct ovn_datapath *od, struct nbrec_acl **acls,
>                   size_t n_acls)
> @@ -6351,7 +6269,8 @@ od_set_acl_flags(struct ovn_datapath *od, struct
nbrec_acl **acls,
>  }
>
>  static void
> -ls_get_acl_flags(struct ovn_datapath *od, const struct hmap
*ls_port_groups)
> +ls_get_acl_flags(struct ovn_datapath *od,
> +                 const struct ls_port_group_table *ls_port_groups)
>  {
>      od->has_acls = false;
>      od->has_stateful_acl = false;
> @@ -6361,15 +6280,13 @@ ls_get_acl_flags(struct ovn_datapath *od, const
struct hmap *ls_port_groups)
>          return;
>      }
>
> -    const struct ls_port_group *ls_pg;
> -
> -    ls_pg = ls_port_group_find(ls_port_groups, od->nbs);
> +    const struct ls_port_group *ls_pg =
> +        ls_port_group_table_find(ls_port_groups, od->nbs);
>      if (!ls_pg) {
>          return;
>      }
>
> -
> -    struct ls_port_group_record *ls_pg_rec;
> +    const struct ls_port_group_record *ls_pg_rec;
>      HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
>          if (od_set_acl_flags(od, ls_pg_rec->nb_pg->acls,
>                               ls_pg_rec->nb_pg->n_acls)) {
> @@ -6578,7 +6495,7 @@ build_stateless_filter(struct ovn_datapath *od,
>
>  static void
>  build_stateless_filters(struct ovn_datapath *od,
> -                        const struct hmap *ls_port_groups,
> +                        const struct ls_port_group_table *ls_port_groups,
>                          struct hmap *lflows)
>  {
>      for (size_t i = 0; i < od->nbs->n_acls; i++) {
> @@ -6588,8 +6505,8 @@ build_stateless_filters(struct ovn_datapath *od,
>          }
>      }
>
> -    const struct ls_port_group *ls_pg =
ls_port_group_find(ls_port_groups,
> -                                                           od->nbs);
> +    const struct ls_port_group *ls_pg =
> +        ls_port_group_table_find(ls_port_groups, od->nbs);
>      if (!ls_pg) {
>          return;
>      }
> @@ -6607,7 +6524,8 @@ build_stateless_filters(struct ovn_datapath *od,
>  }
>
>  static void
> -build_pre_acls(struct ovn_datapath *od, const struct hmap
*ls_port_groups,
> +build_pre_acls(struct ovn_datapath *od,
> +               const struct ls_port_group_table *ls_port_groups,
>                 struct hmap *lflows)
>  {
>      /* Ingress and Egress Pre-ACL Table (Priority 0): Packets are
> @@ -7301,44 +7219,6 @@ ovn_update_ipv6_options(struct hmap *lr_ports)
>      }
>  }
>
> -static void
> -build_port_group_lswitches(
> -    const struct nbrec_port_group_table *nbrec_port_group_table,
> -    struct hmap *ls_pgs, struct hmap *ls_ports)
> -{
> -    hmap_init(ls_pgs);
> -
> -    const struct nbrec_port_group *nb_pg;
> -    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg, nbrec_port_group_table) {
> -        for (size_t i = 0; i < nb_pg->n_ports; i++) {
> -            struct ovn_port *op = ovn_port_find(ls_ports,
> -                                                nb_pg->ports[i]->name);
> -            if (!op) {
> -                static struct vlog_rate_limit rl =
VLOG_RATE_LIMIT_INIT(1, 1);
> -                VLOG_ERR_RL(&rl, "lport %s in port group %s not found.",
> -                            nb_pg->ports[i]->name,
> -                            nb_pg->name);
> -                continue;
> -            }
> -
> -            if (!op->od->nbs) {
> -                static struct vlog_rate_limit rl =
VLOG_RATE_LIMIT_INIT(1, 1);
> -                VLOG_WARN_RL(&rl, "lport %s in port group %s has no
lswitch.",
> -                             nb_pg->ports[i]->name,
> -                             nb_pg->name);
> -                continue;
> -            }
> -
> -            struct ls_port_group *ls_pg =
> -                ls_port_group_find(ls_pgs, op->od->nbs);
> -            if (!ls_pg) {
> -                ls_pg = ls_port_group_create(ls_pgs, op->od->nbs,
op->od->sb);
> -            }
> -            ls_port_group_record_add(&ls_pg->nb_pgs, nb_pg, op->key);
> -        }
> -    }
> -}
> -
>  #define IPV6_CT_OMIT_MATCH "nd || nd_ra || nd_rs || mldv1 || mldv2"
>
>  static void
> @@ -7491,7 +7371,8 @@ build_acl_log_related_flows(struct ovn_datapath
*od, struct hmap *lflows,
>
>  static void
>  build_acls(struct ovn_datapath *od, const struct chassis_features
*features,
> -           struct hmap *lflows, const struct hmap *ls_port_groups,
> +           struct hmap *lflows,
> +           const struct ls_port_group_table *ls_port_groups,
>             const struct shash *meter_groups)
>  {
>      const char *default_acl_action = default_acl_drop
> @@ -7678,8 +7559,8 @@ build_acls(struct ovn_datapath *od, const struct
chassis_features *features,
>                       meter_groups, &match, &actions);
>      }
>
> -    const struct ls_port_group *ls_pg =
ls_port_group_find(ls_port_groups,
> -                                                           od->nbs);
> +    const struct ls_port_group *ls_pg =
> +        ls_port_group_table_find(ls_port_groups, od->nbs);
>      if (ls_pg) {
>          const struct ls_port_group_record *ls_pg_rec;
>          HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> @@ -9188,11 +9069,12 @@ build_lswitch_lflows_l2_unknown(struct
ovn_datapath *od,
>  /* Build pre-ACL and ACL tables for both ingress and egress.
>   * Ingress tables 3 through 10.  Egress tables 0 through 7. */
>  static void
> -build_lswitch_lflows_pre_acl_and_acl(struct ovn_datapath *od,
> -                                     const struct hmap *ls_port_groups,
> -                                     const struct chassis_features
*features,
> -                                     struct hmap *lflows,
> -                                     const struct shash *meter_groups)
> +build_lswitch_lflows_pre_acl_and_acl(
> +    struct ovn_datapath *od,
> +    const struct ls_port_group_table *ls_port_groups,
> +    const struct chassis_features *features,
> +    struct hmap *lflows,
> +    const struct shash *meter_groups)
>  {
>      ovs_assert(od->nbs);
>      ls_get_acl_flags(od, ls_port_groups);
> @@ -15502,7 +15384,7 @@ struct lswitch_flow_build_info {
>      const struct ovn_datapaths *lr_datapaths;
>      const struct hmap *ls_ports;
>      const struct hmap *lr_ports;
> -    const struct hmap *ls_port_groups;
> +    const struct ls_port_group_table *ls_port_groups;
>      struct hmap *lflows;
>      struct hmap *igmp_groups;
>      const struct shash *meter_groups;
> @@ -15800,7 +15682,7 @@ build_lswitch_and_lrouter_flows(const struct
ovn_datapaths *ls_datapaths,
>                                  const struct ovn_datapaths *lr_datapaths,
>                                  const struct hmap *ls_ports,
>                                  const struct hmap *lr_ports,
> -                                const struct hmap *ls_port_groups,
> +                                const struct ls_port_group_table *ls_pgs,
>                                  struct hmap *lflows,
>                                  struct hmap *igmp_groups,
>                                  const struct shash *meter_groups,
> @@ -15828,7 +15710,7 @@ build_lswitch_and_lrouter_flows(const struct
ovn_datapaths *ls_datapaths,
>              lsiv[index].lr_datapaths = lr_datapaths;
>              lsiv[index].ls_ports = ls_ports;
>              lsiv[index].lr_ports = lr_ports;
> -            lsiv[index].ls_port_groups = ls_port_groups;
> +            lsiv[index].ls_port_groups = ls_pgs;
>              lsiv[index].igmp_groups = igmp_groups;
>              lsiv[index].meter_groups = meter_groups;
>              lsiv[index].lbs = lbs;
> @@ -15861,7 +15743,7 @@ build_lswitch_and_lrouter_flows(const struct
ovn_datapaths *ls_datapaths,
>              .lr_datapaths = lr_datapaths,
>              .ls_ports = ls_ports,
>              .lr_ports = lr_ports,
> -            .ls_port_groups = ls_port_groups,
> +            .ls_port_groups = ls_pgs,
>              .lflows = lflows,
>              .igmp_groups = igmp_groups,
>              .meter_groups = meter_groups,
> @@ -16546,56 +16428,6 @@ bool lflow_handle_northd_ls_changes(struct
ovsdb_idl_txn *ovnsb_txn,
>
>  }
>
> -/* Each port group in Port_Group table in OVN_Northbound has a
corresponding
> - * entry in Port_Group table in OVN_Southbound. In OVN_Northbound the
entries
> - * contains lport uuids, while in OVN_Southbound we store the lport
names.
> - */
> -static void
> -sync_port_groups(struct ovsdb_idl_txn *ovnsb_txn,
> -                 const struct sbrec_port_group_table
*sbrec_port_group_table,
> -                 struct hmap *ls_pgs)
> -{
> -    struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
> -
> -    const struct sbrec_port_group *sb_port_group;
> -    SBREC_PORT_GROUP_TABLE_FOR_EACH (sb_port_group,
sbrec_port_group_table) {
> -        shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
> -    }
> -
> -    struct ds sb_name = DS_EMPTY_INITIALIZER;
> -
> -    struct ls_port_group *ls_pg;
> -    HMAP_FOR_EACH (ls_pg, key_node, ls_pgs) {
> -        struct ls_port_group_record *ls_pg_rec;
> -
> -        HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> -            get_sb_port_group_name(ls_pg_rec->nb_pg->name,
> -                                   ls_pg->sb_datapath_key,
> -                                   &sb_name);
> -            sb_port_group = shash_find_and_delete(&sb_port_groups,
> -                                                  ds_cstr(&sb_name));
> -            if (!sb_port_group) {
> -                sb_port_group = sbrec_port_group_insert(ovnsb_txn);
> -                sbrec_port_group_set_name(sb_port_group,
ds_cstr(&sb_name));
> -            }
> -
> -            const char **nb_port_names = sset_array(&ls_pg_rec->ports);
> -            sbrec_port_group_set_ports(sb_port_group,
> -                                       nb_port_names,
> -                                       sset_count(&ls_pg_rec->ports));
> -            free(nb_port_names);
> -        }
> -    }
> -    ds_destroy(&sb_name);
> -
> -    struct shash_node *node;
> -    SHASH_FOR_EACH_SAFE (node, &sb_port_groups) {
> -        sbrec_port_group_delete(node->data);
> -        shash_delete(&sb_port_groups, node);
> -    }
> -    shash_destroy(&sb_port_groups);
> -}
> -
>  struct band_entry {
>      int64_t rate;
>      int64_t burst_size;
> @@ -17406,7 +17238,7 @@ northd_init(struct northd_data *data)
>      ovn_datapaths_init(&data->lr_datapaths);
>      hmap_init(&data->ls_ports);
>      hmap_init(&data->lr_ports);
> -    hmap_init(&data->ls_port_groups);
> +    ls_port_group_table_init(&data->ls_port_groups);
>      shash_init(&data->meter_groups);
>      hmap_init(&data->lbs);
>      hmap_init(&data->lb_groups);
> @@ -17438,11 +17270,7 @@ northd_destroy(struct northd_data *data)
>      }
>      hmap_destroy(&data->lb_groups);
>
> -    struct ls_port_group *ls_pg;
> -    HMAP_FOR_EACH_SAFE (ls_pg, key_node, &data->ls_port_groups) {
> -        ls_port_group_destroy(&data->ls_port_groups, ls_pg);
> -    }
> -    hmap_destroy(&data->ls_port_groups);
> +    ls_port_group_table_destroy(&data->ls_port_groups);
>
>      struct shash_node *node;
>      SHASH_FOR_EACH_SAFE (node, &data->meter_groups) {
> @@ -17582,9 +17410,9 @@ ovnnb_db_run(struct northd_input *input_data,
>                         ods_size(&data->ls_datapaths),
>                         ods_size(&data->lr_datapaths));
>      build_ipam(&data->ls_datapaths.datapaths, &data->ls_ports);
> -    build_port_group_lswitches(input_data->nbrec_port_group_table,
> -                               &data->ls_port_groups,
> -                               &data->ls_ports);
> +    ls_port_group_table_build(&data->ls_port_groups,
> +                              input_data->nbrec_port_group_table,
> +                              &data->ls_ports);
>      build_lrouter_groups(&data->lr_ports, &data->lr_list);
>      build_ip_mcast(ovnsb_txn, input_data->sbrec_ip_multicast_table,
>                     input_data->sbrec_ip_mcast_by_dp,
> @@ -17602,8 +17430,9 @@ ovnnb_db_run(struct northd_input *input_data,
>
>      sync_lbs(ovnsb_txn, input_data->sbrec_load_balancer_table,
>               &data->ls_datapaths, &data->lbs);
> -    sync_port_groups(ovnsb_txn, input_data->sbrec_port_group_table,
> -                     &data->ls_port_groups);
> +    ls_port_group_table_sync(&data->ls_port_groups,
> +                             input_data->sbrec_port_group_table,
> +                             ovnsb_txn);
>      sync_meters(ovnsb_txn, input_data->nbrec_meter_table,
>                  input_data->nbrec_acl_table,
input_data->sbrec_meter_table,
>                  &data->meter_groups);
> @@ -17898,3 +17727,12 @@ northd_get_svc_monitor_mac(void)
>  {
>      return svc_monitor_mac;
>  }
> +
> +const struct ovn_datapath *
> +northd_get_datapath_for_port(const struct hmap *ls_ports,
> +                             const char *port_name)
> +{
> +    const struct ovn_port *op = ovn_port_find(ls_ports, port_name);
> +
> +    return op ? op->od : NULL;
> +}
> diff --git a/northd/northd.h b/northd/northd.h
> index 38bc7f50f1..da93a7c6a5 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -19,6 +19,7 @@
>  #include "lib/ovn-util.h"
>  #include "lib/ovs-atomic.h"
>  #include "lib/sset.h"
> +#include "northd/en-port-group.h"
>  #include "northd/ipam.h"
>  #include "openvswitch/hmap.h"
>
> @@ -108,7 +109,7 @@ struct northd_data {
>      struct ovn_datapaths lr_datapaths;
>      struct hmap ls_ports;
>      struct hmap lr_ports;
> -    struct hmap ls_port_groups;         /* Stores struct ls_port_group.
*/
> +    struct ls_port_group_table ls_port_groups;
>      struct shash meter_groups;
>      struct hmap lbs;
>      struct hmap lb_groups;
> @@ -144,7 +145,7 @@ struct lflow_input {
>      const struct ovn_datapaths *lr_datapaths;
>      const struct hmap *ls_ports;
>      const struct hmap *lr_ports;
> -    const struct hmap *ls_port_groups;
> +    const struct ls_port_group_table *ls_port_groups;
>      const struct shash *meter_groups;
>      const struct hmap *lbs;
>      const struct hmap *bfd_connections;
> @@ -314,24 +315,6 @@ struct ovn_datapath {
>      struct hmap ports;
>  };
>
> -/* Per logical switch port group information. */
> -struct ls_port_group {
> -    struct hmap_node key_node;  /* Index on 'nbs->header_.uuid'. */
> -
> -    const struct nbrec_logical_switch *nbs;
> -    int64_t sb_datapath_key; /* SB.Datapath_Binding.tunnel_key. */
> -
> -    /* Port groups with ports attached to 'nbs'. */
> -    struct hmap nb_pgs; /* Stores struct ls_port_group_record. */
> -};
> -
> -struct ls_port_group_record {
> -    struct hmap_node key_node;  /* Index on 'nb_pg->header_.uuid'. */
> -
> -    const struct nbrec_port_group *nb_pg;
> -    struct sset ports;          /* Subset of 'nb_pg' ports in this
record. */
> -};
> -
>  void ovnnb_db_run(struct northd_input *input_data,
>                    struct northd_data *data,
>                    struct ovsdb_idl_txn *ovnnb_txn,
> @@ -369,4 +352,7 @@ void bfd_cleanup_connections(const struct
nbrec_bfd_table *,
>  void run_update_worker_pool(int n_threads);
>
>  const char *northd_get_svc_monitor_mac(void);
> +
> +const struct ovn_datapath *northd_get_datapath_for_port(
> +    const struct hmap *ls_ports, const char *port_name);
>  #endif /* NORTHD_H */
>

Acked-by: Han Zhou <hzhou@ovn.org>
diff mbox series

Patch

diff --git a/northd/automake.mk b/northd/automake.mk
index b17f1fdb54..0fc634b4b4 100644
--- a/northd/automake.mk
+++ b/northd/automake.mk
@@ -14,6 +14,8 @@  northd_ovn_northd_SOURCES = \
 	northd/en-lflow.h \
 	northd/en-northd-output.c \
 	northd/en-northd-output.h \
+	northd/en-port-group.c \
+	northd/en-port-group.h \
 	northd/en-sync-sb.c \
 	northd/en-sync-sb.h \
 	northd/en-sync-from-sb.c \
diff --git a/northd/en-port-group.c b/northd/en-port-group.c
new file mode 100644
index 0000000000..b83926c351
--- /dev/null
+++ b/northd/en-port-group.c
@@ -0,0 +1,237 @@ 
+/*
+ * Copyright (c) 2023, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "openvswitch/vlog.h"
+
+#include "en-port-group.h"
+#include "northd.h"
+
+VLOG_DEFINE_THIS_MODULE(en_port_group);
+
+static struct ls_port_group *ls_port_group_create(
+    struct ls_port_group_table *,
+    const struct nbrec_logical_switch *,
+    const struct sbrec_datapath_binding *);
+
+static void ls_port_group_destroy(struct ls_port_group_table *,
+                                  struct ls_port_group *);
+
+static struct ls_port_group_record *ls_port_group_record_add(
+    struct ls_port_group *,
+    const struct nbrec_port_group *,
+    const char *port_name);
+
+static void ls_port_group_record_destroy(
+    struct ls_port_group *,
+    struct ls_port_group_record *);
+
+void
+ls_port_group_table_init(struct ls_port_group_table *table)
+{
+    *table = (struct ls_port_group_table) {
+        .entries = HMAP_INITIALIZER(&table->entries),
+    };
+}
+
+void
+ls_port_group_table_clear(struct ls_port_group_table *table)
+{
+    struct ls_port_group *ls_pg;
+    HMAP_FOR_EACH_SAFE (ls_pg, key_node, &table->entries) {
+        ls_port_group_destroy(table, ls_pg);
+    }
+}
+
+void
+ls_port_group_table_destroy(struct ls_port_group_table *table)
+{
+    ls_port_group_table_clear(table);
+    hmap_destroy(&table->entries);
+}
+
+struct ls_port_group *
+ls_port_group_table_find(const struct ls_port_group_table *table,
+                         const struct nbrec_logical_switch *nbs)
+{
+    struct ls_port_group *ls_pg;
+
+    HMAP_FOR_EACH_WITH_HASH (ls_pg, key_node, uuid_hash(&nbs->header_.uuid),
+                             &table->entries) {
+        if (nbs == ls_pg->nbs) {
+            return ls_pg;
+        }
+    }
+    return NULL;
+}
+
+void
+ls_port_group_table_build(struct ls_port_group_table *ls_port_groups,
+                          const struct nbrec_port_group_table *pg_table,
+                          const struct hmap *ls_ports)
+{
+    const struct nbrec_port_group *nb_pg;
+    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg, pg_table) {
+        for (size_t i = 0; i < nb_pg->n_ports; i++) {
+            const char *port_name = nb_pg->ports[i]->name;
+            const struct ovn_datapath *od =
+                northd_get_datapath_for_port(ls_ports, port_name);
+
+            if (!od) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+                VLOG_ERR_RL(&rl, "lport %s in port group %s not found.",
+                            port_name, nb_pg->name);
+                continue;
+            }
+
+            if (!od->nbs) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+                VLOG_WARN_RL(&rl, "lport %s in port group %s has no lswitch.",
+                             nb_pg->ports[i]->name,
+                             nb_pg->name);
+                continue;
+            }
+
+            struct ls_port_group *ls_pg =
+                ls_port_group_table_find(ls_port_groups, od->nbs);
+            if (!ls_pg) {
+                ls_pg = ls_port_group_create(ls_port_groups, od->nbs, od->sb);
+            }
+            ls_port_group_record_add(ls_pg, nb_pg, port_name);
+        }
+    }
+}
+
+/* Each port group in Port_Group table in OVN_Northbound has a corresponding
+ * entry in Port_Group table in OVN_Southbound. In OVN_Northbound the entries
+ * contains lport uuids, while in OVN_Southbound we store the lport names.
+ */
+void
+ls_port_group_table_sync(
+    const struct ls_port_group_table *ls_port_groups,
+    const struct sbrec_port_group_table *sbrec_port_group_table,
+    struct ovsdb_idl_txn *ovnsb_txn)
+{
+    struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
+
+    const struct sbrec_port_group *sb_port_group;
+    SBREC_PORT_GROUP_TABLE_FOR_EACH (sb_port_group, sbrec_port_group_table) {
+        shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
+    }
+
+    struct ds sb_name = DS_EMPTY_INITIALIZER;
+
+    struct ls_port_group *ls_pg;
+    HMAP_FOR_EACH (ls_pg, key_node, &ls_port_groups->entries) {
+        struct ls_port_group_record *ls_pg_rec;
+
+        HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
+            get_sb_port_group_name(ls_pg_rec->nb_pg->name,
+                                   ls_pg->sb_datapath_key,
+                                   &sb_name);
+            sb_port_group = shash_find_and_delete(&sb_port_groups,
+                                                  ds_cstr(&sb_name));
+            if (!sb_port_group) {
+                sb_port_group = sbrec_port_group_insert(ovnsb_txn);
+                sbrec_port_group_set_name(sb_port_group, ds_cstr(&sb_name));
+            }
+
+            const char **nb_port_names = sset_array(&ls_pg_rec->ports);
+            sbrec_port_group_set_ports(sb_port_group,
+                                       nb_port_names,
+                                       sset_count(&ls_pg_rec->ports));
+            free(nb_port_names);
+        }
+    }
+    ds_destroy(&sb_name);
+
+    struct shash_node *node;
+    SHASH_FOR_EACH_SAFE (node, &sb_port_groups) {
+        sbrec_port_group_delete(node->data);
+        shash_delete(&sb_port_groups, node);
+    }
+    shash_destroy(&sb_port_groups);
+}
+
+static struct ls_port_group *
+ls_port_group_create(struct ls_port_group_table *ls_port_groups,
+                     const struct nbrec_logical_switch *nbs,
+                     const struct sbrec_datapath_binding *dp)
+{
+    struct ls_port_group *ls_pg = xmalloc(sizeof *ls_pg);
+
+    *ls_pg = (struct ls_port_group) {
+        .nbs = nbs,
+        .sb_datapath_key = dp->tunnel_key,
+        .nb_pgs = HMAP_INITIALIZER(&ls_pg->nb_pgs),
+    };
+    hmap_insert(&ls_port_groups->entries, &ls_pg->key_node,
+                uuid_hash(&nbs->header_.uuid));
+    return ls_pg;
+}
+
+static void
+ls_port_group_destroy(struct ls_port_group_table *ls_port_groups,
+                      struct ls_port_group *ls_pg)
+{
+    if (ls_pg) {
+        struct ls_port_group_record *ls_pg_rec;
+        HMAP_FOR_EACH_SAFE (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
+            ls_port_group_record_destroy(ls_pg, ls_pg_rec);
+        }
+        hmap_destroy(&ls_pg->nb_pgs);
+        hmap_remove(&ls_port_groups->entries, &ls_pg->key_node);
+        free(ls_pg);
+    }
+}
+
+static struct ls_port_group_record *
+ls_port_group_record_add(struct ls_port_group *ls_pg,
+                         const struct nbrec_port_group *nb_pg,
+                         const char *port_name)
+{
+    struct ls_port_group_record *ls_pg_rec = NULL;
+    size_t hash = uuid_hash(&nb_pg->header_.uuid);
+
+    HMAP_FOR_EACH_WITH_HASH (ls_pg_rec, key_node, hash, &ls_pg->nb_pgs) {
+        if (ls_pg_rec->nb_pg == nb_pg) {
+            goto done;
+        }
+    }
+
+    ls_pg_rec = xzalloc(sizeof *ls_pg_rec);
+    *ls_pg_rec = (struct ls_port_group_record) {
+        .nb_pg = nb_pg,
+        .ports = SSET_INITIALIZER(&ls_pg_rec->ports),
+    };
+    hmap_insert(&ls_pg->nb_pgs, &ls_pg_rec->key_node, hash);
+done:
+    sset_add(&ls_pg_rec->ports, port_name);
+    return ls_pg_rec;
+}
+
+static void
+ls_port_group_record_destroy(struct ls_port_group *ls_pg,
+                             struct ls_port_group_record *ls_pg_rec)
+{
+    if (ls_pg_rec) {
+        hmap_remove(&ls_pg->nb_pgs, &ls_pg_rec->key_node);
+        sset_destroy(&ls_pg_rec->ports);
+        free(ls_pg_rec);
+    }
+}
+
diff --git a/northd/en-port-group.h b/northd/en-port-group.h
new file mode 100644
index 0000000000..2c8e01f51f
--- /dev/null
+++ b/northd/en-port-group.h
@@ -0,0 +1,63 @@ 
+/*
+ * Copyright (c) 2023, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EN_PORT_GROUP_H
+#define EN_PORT_GROUP_H 1
+
+#include <stdint.h>
+
+#include "lib/inc-proc-eng.h"
+#include "lib/ovn-nb-idl.h"
+#include "lib/ovn-sb-idl.h"
+#include "openvswitch/hmap.h"
+
+#include "sset.h"
+
+/* Per logical switch port group information. */
+struct ls_port_group_table {
+    struct hmap entries; /* Stores struct ls_port_group. */
+};
+
+struct ls_port_group {
+    struct hmap_node key_node;  /* Index on 'nbs->header_.uuid'. */
+
+    const struct nbrec_logical_switch *nbs;
+    int64_t sb_datapath_key; /* SB.Datapath_Binding.tunnel_key. */
+
+    /* Port groups with ports attached to 'nbs'. */
+    struct hmap nb_pgs; /* Stores struct ls_port_group_record. */
+};
+
+struct ls_port_group_record {
+    struct hmap_node key_node;  /* Index on 'nb_pg->header_.uuid'. */
+
+    const struct nbrec_port_group *nb_pg;
+    struct sset ports;          /* Subset of 'nb_pg' ports in this record. */
+};
+
+void ls_port_group_table_init(struct ls_port_group_table *);
+void ls_port_group_table_clear(struct ls_port_group_table *);
+void ls_port_group_table_destroy(struct ls_port_group_table *);
+struct ls_port_group *ls_port_group_table_find(
+    const struct ls_port_group_table *,
+    const struct nbrec_logical_switch *);
+
+void ls_port_group_table_build(struct ls_port_group_table *ls_port_groups,
+                               const struct nbrec_port_group_table *,
+                               const struct hmap *ls_ports);
+void ls_port_group_table_sync(const struct ls_port_group_table *ls_port_groups,
+                              const struct sbrec_port_group_table *,
+                              struct ovsdb_idl_txn *ovnsb_txn);
+#endif /* EN_PORT_GROUP_H */
diff --git a/northd/northd.c b/northd/northd.c
index 73fab3af7e..04da75fa96 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -21,6 +21,7 @@ 
 #include "bitmap.h"
 #include "coverage.h"
 #include "dirs.h"
+#include "en-port-group.h"
 #include "ipam.h"
 #include "openvswitch/dynamic-string.h"
 #include "hash.h"
@@ -6237,89 +6238,6 @@  build_dhcpv6_action(struct ovn_port *op, struct in6_addr *offer_ip,
     return true;
 }
 
-static struct ls_port_group_record *
-ls_port_group_record_add(struct hmap *nb_pgs,
-                         const struct nbrec_port_group *nb_pg,
-                         const char *port_name)
-{
-    struct ls_port_group_record *ls_pg_rec = NULL;
-    size_t hash = uuid_hash(&nb_pg->header_.uuid);
-
-    HMAP_FOR_EACH_WITH_HASH (ls_pg_rec, key_node, hash, nb_pgs) {
-        if (ls_pg_rec->nb_pg == nb_pg) {
-            goto done;
-        }
-    }
-
-    ls_pg_rec = xzalloc(sizeof *ls_pg_rec);
-    *ls_pg_rec = (struct ls_port_group_record) {
-        .nb_pg = nb_pg,
-        .ports = SSET_INITIALIZER(&ls_pg_rec->ports),
-    };
-    hmap_insert(nb_pgs, &ls_pg_rec->key_node, hash);
-done:
-    sset_add(&ls_pg_rec->ports, port_name);
-    return ls_pg_rec;
-}
-
-static void
-ls_port_group_record_destroy(struct hmap *nb_pgs,
-                             struct ls_port_group_record *ls_pg_rec)
-{
-    if (ls_pg_rec) {
-        hmap_remove(nb_pgs, &ls_pg_rec->key_node);
-        sset_destroy(&ls_pg_rec->ports);
-        free(ls_pg_rec);
-    }
-}
-
-
-static struct ls_port_group *
-ls_port_group_create(struct hmap *ls_port_groups,
-                     const struct nbrec_logical_switch *nbs,
-                     const struct sbrec_datapath_binding *dp)
-{
-    struct ls_port_group *ls_pg = xmalloc(sizeof *ls_pg);
-
-    *ls_pg = (struct ls_port_group) {
-        .nbs = nbs,
-        .sb_datapath_key = dp->tunnel_key,
-        .nb_pgs = HMAP_INITIALIZER(&ls_pg->nb_pgs),
-    };
-    hmap_insert(ls_port_groups, &ls_pg->key_node,
-                uuid_hash(&nbs->header_.uuid));
-    return ls_pg;
-}
-
-static void
-ls_port_group_destroy(struct hmap *ls_port_groups, struct ls_port_group *ls_pg)
-{
-    if (ls_pg) {
-        struct ls_port_group_record *ls_pg_rec;
-        HMAP_FOR_EACH_SAFE (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
-            ls_port_group_record_destroy(&ls_pg->nb_pgs, ls_pg_rec);
-        }
-        hmap_destroy(&ls_pg->nb_pgs);
-        hmap_remove(ls_port_groups, &ls_pg->key_node);
-        free(ls_pg);
-    }
-}
-
-static struct ls_port_group *
-ls_port_group_find(const struct hmap *ls_port_groups,
-                   const struct nbrec_logical_switch *nbs)
-{
-    struct ls_port_group *ls_pg;
-
-    HMAP_FOR_EACH_WITH_HASH (ls_pg, key_node, uuid_hash(&nbs->header_.uuid),
-                             ls_port_groups) {
-        if (nbs == ls_pg->nbs) {
-            return ls_pg;
-        }
-    }
-    return NULL;
-}
-
 static bool
 od_set_acl_flags(struct ovn_datapath *od, struct nbrec_acl **acls,
                  size_t n_acls)
@@ -6351,7 +6269,8 @@  od_set_acl_flags(struct ovn_datapath *od, struct nbrec_acl **acls,
 }
 
 static void
-ls_get_acl_flags(struct ovn_datapath *od, const struct hmap *ls_port_groups)
+ls_get_acl_flags(struct ovn_datapath *od,
+                 const struct ls_port_group_table *ls_port_groups)
 {
     od->has_acls = false;
     od->has_stateful_acl = false;
@@ -6361,15 +6280,13 @@  ls_get_acl_flags(struct ovn_datapath *od, const struct hmap *ls_port_groups)
         return;
     }
 
-    const struct ls_port_group *ls_pg;
-
-    ls_pg = ls_port_group_find(ls_port_groups, od->nbs);
+    const struct ls_port_group *ls_pg =
+        ls_port_group_table_find(ls_port_groups, od->nbs);
     if (!ls_pg) {
         return;
     }
 
-
-    struct ls_port_group_record *ls_pg_rec;
+    const struct ls_port_group_record *ls_pg_rec;
     HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
         if (od_set_acl_flags(od, ls_pg_rec->nb_pg->acls,
                              ls_pg_rec->nb_pg->n_acls)) {
@@ -6578,7 +6495,7 @@  build_stateless_filter(struct ovn_datapath *od,
 
 static void
 build_stateless_filters(struct ovn_datapath *od,
-                        const struct hmap *ls_port_groups,
+                        const struct ls_port_group_table *ls_port_groups,
                         struct hmap *lflows)
 {
     for (size_t i = 0; i < od->nbs->n_acls; i++) {
@@ -6588,8 +6505,8 @@  build_stateless_filters(struct ovn_datapath *od,
         }
     }
 
-    const struct ls_port_group *ls_pg = ls_port_group_find(ls_port_groups,
-                                                           od->nbs);
+    const struct ls_port_group *ls_pg =
+        ls_port_group_table_find(ls_port_groups, od->nbs);
     if (!ls_pg) {
         return;
     }
@@ -6607,7 +6524,8 @@  build_stateless_filters(struct ovn_datapath *od,
 }
 
 static void
-build_pre_acls(struct ovn_datapath *od, const struct hmap *ls_port_groups,
+build_pre_acls(struct ovn_datapath *od,
+               const struct ls_port_group_table *ls_port_groups,
                struct hmap *lflows)
 {
     /* Ingress and Egress Pre-ACL Table (Priority 0): Packets are
@@ -7301,44 +7219,6 @@  ovn_update_ipv6_options(struct hmap *lr_ports)
     }
 }
 
-static void
-build_port_group_lswitches(
-    const struct nbrec_port_group_table *nbrec_port_group_table,
-    struct hmap *ls_pgs, struct hmap *ls_ports)
-{
-    hmap_init(ls_pgs);
-
-    const struct nbrec_port_group *nb_pg;
-    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg, nbrec_port_group_table) {
-        for (size_t i = 0; i < nb_pg->n_ports; i++) {
-            struct ovn_port *op = ovn_port_find(ls_ports,
-                                                nb_pg->ports[i]->name);
-            if (!op) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-                VLOG_ERR_RL(&rl, "lport %s in port group %s not found.",
-                            nb_pg->ports[i]->name,
-                            nb_pg->name);
-                continue;
-            }
-
-            if (!op->od->nbs) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-                VLOG_WARN_RL(&rl, "lport %s in port group %s has no lswitch.",
-                             nb_pg->ports[i]->name,
-                             nb_pg->name);
-                continue;
-            }
-
-            struct ls_port_group *ls_pg =
-                ls_port_group_find(ls_pgs, op->od->nbs);
-            if (!ls_pg) {
-                ls_pg = ls_port_group_create(ls_pgs, op->od->nbs, op->od->sb);
-            }
-            ls_port_group_record_add(&ls_pg->nb_pgs, nb_pg, op->key);
-        }
-    }
-}
-
 #define IPV6_CT_OMIT_MATCH "nd || nd_ra || nd_rs || mldv1 || mldv2"
 
 static void
@@ -7491,7 +7371,8 @@  build_acl_log_related_flows(struct ovn_datapath *od, struct hmap *lflows,
 
 static void
 build_acls(struct ovn_datapath *od, const struct chassis_features *features,
-           struct hmap *lflows, const struct hmap *ls_port_groups,
+           struct hmap *lflows,
+           const struct ls_port_group_table *ls_port_groups,
            const struct shash *meter_groups)
 {
     const char *default_acl_action = default_acl_drop
@@ -7678,8 +7559,8 @@  build_acls(struct ovn_datapath *od, const struct chassis_features *features,
                      meter_groups, &match, &actions);
     }
 
-    const struct ls_port_group *ls_pg = ls_port_group_find(ls_port_groups,
-                                                           od->nbs);
+    const struct ls_port_group *ls_pg =
+        ls_port_group_table_find(ls_port_groups, od->nbs);
     if (ls_pg) {
         const struct ls_port_group_record *ls_pg_rec;
         HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
@@ -9188,11 +9069,12 @@  build_lswitch_lflows_l2_unknown(struct ovn_datapath *od,
 /* Build pre-ACL and ACL tables for both ingress and egress.
  * Ingress tables 3 through 10.  Egress tables 0 through 7. */
 static void
-build_lswitch_lflows_pre_acl_and_acl(struct ovn_datapath *od,
-                                     const struct hmap *ls_port_groups,
-                                     const struct chassis_features *features,
-                                     struct hmap *lflows,
-                                     const struct shash *meter_groups)
+build_lswitch_lflows_pre_acl_and_acl(
+    struct ovn_datapath *od,
+    const struct ls_port_group_table *ls_port_groups,
+    const struct chassis_features *features,
+    struct hmap *lflows,
+    const struct shash *meter_groups)
 {
     ovs_assert(od->nbs);
     ls_get_acl_flags(od, ls_port_groups);
@@ -15502,7 +15384,7 @@  struct lswitch_flow_build_info {
     const struct ovn_datapaths *lr_datapaths;
     const struct hmap *ls_ports;
     const struct hmap *lr_ports;
-    const struct hmap *ls_port_groups;
+    const struct ls_port_group_table *ls_port_groups;
     struct hmap *lflows;
     struct hmap *igmp_groups;
     const struct shash *meter_groups;
@@ -15800,7 +15682,7 @@  build_lswitch_and_lrouter_flows(const struct ovn_datapaths *ls_datapaths,
                                 const struct ovn_datapaths *lr_datapaths,
                                 const struct hmap *ls_ports,
                                 const struct hmap *lr_ports,
-                                const struct hmap *ls_port_groups,
+                                const struct ls_port_group_table *ls_pgs,
                                 struct hmap *lflows,
                                 struct hmap *igmp_groups,
                                 const struct shash *meter_groups,
@@ -15828,7 +15710,7 @@  build_lswitch_and_lrouter_flows(const struct ovn_datapaths *ls_datapaths,
             lsiv[index].lr_datapaths = lr_datapaths;
             lsiv[index].ls_ports = ls_ports;
             lsiv[index].lr_ports = lr_ports;
-            lsiv[index].ls_port_groups = ls_port_groups;
+            lsiv[index].ls_port_groups = ls_pgs;
             lsiv[index].igmp_groups = igmp_groups;
             lsiv[index].meter_groups = meter_groups;
             lsiv[index].lbs = lbs;
@@ -15861,7 +15743,7 @@  build_lswitch_and_lrouter_flows(const struct ovn_datapaths *ls_datapaths,
             .lr_datapaths = lr_datapaths,
             .ls_ports = ls_ports,
             .lr_ports = lr_ports,
-            .ls_port_groups = ls_port_groups,
+            .ls_port_groups = ls_pgs,
             .lflows = lflows,
             .igmp_groups = igmp_groups,
             .meter_groups = meter_groups,
@@ -16546,56 +16428,6 @@  bool lflow_handle_northd_ls_changes(struct ovsdb_idl_txn *ovnsb_txn,
 
 }
 
-/* Each port group in Port_Group table in OVN_Northbound has a corresponding
- * entry in Port_Group table in OVN_Southbound. In OVN_Northbound the entries
- * contains lport uuids, while in OVN_Southbound we store the lport names.
- */
-static void
-sync_port_groups(struct ovsdb_idl_txn *ovnsb_txn,
-                 const struct sbrec_port_group_table *sbrec_port_group_table,
-                 struct hmap *ls_pgs)
-{
-    struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
-
-    const struct sbrec_port_group *sb_port_group;
-    SBREC_PORT_GROUP_TABLE_FOR_EACH (sb_port_group, sbrec_port_group_table) {
-        shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
-    }
-
-    struct ds sb_name = DS_EMPTY_INITIALIZER;
-
-    struct ls_port_group *ls_pg;
-    HMAP_FOR_EACH (ls_pg, key_node, ls_pgs) {
-        struct ls_port_group_record *ls_pg_rec;
-
-        HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
-            get_sb_port_group_name(ls_pg_rec->nb_pg->name,
-                                   ls_pg->sb_datapath_key,
-                                   &sb_name);
-            sb_port_group = shash_find_and_delete(&sb_port_groups,
-                                                  ds_cstr(&sb_name));
-            if (!sb_port_group) {
-                sb_port_group = sbrec_port_group_insert(ovnsb_txn);
-                sbrec_port_group_set_name(sb_port_group, ds_cstr(&sb_name));
-            }
-
-            const char **nb_port_names = sset_array(&ls_pg_rec->ports);
-            sbrec_port_group_set_ports(sb_port_group,
-                                       nb_port_names,
-                                       sset_count(&ls_pg_rec->ports));
-            free(nb_port_names);
-        }
-    }
-    ds_destroy(&sb_name);
-
-    struct shash_node *node;
-    SHASH_FOR_EACH_SAFE (node, &sb_port_groups) {
-        sbrec_port_group_delete(node->data);
-        shash_delete(&sb_port_groups, node);
-    }
-    shash_destroy(&sb_port_groups);
-}
-
 struct band_entry {
     int64_t rate;
     int64_t burst_size;
@@ -17406,7 +17238,7 @@  northd_init(struct northd_data *data)
     ovn_datapaths_init(&data->lr_datapaths);
     hmap_init(&data->ls_ports);
     hmap_init(&data->lr_ports);
-    hmap_init(&data->ls_port_groups);
+    ls_port_group_table_init(&data->ls_port_groups);
     shash_init(&data->meter_groups);
     hmap_init(&data->lbs);
     hmap_init(&data->lb_groups);
@@ -17438,11 +17270,7 @@  northd_destroy(struct northd_data *data)
     }
     hmap_destroy(&data->lb_groups);
 
-    struct ls_port_group *ls_pg;
-    HMAP_FOR_EACH_SAFE (ls_pg, key_node, &data->ls_port_groups) {
-        ls_port_group_destroy(&data->ls_port_groups, ls_pg);
-    }
-    hmap_destroy(&data->ls_port_groups);
+    ls_port_group_table_destroy(&data->ls_port_groups);
 
     struct shash_node *node;
     SHASH_FOR_EACH_SAFE (node, &data->meter_groups) {
@@ -17582,9 +17410,9 @@  ovnnb_db_run(struct northd_input *input_data,
                        ods_size(&data->ls_datapaths),
                        ods_size(&data->lr_datapaths));
     build_ipam(&data->ls_datapaths.datapaths, &data->ls_ports);
-    build_port_group_lswitches(input_data->nbrec_port_group_table,
-                               &data->ls_port_groups,
-                               &data->ls_ports);
+    ls_port_group_table_build(&data->ls_port_groups,
+                              input_data->nbrec_port_group_table,
+                              &data->ls_ports);
     build_lrouter_groups(&data->lr_ports, &data->lr_list);
     build_ip_mcast(ovnsb_txn, input_data->sbrec_ip_multicast_table,
                    input_data->sbrec_ip_mcast_by_dp,
@@ -17602,8 +17430,9 @@  ovnnb_db_run(struct northd_input *input_data,
 
     sync_lbs(ovnsb_txn, input_data->sbrec_load_balancer_table,
              &data->ls_datapaths, &data->lbs);
-    sync_port_groups(ovnsb_txn, input_data->sbrec_port_group_table,
-                     &data->ls_port_groups);
+    ls_port_group_table_sync(&data->ls_port_groups,
+                             input_data->sbrec_port_group_table,
+                             ovnsb_txn);
     sync_meters(ovnsb_txn, input_data->nbrec_meter_table,
                 input_data->nbrec_acl_table, input_data->sbrec_meter_table,
                 &data->meter_groups);
@@ -17898,3 +17727,12 @@  northd_get_svc_monitor_mac(void)
 {
     return svc_monitor_mac;
 }
+
+const struct ovn_datapath *
+northd_get_datapath_for_port(const struct hmap *ls_ports,
+                             const char *port_name)
+{
+    const struct ovn_port *op = ovn_port_find(ls_ports, port_name);
+
+    return op ? op->od : NULL;
+}
diff --git a/northd/northd.h b/northd/northd.h
index 38bc7f50f1..da93a7c6a5 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -19,6 +19,7 @@ 
 #include "lib/ovn-util.h"
 #include "lib/ovs-atomic.h"
 #include "lib/sset.h"
+#include "northd/en-port-group.h"
 #include "northd/ipam.h"
 #include "openvswitch/hmap.h"
 
@@ -108,7 +109,7 @@  struct northd_data {
     struct ovn_datapaths lr_datapaths;
     struct hmap ls_ports;
     struct hmap lr_ports;
-    struct hmap ls_port_groups;         /* Stores struct ls_port_group. */
+    struct ls_port_group_table ls_port_groups;
     struct shash meter_groups;
     struct hmap lbs;
     struct hmap lb_groups;
@@ -144,7 +145,7 @@  struct lflow_input {
     const struct ovn_datapaths *lr_datapaths;
     const struct hmap *ls_ports;
     const struct hmap *lr_ports;
-    const struct hmap *ls_port_groups;
+    const struct ls_port_group_table *ls_port_groups;
     const struct shash *meter_groups;
     const struct hmap *lbs;
     const struct hmap *bfd_connections;
@@ -314,24 +315,6 @@  struct ovn_datapath {
     struct hmap ports;
 };
 
-/* Per logical switch port group information. */
-struct ls_port_group {
-    struct hmap_node key_node;  /* Index on 'nbs->header_.uuid'. */
-
-    const struct nbrec_logical_switch *nbs;
-    int64_t sb_datapath_key; /* SB.Datapath_Binding.tunnel_key. */
-
-    /* Port groups with ports attached to 'nbs'. */
-    struct hmap nb_pgs; /* Stores struct ls_port_group_record. */
-};
-
-struct ls_port_group_record {
-    struct hmap_node key_node;  /* Index on 'nb_pg->header_.uuid'. */
-
-    const struct nbrec_port_group *nb_pg;
-    struct sset ports;          /* Subset of 'nb_pg' ports in this record. */
-};
-
 void ovnnb_db_run(struct northd_input *input_data,
                   struct northd_data *data,
                   struct ovsdb_idl_txn *ovnnb_txn,
@@ -369,4 +352,7 @@  void bfd_cleanup_connections(const struct nbrec_bfd_table *,
 void run_update_worker_pool(int n_threads);
 
 const char *northd_get_svc_monitor_mac(void);
+
+const struct ovn_datapath *northd_get_datapath_for_port(
+    const struct hmap *ls_ports, const char *port_name);
 #endif /* NORTHD_H */