On Jun 27, Mark Michelson via dev wrote:
> In current OVN, the en-northd node (via code in northd.c) takes full
> control of syncing logical switches and logical routers with southbound
> datapath bindings. This is fine, so long as:
> 1) These are the only datapath types to sync.
> 2) northd will always be the arbiter of datapath syncing.
> 
> However, future commits will introduce new types of datapaths. These are
> not good fits for the en-northd node, since they have completely
> independent processing rules, and trying to shoehorn them into a struct
> ovn_datapath would be wasteful.
> 
> This patch introduces a new way of syncing datapaths. Each datapath type
> has a node that is responsible for creating an ovn_unsynced_datapath_map
> for the type of datapath it creates. Then a type-agnostic datapath
> syncing node syncs these datapaths with the southbound Datapath_Binding
> type. Then, these synced datapaths are fed back into type-specific
> datapath nodes, which translate these synced datapaths into specific
> types.
> 
> Nodes can then use these as inputs if they need synced datapaths (i.e. a
> northbound type with its corresponding southbound type). In this case,
> en_northd uses the synced logical switch and logical router types in
> order to create its ovn_datapath structures.
> 
> Doing this will provide an easy way to sync new datapath types to the
> southbound database.
> 
> Signed-off-by: Mark Michelson <mmich...@redhat.com>

Hi Mark,

I think the patch is fine. Just few nit inline, anyway:
Acked-by: Lorenzo Bianconi <lorenzo.bianc...@redhat.com>

Regards,
Lorenzo

[...]
> +enum engine_node_state
> +en_datapath_logical_router_run(struct engine_node *node , void *data)
> +{
> +    const struct nbrec_logical_router_table *nb_lr_table =
> +        EN_OVSDB_GET(engine_get_input("NB_logical_router", node));
> +
> +    struct ovn_unsynced_datapath_map *map = data;
> +
> +    ovn_unsynced_datapath_map_destroy(map);
> +    ovn_unsynced_datapath_map_init(map, DP_ROUTER);
> +
> +    const struct nbrec_logical_router *nbr;
> +    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH (nbr, nb_lr_table) {
> +        if (nbr->enabled && !(*nbr->enabled)) {
> +            continue;
> +        }
> +        struct ovn_unsynced_datapath *dp;
> +        dp = ovn_unsynced_datapath_alloc(nbr->name, DP_ROUTER,
> +                                         smap_get_int(&nbr->options,
> +                                                      "requested-tnl-key", 
> 0),
> +                                         &nbr->header_);
nit: you can just do:
struct ovn_unsynced_datapath *dp = ovn_unsynced_datapath_alloc();
> +
> +        smap_add(&dp->external_ids, "name", dp->name);
> +        const char *neutron_router = smap_get(&nbr->options,
> +                                               "neutron:router_name");
> +        if (neutron_router && neutron_router[0]) {
> +            smap_add(&dp->external_ids, "name2", neutron_router);
> +        }
> +
> +        int64_t ct_zone_limit = ovn_smap_get_llong(&nbr->options,
> +                                                   "ct-zone-limit", -1);
> +        if (ct_zone_limit > 0) {
> +            smap_add_format(&dp->external_ids, "ct-zone-limit", "%"PRId64,
> +                            ct_zone_limit);
> +        }
> +
> +        int nat_default_ct = smap_get_int(&nbr->options,
> +                                          "snat-ct-zone", -1);
> +        if (nat_default_ct >= 0) {
> +            smap_add_format(&dp->external_ids, "snat-ct-zone", "%d",
> +                            nat_default_ct);
> +        }
> +
> +        bool learn_from_arp_request =
> +            smap_get_bool(&nbr->options, "always_learn_from_arp_request",
> +                          true);
> +        if (!learn_from_arp_request) {
> +            smap_add(&dp->external_ids, "always_learn_from_arp_request",
> +                     "false");
> +        }
> +
> +        /* For timestamp refreshing, the smallest threshold of the option is
> +         * set to SB to make sure all entries are refreshed in time.
> +         * This approach simplifies processing in ovn-controller, but it
> +         * may be enhanced, if necessary, to parse the complete CIDR-based
> +         * threshold configurations to SB to reduce unnecessary refreshes. */
> +        uint32_t age_threshold = min_mac_binding_age_threshold(
> +                                       smap_get(&nbr->options,
> +                                               "mac_binding_age_threshold"));
> +        if (age_threshold) {
> +            smap_add_format(&dp->external_ids, "mac_binding_age_threshold",
> +                            "%u", age_threshold);
> +        }
> +
> +        hmap_insert(&map->dps, &dp->hmap_node, 
> uuid_hash(&nbr->header_.uuid));
> +    }
> +
> +    return EN_UPDATED;
> +}
> +

just one new line (here and below when it occurs).

> +
> +void
> +en_datapath_logical_router_cleanup(void *data)
> +{
> +    struct ovn_unsynced_datapath_map *map = data;
> +    ovn_unsynced_datapath_map_destroy(map);
> +}
> +
> +static void
> +synced_logical_router_map_init(
> +    struct ovn_synced_logical_router_map *router_map)
> +{
> +    hmap_init(&router_map->synced_routers);
> +}
> +
> +static void
> +synced_logical_router_map_destroy(
> +    struct ovn_synced_logical_router_map *router_map)
> +{
> +    struct ovn_synced_logical_router *lr;
> +    HMAP_FOR_EACH_POP (lr, hmap_node, &router_map->synced_routers) {
> +        free(lr);
> +    }
> +    hmap_destroy(&router_map->synced_routers);
> +}
> +
> +void *
> +en_datapath_synced_logical_router_init(struct engine_node *node OVS_UNUSED,
> +                                      struct engine_arg *args OVS_UNUSED)
> +{
> +    struct ovn_synced_logical_router_map *router_map;
> +    router_map = xmalloc(sizeof *router_map);
> +    synced_logical_router_map_init(router_map);
> +
> +    return router_map;
> +}
> +
> +enum engine_node_state
> +en_datapath_synced_logical_router_run(struct engine_node *node , void *data)
> +{
> +    const struct ovn_synced_datapaths *dps =
> +        engine_get_input_data("datapath_sync", node);
> +    struct ovn_synced_logical_router_map *router_map = data;
> +
> +    synced_logical_router_map_destroy(router_map);
> +    synced_logical_router_map_init(router_map);
> +
> +    struct ovn_synced_datapath *sdp;
> +    LIST_FOR_EACH (sdp, list_node, &dps->synced_dps) {
> +        if (sdp->nb_row->table->class_ != &nbrec_table_logical_router) {
> +            continue;
> +        }
> +        struct ovn_synced_logical_router *lr = xmalloc(sizeof *lr);
> +        lr->nb = CONTAINER_OF(sdp->nb_row, struct nbrec_logical_router,
> +                              header_);
> +        lr->sb = sdp->sb_dp;
> +        hmap_insert(&router_map->synced_routers, &lr->hmap_node,
> +                    uuid_hash(&lr->nb->header_.uuid));
> +    }
> +
> +    return EN_UPDATED;
> +}
> +
> +void en_datapath_synced_logical_router_cleanup(void *data)
> +{
> +    struct ovn_synced_logical_router_map *router_map = data;
> +    synced_logical_router_map_destroy(router_map);
> +}
> diff --git a/northd/en-datapath-logical-router.h 
> b/northd/en-datapath-logical-router.h
> new file mode 100644
> index 000000000..587de8393
> --- /dev/null
> +++ b/northd/en-datapath-logical-router.h
> @@ -0,0 +1,49 @@
> +/*
> + * Copyright (c) 2025, 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_DATAPATH_LOGICAL_ROUTER_H
> +#define EN_DATAPATH_LOGICAL_ROUTER_H
> +
> +#include "lib/inc-proc-eng.h"
> +#include "openvswitch/hmap.h"
> +
> +
> +void *en_datapath_logical_router_init(struct engine_node *,
> +                                      struct engine_arg *);
> +
> +enum engine_node_state en_datapath_logical_router_run(struct engine_node *,
> +                                                      void *data);
> +void en_datapath_logical_router_cleanup(void *data);
> +
> +struct ovn_synced_logical_router {
> +    struct hmap_node hmap_node;
> +    const struct nbrec_logical_router *nb;
> +    const struct sbrec_datapath_binding *sb;
> +};
> +
> +struct ovn_synced_logical_router_map {
> +    struct hmap synced_routers;
> +};
> +
> +void *en_datapath_synced_logical_router_init(struct engine_node *,
> +                                             struct engine_arg *);
> +
> +enum engine_node_state en_datapath_synced_logical_router_run(
> +    struct engine_node *, void *data);
> +
> +void en_datapath_synced_logical_router_cleanup(void *data);
> +
> +#endif /* EN_DATAPATH_LOGICAL_ROUTER_H */
> diff --git a/northd/en-datapath-logical-switch.c 
> b/northd/en-datapath-logical-switch.c
> new file mode 100644
> index 000000000..2ef2f7d9a
> --- /dev/null
> +++ b/northd/en-datapath-logical-switch.c
> @@ -0,0 +1,172 @@
> +/*
> + * Copyright (c) 2025, 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/hmap.h"
> +#include "openvswitch/util.h"
> +#include "openvswitch/vlog.h"
> +
> +#include "inc-proc-eng.h"
> +#include "ovn-nb-idl.h"
> +#include "datapath-sync.h"
> +#include "en-datapath-logical-switch.h"
> +#include "en-global-config.h"
> +#include "ovn-util.h"
> +
> +VLOG_DEFINE_THIS_MODULE(en_datapath_logical_switch);
> +
> +void *
> +en_datapath_logical_switch_init(struct engine_node *node OVS_UNUSED,
> +                                struct engine_arg *args OVS_UNUSED)
> +{
> +    struct ovn_unsynced_datapath_map *map = xmalloc(sizeof *map);
> +    ovn_unsynced_datapath_map_init(map, DP_SWITCH);
> +    return map;
> +}
> +
> +enum engine_node_state
> +en_datapath_logical_switch_run(struct engine_node *node , void *data)
> +{
> +    const struct nbrec_logical_switch_table *nb_ls_table =
> +        EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
> +    const struct ed_type_global_config *global_config =
> +        engine_get_input_data("global_config", node);
> +
> +    struct ovn_unsynced_datapath_map *map = data;
> +
> +    ovn_unsynced_datapath_map_destroy(map);
> +    ovn_unsynced_datapath_map_init(map, DP_SWITCH);
> +
> +    const struct nbrec_logical_switch *nbs;
> +    NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH (nbs, nb_ls_table) {
> +        uint32_t requested_tunnel_key = smap_get_int(&nbs->other_config,
> +                                                     "requested-tnl-key", 0);
> +        const char *ts = smap_get(&nbs->other_config, "interconn-ts");
> +
> +        if (!ts && global_config->vxlan_mode &&
> +            requested_tunnel_key >= 1 << 12) {
> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> +            VLOG_WARN_RL(&rl, "Tunnel key %"PRIu32" for datapath %s is "
> +                         "incompatible with VXLAN", requested_tunnel_key,
> +                         nbs->name);
> +            requested_tunnel_key = 0;
> +        }
> +
> +        struct ovn_unsynced_datapath *dp;
> +        dp = ovn_unsynced_datapath_alloc(nbs->name, DP_SWITCH,
> +                                         requested_tunnel_key, 
> &nbs->header_);

ditto

> +
> +        smap_init(&dp->external_ids);

this is already initialized in ovn_unsynced_datapath_alloc()

> +        smap_add(&dp->external_ids, "name", dp->name);
> +        const char *neutron_network = smap_get(&nbs->other_config,
> +                                               "neutron:network_name");
> +        if (neutron_network && neutron_network[0]) {
> +            smap_add(&dp->external_ids, "name2", neutron_network);
> +        }
> +
> +        int64_t ct_zone_limit = ovn_smap_get_llong(&nbs->other_config,
> +                                                   "ct-zone-limit", -1);
> +        if (ct_zone_limit > 0) {
> +            smap_add_format(&dp->external_ids, "ct-zone-limit", "%"PRId64,
> +                            ct_zone_limit);
> +        }
> +
> +        if (ts) {
> +            smap_add(&dp->external_ids, "interconn-ts", ts);
> +        }
> +
> +        uint32_t age_threshold = smap_get_uint(&nbs->other_config,
> +                                               "fdb_age_threshold", 0);
> +        if (age_threshold) {
> +            smap_add_format(&dp->external_ids, "fdb_age_threshold",
> +                            "%u", age_threshold);
> +        }
> +
> +        hmap_insert(&map->dps, &dp->hmap_node, 
> uuid_hash(&nbs->header_.uuid));
> +    }
> +
> +    return EN_UPDATED;
> +}
> +
> +
> +void
> +en_datapath_logical_switch_cleanup(void *data)
> +{
> +    struct ovn_unsynced_datapath_map *map = data;
> +    ovn_unsynced_datapath_map_destroy(map);
> +}
> +
> +static void
> +synced_logical_switch_map_init(
> +    struct ovn_synced_logical_switch_map *switch_map)
> +{
> +    hmap_init(&switch_map->synced_switches);
> +}
> +
> +static void
> +synced_logical_switch_map_destroy(
> +    struct ovn_synced_logical_switch_map *switch_map)
> +{
> +    struct ovn_synced_logical_switch *ls;
> +    HMAP_FOR_EACH_POP (ls, hmap_node, &switch_map->synced_switches) {
> +        free(ls);
> +    }
> +    hmap_destroy(&switch_map->synced_switches);
> +}
> +
> +void *
> +en_datapath_synced_logical_switch_init(struct engine_node *node OVS_UNUSED,
> +                                      struct engine_arg *args OVS_UNUSED)
> +{
> +    struct ovn_synced_logical_switch_map *switch_map;
> +    switch_map = xmalloc(sizeof *switch_map);
> +    synced_logical_switch_map_init(switch_map);
> +
> +    return switch_map;
> +}
> +
> +enum engine_node_state
> +en_datapath_synced_logical_switch_run(struct engine_node *node , void *data)
> +{
> +    const struct ovn_synced_datapaths *dps =
> +        engine_get_input_data("datapath_sync", node);
> +    struct ovn_synced_logical_switch_map *switch_map = data;
> +
> +    synced_logical_switch_map_destroy(switch_map);
> +    synced_logical_switch_map_init(switch_map);
> +
> +    struct ovn_synced_datapath *sdp;
> +    LIST_FOR_EACH (sdp, list_node, &dps->synced_dps) {
> +        if (sdp->nb_row->table->class_ != &nbrec_table_logical_switch) {
> +            continue;
> +        }
> +        struct ovn_synced_logical_switch *lsw = xmalloc(sizeof *lsw);
> +        lsw->nb = CONTAINER_OF(sdp->nb_row, struct nbrec_logical_switch,
> +                               header_);
> +        lsw->sb = sdp->sb_dp;
> +        hmap_insert(&switch_map->synced_switches, &lsw->hmap_node,
> +                    uuid_hash(&lsw->nb->header_.uuid));
> +    }
> +
> +    return EN_UPDATED;
> +}
> +
> +void en_datapath_synced_logical_switch_cleanup(void *data)
> +{
> +    struct ovn_synced_logical_switch_map *switch_map = data;
> +    synced_logical_switch_map_destroy(switch_map);
> +}
> diff --git a/northd/en-datapath-logical-switch.h 
> b/northd/en-datapath-logical-switch.h
> new file mode 100644
> index 000000000..1190b7be8
> --- /dev/null
> +++ b/northd/en-datapath-logical-switch.h
> @@ -0,0 +1,48 @@
> +/*
> + * Copyright (c) 2025, 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_DATAPATH_LOGICAL_SWITCH_H
> +#define EN_DATAPATH_LOGICAL_SWITCH_H
> +
> +#include "lib/inc-proc-eng.h"
> +#include "openvswitch/hmap.h"
> +
> +
> +void *en_datapath_logical_switch_init(struct engine_node *,
> +                                      struct engine_arg *);
> +
> +enum engine_node_state en_datapath_logical_switch_run(struct engine_node *,
> +                                                      void *data);
> +void en_datapath_logical_switch_cleanup(void *data);
> +
> +struct ovn_synced_logical_switch {
> +    struct hmap_node hmap_node;
> +    const struct nbrec_logical_switch *nb;
> +    const struct sbrec_datapath_binding *sb;
> +};
> +
> +struct ovn_synced_logical_switch_map {
> +    struct hmap synced_switches;
> +};
> +
> +void *en_datapath_synced_logical_switch_init(struct engine_node *,
> +                                             struct engine_arg *);
> +
> +enum engine_node_state en_datapath_synced_logical_switch_run(
> +    struct engine_node *, void *data);
> +void en_datapath_synced_logical_switch_cleanup(void *data);
> +
> +#endif /* EN_DATAPATH_LOGICAL_SWITCH_H */
> diff --git a/northd/en-datapath-sync.c b/northd/en-datapath-sync.c
> new file mode 100644
> index 000000000..ff1b97e01
> --- /dev/null
> +++ b/northd/en-datapath-sync.c
> @@ -0,0 +1,313 @@
> +/*
> + * Copyright (c) 2025, 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 "uuidset.h"
> +
> +#include "en-datapath-sync.h"
> +#include "en-global-config.h"
> +#include "datapath-sync.h"
> +#include "ovn-sb-idl.h"
> +#include "openvswitch/vlog.h"
> +#include "vec.h"
> +
> +VLOG_DEFINE_THIS_MODULE(datapath_sync);
> +
> +void *
> +en_datapath_sync_init(struct engine_node *node OVS_UNUSED,
> +                      struct engine_arg *args OVS_UNUSED)
> +{
> +    struct ovn_synced_datapaths *synced_datapaths
> +        = xmalloc(sizeof *synced_datapaths);
> +    ovs_list_init(&synced_datapaths->synced_dps);
> +
> +    return synced_datapaths;
> +}
> +
> +static struct ovn_unsynced_datapath *
> +find_unsynced_datapath(const struct ovn_unsynced_datapath_map **maps,
> +                       const struct sbrec_datapath_binding *sb_dp)
> +{
> +    enum ovn_datapath_type dp_type;
> +    dp_type = ovn_datapath_type_from_string(sb_dp->type);
> +    if (dp_type == DP_MAX) {
> +        /* This record was not created by us. It's invalid. */
> +        return NULL;
> +    }
> +
> +    if (!sb_dp->nb_uuid) {
> +        /* This record was not created by us. It's invalid. */
> +        return NULL;
> +    }
> +
> +    uint32_t hash = uuid_hash(sb_dp->nb_uuid);
> +    struct ovn_unsynced_datapath *dp;
> +    HMAP_FOR_EACH_WITH_HASH (dp, hmap_node, hash, &maps[dp_type]->dps) {
> +        if (uuid_equals(sb_dp->nb_uuid, &dp->nb_row->uuid)) {
> +            return dp;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +struct candidate_sdp {
> +    struct ovn_synced_datapath *sdp;
> +    uint32_t requested_tunnel_key;
> +    uint32_t existing_tunnel_key;
> +    bool tunnel_key_assigned;
> +};
> +
> +static struct ovn_synced_datapath *
> +synced_datapath_alloc(const struct ovn_unsynced_datapath *udp,
> +                      const struct sbrec_datapath_binding *sb_dp)
> +{
> +    struct ovn_synced_datapath *sdp;
> +    sdp = xmalloc(sizeof *sdp);
> +    sdp->sb_dp = sb_dp;
> +    sdp->nb_row = udp->nb_row;
> +    sbrec_datapath_binding_set_external_ids(sb_dp, &udp->external_ids);
> +
> +    sbrec_datapath_binding_set_nb_uuid(sb_dp, &udp->nb_row->uuid, 1);
> +
> +    sbrec_datapath_binding_set_type(sb_dp,
> +                                    ovn_datapath_type_to_string(udp->type));
> +    return sdp;
> +}
> +
> +static void
> +reset_synced_datapaths(struct ovn_synced_datapaths *synced_datapaths)
> +{
> +    struct ovn_synced_datapath *sdp;
> +    LIST_FOR_EACH_POP (sdp, list_node, &synced_datapaths->synced_dps) {
> +        free(sdp);
> +    }
> +    ovs_list_init(&synced_datapaths->synced_dps);
> +}
> +
> +static void
> +create_synced_datapath_candidates_from_sb(
> +    const struct sbrec_datapath_binding_table *sb_dp_table,
> +    struct uuidset *visited,
> +    const struct ovn_unsynced_datapath_map **input_maps,
> +    struct vector *candidate_sdps)
> +{
> +    const struct sbrec_datapath_binding *sb_dp;
> +    SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_SAFE (sb_dp, sb_dp_table) {
> +        struct ovn_unsynced_datapath *udp;
> +        udp = find_unsynced_datapath(input_maps, sb_dp);

ditto

> +        if (!udp) {
> +            sbrec_datapath_binding_delete(sb_dp);
> +            continue;
> +        }
> +
> +        if (uuidset_find(visited, &udp->nb_row->uuid)) {
> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> +            VLOG_INFO_RL(
> +                &rl, "deleting Datapath_Binding "UUID_FMT" with "
> +                "duplicate external-ids:nb_uuid "UUID_FMT,
> +                UUID_ARGS(&sb_dp->header_.uuid),
> +                UUID_ARGS(&udp->nb_row->uuid));
> +            sbrec_datapath_binding_delete(sb_dp);
> +            continue;
> +        }
> +
> +        struct candidate_sdp candidate = {
> +            .sdp = synced_datapath_alloc(udp, sb_dp),
> +            .requested_tunnel_key = udp->requested_tunnel_key,
> +            .existing_tunnel_key = sb_dp->tunnel_key,
> +        };
> +        vector_push(candidate_sdps, &candidate);
> +        uuidset_insert(visited, &udp->nb_row->uuid);
> +    }
> +}
> +
> +static void
> +create_synced_datapath_candidates_from_nb(
> +    const struct ovn_unsynced_datapath_map **input_maps,
> +    struct ovsdb_idl_txn *ovnsb_idl_txn,
> +    struct uuidset *visited,
> +    struct vector *candidate_sdps)
> +{
> +    for (size_t i = 0; i < DP_MAX; i++) {
> +        const struct ovn_unsynced_datapath_map *map = input_maps[i];
> +        struct ovn_unsynced_datapath *udp;
> +        HMAP_FOR_EACH (udp, hmap_node, &map->dps) {
> +            if (uuidset_find(visited, &udp->nb_row->uuid)) {
> +                continue;
> +            }
> +            struct sbrec_datapath_binding *sb_dp;
> +            sb_dp = sbrec_datapath_binding_insert(ovnsb_idl_txn);
> +            struct candidate_sdp candidate = {
> +                .sdp = synced_datapath_alloc(udp, sb_dp),
> +                .requested_tunnel_key = udp->requested_tunnel_key,
> +                .existing_tunnel_key = sb_dp->tunnel_key,
> +            };
> +            vector_push(candidate_sdps, &candidate);
> +        }
> +    }
> +}
> +
> +static void
> +assign_requested_tunnel_keys(struct vector *candidate_sdps,
> +                             struct hmap *dp_tnlids,
> +                             struct ovn_synced_datapaths *synced_datapaths)
> +{
> +    struct candidate_sdp *candidate;
> +    VECTOR_FOR_EACH_PTR (candidate_sdps, candidate) {
> +        if (!candidate->requested_tunnel_key) {
> +            continue;
> +        }
> +        if (!ovn_add_tnlid(dp_tnlids, candidate->requested_tunnel_key)) {
> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> +            VLOG_WARN_RL(&rl, "Logical datapath "UUID_FMT" requests same "
> +                         "tunnel key %"PRIu32" as another logical datapath",
> +                         UUID_ARGS(&candidate->sdp->nb_row->uuid),
> +                         candidate->requested_tunnel_key);
> +            continue;
> +        }
> +        sbrec_datapath_binding_set_tunnel_key(candidate->sdp->sb_dp,
> +                                              
> candidate->requested_tunnel_key);
> +        ovs_list_push_back(&synced_datapaths->synced_dps,
> +                           &candidate->sdp->list_node);
> +        candidate->tunnel_key_assigned = true;
> +    }
> +}
> +
> +static void
> +assign_existing_tunnel_keys(struct vector *candidate_sdps,
> +                            struct hmap *dp_tnlids,
> +                            struct ovn_synced_datapaths *synced_datapaths)
> +{
> +    struct candidate_sdp *candidate;
> +    VECTOR_FOR_EACH_PTR (candidate_sdps, candidate) {
> +        if (!candidate->existing_tunnel_key ||
> +            candidate->tunnel_key_assigned) {
> +            continue;
> +        }
> +        /* Existing southbound DP. If this key is available,
> +         * reuse it.
> +         */
> +        if (ovn_add_tnlid(dp_tnlids, candidate->existing_tunnel_key)) {
> +            ovs_list_push_back(&synced_datapaths->synced_dps,
> +                               &candidate->sdp->list_node);
> +            candidate->tunnel_key_assigned = true;
> +        }
> +    }
> +}
> +
> +static void
> +allocate_tunnel_keys(struct vector *candidate_sdps,
> +                     struct hmap *dp_tnlids,
> +                     uint32_t max_dp_tunnel_id,
> +                     struct ovn_synced_datapaths *synced_datapaths)
> +{
> +    uint32_t hint = 0;
> +    struct candidate_sdp *candidate;
> +    VECTOR_FOR_EACH_PTR (candidate_sdps, candidate) {
> +        if (candidate->tunnel_key_assigned) {
> +            continue;
> +        }
> +        uint32_t tunnel_key =
> +            ovn_allocate_tnlid(dp_tnlids, "datapath", OVN_MIN_DP_KEY_LOCAL,
> +                               max_dp_tunnel_id, &hint);
> +        if (!tunnel_key) {
> +            continue;
> +        }
> +        sbrec_datapath_binding_set_tunnel_key(candidate->sdp->sb_dp,
> +                                              tunnel_key);
> +        ovs_list_push_back(&synced_datapaths->synced_dps,
> +                           &candidate->sdp->list_node);
> +        candidate->tunnel_key_assigned = true;
> +    }
> +}
> +
> +static void
> +delete_unassigned_candidates(struct vector *candidate_sdps)
> +{
> +    struct candidate_sdp *candidate;
> +    VECTOR_FOR_EACH_PTR (candidate_sdps, candidate) {
> +        if (candidate->tunnel_key_assigned) {
> +            continue;
> +        }
> +        sbrec_datapath_binding_delete(candidate->sdp->sb_dp);
> +        free(candidate->sdp);
> +    }
> +}
> +
> +enum engine_node_state
> +en_datapath_sync_run(struct engine_node *node , void *data)
> +{
> +    const struct sbrec_datapath_binding_table *sb_dp_table =
> +        EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
> +    const struct ed_type_global_config *global_config =
> +        engine_get_input_data("global_config", node);
> +    const struct ovn_unsynced_datapath_map *unsynced_ls_map =
> +        engine_get_input_data("datapath_logical_switch", node);
> +    const struct ovn_unsynced_datapath_map *unsynced_lr_map =
> +        engine_get_input_data("datapath_logical_router", node);
> +
> +    const struct ovn_unsynced_datapath_map *input_maps[DP_MAX];
> +    struct ovn_synced_datapaths *synced_datapaths = data;
> +
> +    input_maps[unsynced_ls_map->dp_type] = unsynced_ls_map;
> +    input_maps[unsynced_lr_map->dp_type] = unsynced_lr_map;
> +
> +    size_t num_datapaths = 0;
> +    for (enum ovn_datapath_type i = 0; i < DP_MAX; i++) {
> +        ovs_assert(input_maps[i]);
> +        num_datapaths += hmap_count(&input_maps[i]->dps);
> +    }
> +
> +    reset_synced_datapaths(synced_datapaths);
> +
> +    struct uuidset visited = UUIDSET_INITIALIZER(&visited);
> +    struct vector candidate_sdps =
> +        VECTOR_CAPACITY_INITIALIZER(struct candidate_sdp, num_datapaths);
> +    create_synced_datapath_candidates_from_sb(sb_dp_table, &visited,
> +                                              input_maps, &candidate_sdps);
> +
> +    const struct engine_context *eng_ctx = engine_get_context();
> +    create_synced_datapath_candidates_from_nb(input_maps,
> +                                              eng_ctx->ovnsb_idl_txn, 
> &visited,
> +                                              &candidate_sdps);
> +    uuidset_destroy(&visited);
> +
> +    struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
> +    assign_requested_tunnel_keys(&candidate_sdps, &dp_tnlids,
> +                                 synced_datapaths);
> +    assign_existing_tunnel_keys(&candidate_sdps, &dp_tnlids, 
> synced_datapaths);
> +    allocate_tunnel_keys(&candidate_sdps, &dp_tnlids,
> +                         global_config->max_dp_tunnel_id, synced_datapaths);
> +
> +    delete_unassigned_candidates(&candidate_sdps);
> +    vector_destroy(&candidate_sdps);
> +
> +    ovn_destroy_tnlids(&dp_tnlids);
> +
> +    return EN_UPDATED;
> +}
> +
> +void en_datapath_sync_cleanup(void *data)
> +{
> +    struct ovn_synced_datapaths *synced_datapaths = data;
> +    struct ovn_synced_datapath *sdp;
> +
> +    LIST_FOR_EACH_POP (sdp, list_node, &synced_datapaths->synced_dps) {
> +        free(sdp);
> +    }
> +}
> diff --git a/northd/en-datapath-sync.h b/northd/en-datapath-sync.h
> new file mode 100644
> index 000000000..3b3262304
> --- /dev/null
> +++ b/northd/en-datapath-sync.h
> @@ -0,0 +1,27 @@
> +/*
> + * Copyright (c) 2025, 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_DATAPATH_SYNC_H
> +#define EN_DATAPATH_SYNC_H
> +
> +#include "inc-proc-eng.h"
> +
> +void *en_datapath_sync_init(struct engine_node *,
> +                            struct engine_arg *);
> +enum engine_node_state en_datapath_sync_run(struct engine_node *, void 
> *data);
> +void en_datapath_sync_cleanup(void *data);
> +
> +#endif /* EN_DATAPATH_SYNC_H */
> diff --git a/northd/en-global-config.c b/northd/en-global-config.c
> index ee5adfd3b..5a0b4c600 100644
> --- a/northd/en-global-config.c
> +++ b/northd/en-global-config.c
> @@ -63,6 +63,17 @@ en_global_config_init(struct engine_node *node OVS_UNUSED,
>      return data;
>  }
>  
> +static uint32_t
> +get_ovn_max_dp_key_local(bool vxlan_mode, bool vxlan_ic_mode)
> +{
> +    if (vxlan_mode) {
> +        /* OVN_MAX_DP_GLOBAL_NUM doesn't apply for VXLAN mode. */
> +        return vxlan_ic_mode ? OVN_MAX_DP_VXLAN_KEY_LOCAL
> +                             : OVN_MAX_DP_VXLAN_KEY;
> +    }
> +    return vxlan_ic_mode ? OVN_MAX_DP_VXLAN_KEY_LOCAL : OVN_MAX_DP_KEY_LOCAL;
> +}
> +
>  enum engine_node_state
>  en_global_config_run(struct engine_node *node , void *data)
>  {
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index 8402ab943..0c66a6700 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -119,6 +119,12 @@ northd_get_input_data(struct engine_node *node,
>      input_data->svc_monitor_mac_ea = global_config->svc_monitor_mac_ea;
>      input_data->features = &global_config->features;
>      input_data->vxlan_mode = global_config->vxlan_mode;
> +
> +    input_data->synced_lses =
> +        engine_get_input_data("datapath_synced_logical_switch", node);
> +
> +    input_data->synced_lrs =
> +        engine_get_input_data("datapath_synced_logical_router", node);
>  }
>  
>  enum engine_node_state
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index d43bfc16c..4627a053c 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -47,6 +47,9 @@
>  #include "en-advertised-route-sync.h"
>  #include "en-learned-route-sync.h"
>  #include "en-group-ecmp-route.h"
> +#include "en-datapath-logical-router.h"
> +#include "en-datapath-logical-switch.h"
> +#include "en-datapath-sync.h"
>  #include "unixctl.h"
>  #include "util.h"
>  
> @@ -179,6 +182,11 @@ static ENGINE_NODE(advertised_route_sync);
>  static ENGINE_NODE(learned_route_sync, CLEAR_TRACKED_DATA);
>  static ENGINE_NODE(dynamic_routes);
>  static ENGINE_NODE(group_ecmp_route, CLEAR_TRACKED_DATA);
> +static ENGINE_NODE(datapath_logical_router);
> +static ENGINE_NODE(datapath_logical_switch);
> +static ENGINE_NODE(datapath_synced_logical_router);
> +static ENGINE_NODE(datapath_synced_logical_switch);
> +static ENGINE_NODE(datapath_sync);
>  
>  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>                            struct ovsdb_idl_loop *sb)
> @@ -209,6 +217,21 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>      engine_add_input(&en_acl_id, &en_nb_acl, NULL);
>      engine_add_input(&en_acl_id, &en_sb_acl_id, NULL);
>  
> +    engine_add_input(&en_datapath_logical_switch, &en_nb_logical_switch, 
> NULL);
> +    engine_add_input(&en_datapath_logical_switch, &en_global_config, NULL);
> +
> +    engine_add_input(&en_datapath_logical_router, &en_nb_logical_router, 
> NULL);
> +
> +    engine_add_input(&en_datapath_sync, &en_datapath_logical_switch, NULL);
> +    engine_add_input(&en_datapath_sync, &en_datapath_logical_router, NULL);
> +    engine_add_input(&en_datapath_sync, &en_sb_datapath_binding, NULL);
> +    engine_add_input(&en_datapath_sync, &en_global_config, NULL);
> +
> +    engine_add_input(&en_datapath_synced_logical_router, &en_datapath_sync,
> +                     NULL);
> +    engine_add_input(&en_datapath_synced_logical_switch, &en_datapath_sync,
> +                     NULL);
> +
>      engine_add_input(&en_northd, &en_nb_mirror, NULL);
>      engine_add_input(&en_northd, &en_nb_mirror_rule, NULL);
>      engine_add_input(&en_northd, &en_nb_static_mac_binding, NULL);
> @@ -249,6 +272,23 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>      engine_add_input(&en_northd, &en_nb_port_group,
>                       northd_nb_port_group_handler);
>  
> +    /* Currently, northd handles logical router and switch changes in nodes
> +     * that read directly from the northbound logical tables. Those nodes
> +     * will trigger a recompute if conditions on changed logical routers
> +     * or logical switches cannot be handled. From en-northd's perspective,
> +     * synced logical switch and router changes are always handled.
> +     *
> +     * Once datapath syncing has incremental processing added, then
> +     * en-northd can move its logical router and switch change handling to
> +     * handlers defined here, and there will be no need for en_northd to
> +     * read directly from the northbound database for incremental handling
> +     * of these types.
> +     */
> +    engine_add_input(&en_northd, &en_datapath_synced_logical_router,
> +                     engine_noop_handler);
> +    engine_add_input(&en_northd, &en_datapath_synced_logical_switch,
> +                     engine_noop_handler);
> +
>      engine_add_input(&en_lr_nat, &en_northd, lr_nat_northd_handler);
>  
>      engine_add_input(&en_lr_stateful, &en_northd, 
> lr_stateful_northd_handler);
> diff --git a/northd/northd.c b/northd/northd.c
> index 49ed36df7..faa520ecd 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -52,6 +52,8 @@
>  #include "en-ls-stateful.h"
>  #include "en-multicast.h"
>  #include "en-sampling-app.h"
> +#include "en-datapath-logical-switch.h"
> +#include "en-datapath-logical-router.h"
>  #include "lib/ovn-parallel-hmap.h"
>  #include "ovn/actions.h"
>  #include "ovn/features.h"
> @@ -96,8 +98,6 @@ static bool default_acl_drop;
>   * and ports tunnel key allocation (12 bits for each instead of default 16). 
> */
>  static bool vxlan_mode;
>  
> -static bool vxlan_ic_mode;
> -
>  #define MAX_OVN_TAGS 4096
>  
>  
> @@ -482,6 +482,8 @@ struct lrouter_group {
>      struct hmapx tmp_ha_ref_chassis;
>  };
>  
> +static void init_mcast_info_for_datapath(struct ovn_datapath *od);
> +
>  static struct ovn_datapath *
>  ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
>                      const struct nbrec_logical_switch *nbs,
> @@ -504,6 +506,8 @@ ovn_datapath_create(struct hmap *datapaths, const struct 
> uuid *key,
>      od->l3dgw_ports = VECTOR_EMPTY_INITIALIZER(struct ovn_port *);
>      od->localnet_ports = VECTOR_EMPTY_INITIALIZER(struct ovn_port *);
>      od->lb_with_stateless_mode = false;
> +    od->tunnel_key = sb->tunnel_key;
> +    init_mcast_info_for_datapath(od);
>      return od;
>  }
>  
> @@ -747,84 +751,6 @@ store_mcast_info_for_switch_datapath(const struct 
> sbrec_ip_multicast *sb,
>      }
>  }
>  
> -static void
> -ovn_datapath_update_external_ids(struct ovn_datapath *od)
> -{
> -    /* Get the NB  UUID to set in external-ids. */
> -    char uuid_s[UUID_LEN + 1];
> -    sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key));
> -
> -    /* Get names to set in external-ids. */
> -    const char *name = od->nbs ? od->nbs->name : od->nbr->name;
> -    const char *name2 = (od->nbs
> -                         ? smap_get(&od->nbs->external_ids,
> -                                    "neutron:network_name")
> -                         : smap_get(&od->nbr->external_ids,
> -                                    "neutron:router_name"));
> -
> -    /* Set external-ids. */
> -    struct smap ids = SMAP_INITIALIZER(&ids);
> -    smap_add(&ids, "name", name);
> -    if (name2 && name2[0]) {
> -        smap_add(&ids, "name2", name2);
> -    }
> -
> -    int64_t ct_zone_limit = ovn_smap_get_llong(od->nbs ?
> -                                               &od->nbs->other_config :
> -                                               &od->nbr->options,
> -                                               "ct-zone-limit", -1);
> -    if (ct_zone_limit > 0) {
> -        smap_add_format(&ids, "ct-zone-limit", "%"PRId64, ct_zone_limit);
> -    }
> -
> -    /* Set interconn-ts. */
> -    if (od->nbs) {
> -        const char *ts = smap_get(&od->nbs->other_config, "interconn-ts");
> -        if (ts) {
> -            smap_add(&ids, "interconn-ts", ts);
> -        }
> -
> -        uint32_t age_threshold = smap_get_uint(&od->nbs->other_config,
> -                                               "fdb_age_threshold", 0);
> -        if (age_threshold) {
> -            smap_add_format(&ids, "fdb_age_threshold",
> -                            "%u", age_threshold);
> -        }
> -    }
> -
> -    /* Set snat-ct-zone */
> -    if (od->nbr) {
> -        int nat_default_ct = smap_get_int(&od->nbr->options,
> -                                           "snat-ct-zone", -1);
> -        if (nat_default_ct >= 0) {
> -            smap_add_format(&ids, "snat-ct-zone", "%d", nat_default_ct);
> -        }
> -
> -        bool learn_from_arp_request =
> -            smap_get_bool(&od->nbr->options, "always_learn_from_arp_request",
> -                          true);
> -        if (!learn_from_arp_request) {
> -            smap_add(&ids, "always_learn_from_arp_request", "false");
> -        }
> -
> -        /* For timestamp refreshing, the smallest threshold of the option is
> -         * set to SB to make sure all entries are refreshed in time.
> -         * XXX: This approach simplifies processing in ovn-controller, but it
> -         * may be enhanced, if necessary, to parse the complete CIDR-based
> -         * threshold configurations to SB to reduce unnecessary refreshes. */
> -        uint32_t age_threshold = min_mac_binding_age_threshold(
> -                                       smap_get(&od->nbr->options,
> -                                               "mac_binding_age_threshold"));
> -        if (age_threshold) {
> -            smap_add_format(&ids, "mac_binding_age_threshold",
> -                            "%u", age_threshold);
> -        }
> -    }
> -
> -    sbrec_datapath_binding_set_external_ids(od->sb, &ids);
> -    smap_destroy(&ids);
> -}
> -
>  static enum dynamic_routing_redistribute_mode
>  parse_dynamic_routing_redistribute(
>      const struct smap *options,
> @@ -876,171 +802,6 @@ parse_dynamic_routing_redistribute(
>      return out;
>  }
>  
> -static void
> -join_datapaths(const struct nbrec_logical_switch_table *nbrec_ls_table,
> -               const struct nbrec_logical_router_table *nbrec_lr_table,
> -               const struct sbrec_datapath_binding_table *sbrec_dp_table,
> -               struct ovsdb_idl_txn *ovnsb_txn,
> -               struct hmap *datapaths, struct ovs_list *sb_only,
> -               struct ovs_list *nb_only, struct ovs_list *both)
> -{
> -    ovs_list_init(sb_only);
> -    ovs_list_init(nb_only);
> -    ovs_list_init(both);
> -
> -    const struct sbrec_datapath_binding *sb;
> -    SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_SAFE (sb, sbrec_dp_table) {
> -        if (!sb->nb_uuid) {
> -            ovsdb_idl_txn_add_comment(
> -                ovnsb_txn,
> -                "deleting Datapath_Binding "UUID_FMT" that lacks nb_uuid",
> -                UUID_ARGS(&sb->header_.uuid));
> -            sbrec_datapath_binding_delete(sb);
> -            continue;
> -        }
> -
> -        if (ovn_datapath_find_(datapaths, sb->nb_uuid)) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_INFO_RL(
> -                &rl, "deleting Datapath_Binding "UUID_FMT" with "
> -                "duplicate nb_uuid "UUID_FMT,
> -                UUID_ARGS(&sb->header_.uuid), UUID_ARGS(sb->nb_uuid));
> -            sbrec_datapath_binding_delete(sb);
> -            continue;
> -        }
> -
> -        struct ovn_datapath *od = ovn_datapath_create(datapaths, sb->nb_uuid,
> -                                                      NULL, NULL, sb);
> -        ovs_list_push_back(sb_only, &od->list);
> -    }
> -
> -    vxlan_ic_mode = false;
> -    const struct nbrec_logical_switch *nbs;
> -    NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH (nbs, nbrec_ls_table) {
> -        struct ovn_datapath *od = ovn_datapath_find_(datapaths,
> -                                                     &nbs->header_.uuid);
> -        if (od) {
> -            od->nbs = nbs;
> -            ovs_list_remove(&od->list);
> -            ovs_list_push_back(both, &od->list);
> -            ovn_datapath_update_external_ids(od);
> -        } else {
> -            od = ovn_datapath_create(datapaths, &nbs->header_.uuid,
> -                                     nbs, NULL, NULL);
> -            ovs_list_push_back(nb_only, &od->list);
> -        }
> -
> -        init_ipam_info_for_datapath(od);
> -        init_mcast_info_for_datapath(od);
> -
> -        if (smap_get_bool(&nbs->other_config, "ic-vxlan_mode", false)) {
> -            vxlan_ic_mode = true;
> -        }
> -
> -        if (smap_get_bool(&nbs->other_config, "enable-stateless-acl-with-lb",
> -                          false)) {
> -            od->lb_with_stateless_mode = true;
> -        }
> -    }
> -
> -    const struct nbrec_logical_router *nbr;
> -    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH (nbr, nbrec_lr_table) {
> -        if (!lrouter_is_enabled(nbr)) {
> -            continue;
> -        }
> -
> -        struct ovn_datapath *od = ovn_datapath_find_(datapaths,
> -                                                     &nbr->header_.uuid);
> -        if (od) {
> -            if (!od->nbs) {
> -                od->nbr = nbr;
> -                ovs_list_remove(&od->list);
> -                ovs_list_push_back(both, &od->list);
> -                ovn_datapath_update_external_ids(od);
> -            } else {
> -                /* Can't happen! */
> -                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 
> 1);
> -                VLOG_WARN_RL(&rl,
> -                             "duplicate UUID "UUID_FMT" in OVN_Northbound",
> -                             UUID_ARGS(&nbr->header_.uuid));
> -                continue;
> -            }
> -        } else {
> -            od = ovn_datapath_create(datapaths, &nbr->header_.uuid,
> -                                     NULL, nbr, NULL);
> -            ovs_list_push_back(nb_only, &od->list);
> -        }
> -        init_mcast_info_for_datapath(od);
> -        if (smap_get(&od->nbr->options, "chassis")) {
> -            od->is_gw_router = true;
> -        }
> -        od->dynamic_routing = smap_get_bool(&od->nbr->options,
> -                                            "dynamic-routing", false);
> -        od->dynamic_routing_redistribute =
> -            parse_dynamic_routing_redistribute(&od->nbr->options, DRRM_NONE,
> -                                               od->nbr->name);
> -    }
> -}
> -
> -
> -uint32_t
> -get_ovn_max_dp_key_local(bool _vxlan_mode, bool _vxlan_ic_mode)
> -{
> -    if (_vxlan_mode) {
> -        /* OVN_MAX_DP_GLOBAL_NUM doesn't apply for VXLAN mode. */
> -        return _vxlan_ic_mode ? OVN_MAX_DP_VXLAN_KEY_LOCAL
> -                              : OVN_MAX_DP_VXLAN_KEY;
> -    }
> -    return _vxlan_ic_mode ? OVN_MAX_DP_VXLAN_KEY_LOCAL : 
> OVN_MAX_DP_KEY_LOCAL;
> -}
> -
> -static void
> -ovn_datapath_allocate_key(struct hmap *datapaths, struct hmap *dp_tnlids,
> -                          struct ovn_datapath *od, uint32_t *hint)
> -{
> -    if (!od->tunnel_key) {
> -        od->tunnel_key = ovn_allocate_tnlid(dp_tnlids, "datapath",
> -            OVN_MIN_DP_KEY_LOCAL,
> -            get_ovn_max_dp_key_local(vxlan_mode, vxlan_ic_mode), hint);
> -        if (!od->tunnel_key) {
> -            if (od->sb) {
> -                sbrec_datapath_binding_delete(od->sb);
> -            }
> -            ovs_list_remove(&od->list);
> -            ovn_datapath_destroy(datapaths, od);
> -        }
> -    }
> -}
> -
> -static void
> -ovn_datapath_assign_requested_tnl_id(
> -    struct hmap *dp_tnlids, struct ovn_datapath *od)
> -{
> -    const struct smap *other_config = (od->nbs
> -                                       ? &od->nbs->other_config
> -                                       : &od->nbr->options);
> -    uint32_t tunnel_key = smap_get_int(other_config, "requested-tnl-key", 0);
> -    if (tunnel_key) {
> -        const char *interconn_ts = smap_get(other_config, "interconn-ts");
> -        if (!interconn_ts && vxlan_mode && tunnel_key >= 1 << 12) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -            VLOG_WARN_RL(&rl, "Tunnel key %"PRIu32" for datapath %s is "
> -                         "incompatible with VXLAN", tunnel_key,
> -                         od->nbs ? od->nbs->name : od->nbr->name);
> -            return;
> -        }
> -        if (ovn_add_tnlid(dp_tnlids, tunnel_key)) {
> -            od->tunnel_key = tunnel_key;
> -        } else {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -            VLOG_WARN_RL(&rl, "Logical %s %s requests same tunnel key "
> -                         "%"PRIu32" as another logical switch or router",
> -                         od->nbs ? "switch" : "router",
> -                         od->nbs ? od->nbs->name : od->nbr->name, 
> tunnel_key);
> -        }
> -    }
> -}
> -
>  static void
>  ods_build_array_index(struct ovn_datapaths *datapaths)
>  {
> @@ -1060,88 +821,44 @@ ods_build_array_index(struct ovn_datapaths *datapaths)
>      }
>  }
>  
> -/* Updates the southbound Datapath_Binding table so that it contains the
> - * logical switches and routers specified by the northbound database.
> - *
> - * Initializes 'datapaths' to contain a "struct ovn_datapath" for every 
> logical
> - * switch and router. */
> +/* Initializes 'ls_datapaths' to contain a "struct ovn_datapath" for every
> + * logical switch, and initializes 'lr_datapaths' to contain a
> + * "struct ovn_datapath" for every logical router.
> + */
>  static void
> -build_datapaths(struct ovsdb_idl_txn *ovnsb_txn,
> -                const struct nbrec_logical_switch_table *nbrec_ls_table,
> -                const struct nbrec_logical_router_table *nbrec_lr_table,
> -                const struct sbrec_datapath_binding_table *sbrec_dp_table,
> +build_datapaths(const struct ovn_synced_logical_switch_map *ls_map,
> +                const struct ovn_synced_logical_router_map *lr_map,
>                  struct ovn_datapaths *ls_datapaths,
>                  struct ovn_datapaths *lr_datapaths)
>  {
> -    struct ovs_list sb_only, nb_only, both;
> -
> -    struct hmap *datapaths = &ls_datapaths->datapaths;
> -    join_datapaths(nbrec_ls_table, nbrec_lr_table, sbrec_dp_table, ovnsb_txn,
> -                   datapaths, &sb_only, &nb_only, &both);
> -
> -    /* Assign explicitly requested tunnel ids first. */
> -    struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
> -    struct ovn_datapath *od;
> -    LIST_FOR_EACH (od, list, &both) {
> -        ovn_datapath_assign_requested_tnl_id(&dp_tnlids, od);
> -    }
> -    LIST_FOR_EACH (od, list, &nb_only) {
> -        ovn_datapath_assign_requested_tnl_id(&dp_tnlids, od);
> -    }
> -
> -    /* Keep nonconflicting tunnel IDs that are already assigned. */
> -    LIST_FOR_EACH (od, list, &both) {
> -        if (!od->tunnel_key && ovn_add_tnlid(&dp_tnlids, 
> od->sb->tunnel_key)) {
> -            od->tunnel_key = od->sb->tunnel_key;
> -        }
> -    }
> -
> -    /* Assign new tunnel ids where needed. */
> -    uint32_t hint = 0;
> -    LIST_FOR_EACH_SAFE (od, list, &both) {
> -        ovn_datapath_allocate_key(datapaths, &dp_tnlids, od, &hint);
> -    }
> -    LIST_FOR_EACH_SAFE (od, list, &nb_only) {
> -        ovn_datapath_allocate_key(datapaths, &dp_tnlids, od, &hint);
> -    }
> -
> -    /* Sync tunnel ids from nb to sb. */
> -    LIST_FOR_EACH (od, list, &both) {
> -        if (od->sb->tunnel_key != od->tunnel_key) {
> -            sbrec_datapath_binding_set_tunnel_key(od->sb, od->tunnel_key);
> +    struct ovn_synced_logical_switch *ls;
> +    HMAP_FOR_EACH (ls, hmap_node, &ls_map->synced_switches) {
> +        struct ovn_datapath *od =
> +            ovn_datapath_create(&ls_datapaths->datapaths,
> +                                &ls->nb->header_.uuid,
> +                                ls->nb, NULL, ls->sb);
> +        init_ipam_info_for_datapath(od);
> +        if (smap_get_bool(&od->nbs->other_config,
> +                          "enable-stateless-acl-with-lb",
> +                          false)) {
> +            od->lb_with_stateless_mode = true;
>          }
> -        ovn_datapath_update_external_ids(od);
> -        sbrec_datapath_binding_set_nb_uuid(od->sb, &od->key, 1);
> -        sbrec_datapath_binding_set_type(od->sb, od->nbs ? "logical-switch" :
> -                                        "logical-router");
> -    }
> -    LIST_FOR_EACH (od, list, &nb_only) {
> -        od->sb = sbrec_datapath_binding_insert(ovnsb_txn);
> -        ovn_datapath_update_external_ids(od);
> -        sbrec_datapath_binding_set_tunnel_key(od->sb, od->tunnel_key);
> -        sbrec_datapath_binding_set_nb_uuid(od->sb, &od->key, 1);
> -        sbrec_datapath_binding_set_type(od->sb, od->nbs ? "logical-switch" :
> -                                        "logical-router");
>      }
> -    ovn_destroy_tnlids(&dp_tnlids);
>  
> -    /* Delete southbound records without northbound matches. */
> -    LIST_FOR_EACH_SAFE (od, list, &sb_only) {
> -        ovs_list_remove(&od->list);
> -        sbrec_datapath_binding_delete(od->sb);
> -        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;
> +    struct ovn_synced_logical_router *lr;
> +    HMAP_FOR_EACH (lr, hmap_node, &lr_map->synced_routers) {
> +        struct ovn_datapath *od =
> +            ovn_datapath_create(&lr_datapaths->datapaths,
> +                                &lr->nb->header_.uuid,
> +                                NULL, lr->nb, lr->sb);
> +        if (smap_get(&od->nbr->options, "chassis")) {
> +            od->is_gw_router = true;
>          }
> -        hmap_remove(datapaths, &od->key_node);
> -        hmap_insert(&lr_datapaths->datapaths, &od->key_node,
> -                    od->key_node.hash);
> +        od->dynamic_routing = smap_get_bool(&od->nbr->options,
> +                                            "dynamic-routing", false);
> +        od->dynamic_routing_redistribute =
> +            parse_dynamic_routing_redistribute(&od->nbr->options, DRRM_NONE,
> +                                               od->nbr->name);
>      }
>  
>      ods_build_array_index(ls_datapaths);
> @@ -19466,10 +19183,8 @@ ovnnb_db_run(struct northd_input *input_data,
>  
>      vxlan_mode = input_data->vxlan_mode;
>  
> -    build_datapaths(ovnsb_txn,
> -                    input_data->nbrec_logical_switch_table,
> -                    input_data->nbrec_logical_router_table,
> -                    input_data->sbrec_datapath_binding_table,
> +    build_datapaths(input_data->synced_lses,
> +                    input_data->synced_lrs,
>                      &data->ls_datapaths,
>                      &data->lr_datapaths);
>      build_lb_datapaths(input_data->lbs, input_data->lbgrps,
> diff --git a/northd/northd.h b/northd/northd.h
> index 2b8f7fdee..484ea1be2 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -27,6 +27,7 @@
>  #include "ovs-thread.h"
>  #include "en-lr-stateful.h"
>  #include "vec.h"
> +#include "datapath-sync.h"
>  
>  struct northd_input {
>      /* Northbound table references */
> @@ -71,6 +72,10 @@ struct northd_input {
>      /* ACL ID inputs. */
>      const struct acl_id_data *acl_id_data;
>  
> +    /* Synced datapath inputs. */
> +    const struct ovn_synced_logical_switch_map *synced_lses;
> +    const struct ovn_synced_logical_router_map *synced_lrs;
> +
>      /* Indexes */
>      struct ovsdb_idl_index *sbrec_chassis_by_name;
>      struct ovsdb_idl_index *sbrec_chassis_by_hostname;
> @@ -445,14 +450,6 @@ ovn_datapath_is_stale(const struct ovn_datapath *od)
>  };
>  
>  /* Pipeline stages. */
> -
> -/* The two purposes for which ovn-northd uses OVN logical datapaths. */
> -enum ovn_datapath_type {
> -    DP_SWITCH,                  /* OVN logical switch. */
> -    DP_ROUTER,                  /* OVN logical router. */
> -    DP_MAX,
> -};
> -
>  /* Returns an "enum ovn_stage" built from the arguments.
>   *
>   * (It's better to use ovn_stage_build() for type-safety reasons, but inline
> @@ -977,8 +974,6 @@ lr_has_multiple_gw_ports(const struct ovn_datapath *od)
>      return vector_len(&od->l3dgw_ports) > 1 && !od->is_gw_router;
>  }
>  
> -uint32_t get_ovn_max_dp_key_local(bool _vxlan_mode, bool ic_mode);
> -
>  /* Returns true if the logical router port 'enabled' column is empty or
>   * set to true.  Otherwise, returns false. */
>  static inline bool
> -- 
> 2.49.0
> 
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> 
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to