On Fri, Jul 18, 2025 at 6:15 PM Mark Michelson via dev < ovs-dev@openvswitch.org> 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 have one nit down below. > v10 -> v11: > * Rebased. > * The data structure used by en-datapath-sync for synced datapaths is > now an hmap instead of an ovs_list. This is to facilitate lookups > that will be necessary when adding I-P to the node. > > v9 -> v10: > * Rebased. > * Lots of formatting updates (indentation changes, removal of > unnecessary blank lines, rearranging ENGINE_NODE declarations, etc.). > * Used designated initializers to initialize structures after > allocation. > * The unsynced datapath nodes write the old external_ids to southbound > datapath_bindings in order to maintain backwards compatiblity. > > v8 -> v9: > * Rebased. > * Changed names of files to be in alignment with style of other file > * names. > > v7 -> v8: > * Rebased. > * Like with v7, there are no functional changes, but the code had > to be updated to work with the new Datapath_Binding column types. > > v6 -> v7: > * Rebased. > * There are no functional changes, but the code has been adapted to > work with the new Datapath_Binding columns introduced in patch 1. > > v5 -> v6: > * Rebased. > * All xzalloc() calls in the patch have been changed to xmalloc(). > * Matching existing southbound datapaths to northbound unsynced > datapaths is more efficient due to being able to get the type > directly from the southbound datapath. > * Candidate synced datapaths use a vector instead of a list. > * Disabled logical routers are not passed to the en-datapath-sync node > now. This prevents southbound Datapath_Bindings from being created > for disabled logical routers, and it aligns with how the code worked > prior to this patch. > * Fixed a bug where the check for adding a requested tunnel key was > inverted from what it was supposed to be. This was causing spurious > warning messages in the northd logs, and it also was not preventing > the use of duplicate requested tunnel keys. > * This patch abandons the attempt to blindly pull in unsynced datapath > maps based on their positions in the engine node inputs array. Instead, > we now explicitly pull them in by input name and assign them to array > positions based on their datapath type. > > v4 -> v5: > * Rebased. > > v3 -> v4: > * Rebased. > > v2 -> v3: > * Rebased > * Fixed a typo in datapath_sync.h > > v1 -> v2: > * Many formatting fixes (added newlines, removed unnecessary local > variables, removed unnecessary parameter names, etc.). > * Clarified language in TODO.rst regarding the eventual removal of the > ovn_datapath structure. > * Use ovsdb_idl_row's type field to find the northbound type when > converting from generic synced datapath to logical switch or logical > router. > * Fixed several memory leaks. > * Switched to allocating the input_maps dynamically in en-datapath-sync > since sanitizers complain about stack-allocated variable-length > arrays. > * Changed name of function that deletes remaining candidate synced > datapaths to make it less confusing > (delete_candidates_with_no_tunnel_keys -> delete_candidates). > * Used engine_noop_handler instead of defining new stubs that return > true. > > Any review comments not addressed in this version should have been > addressed by my responses to earlier reviews. > --- > TODO.rst | 12 + > northd/automake.mk | 8 + > northd/datapath-sync.c | 90 +++++++ > northd/datapath-sync.h | 86 +++++++ > northd/en-datapath-logical-router.c | 185 ++++++++++++++ > northd/en-datapath-logical-router.h | 48 ++++ > northd/en-datapath-logical-switch.c | 182 ++++++++++++++ > northd/en-datapath-logical-switch.h | 47 ++++ > northd/en-datapath-sync.c | 320 ++++++++++++++++++++++++ > northd/en-datapath-sync.h | 27 +++ > northd/en-global-config.c | 11 + > northd/en-northd.c | 6 + > northd/inc-proc-northd.c | 40 +++ > northd/northd.c | 362 +++------------------------- > northd/northd.h | 15 +- > 15 files changed, 1105 insertions(+), 334 deletions(-) > create mode 100644 northd/datapath-sync.c > create mode 100644 northd/datapath-sync.h > create mode 100644 northd/en-datapath-logical-router.c > create mode 100644 northd/en-datapath-logical-router.h > create mode 100644 northd/en-datapath-logical-switch.c > create mode 100644 northd/en-datapath-logical-switch.h > create mode 100644 northd/en-datapath-sync.c > create mode 100644 northd/en-datapath-sync.h > > diff --git a/TODO.rst b/TODO.rst > index 85208bfe3..07dca71af 100644 > --- a/TODO.rst > +++ b/TODO.rst > @@ -153,3 +153,15 @@ OVN To-do List > monitoring conditions to update before we actually try to learn > routes. > Otherwise we could try to add duplicated Learned_Routes and the ovnsb > commit would fail. > + > +* Datapath sync nodes > + > + * Add incremental processing to the en-datapath-logical-switch, > + en-datapath-logical-router, en-datapath-sync, and possibly the > + en-datapath-synced-logical-router and > en-datapath-synced-logical-switch > + nodes. > + > + * Migrate data stored in the ovn\_datapath structure to > + ovn\_synced\_logical_router and ovn\_synced\_logical\_switch. This > will > + allow for the eventual removal of the ovn\_datapath structure from the > + codebase. > diff --git a/northd/automake.mk b/northd/automake.mk > index 9a7165529..45ca0337f 100644 > --- a/northd/automake.mk > +++ b/northd/automake.mk > @@ -3,11 +3,19 @@ bin_PROGRAMS += northd/ovn-northd > northd_ovn_northd_SOURCES = \ > northd/aging.c \ > northd/aging.h \ > + northd/datapath-sync.c \ > + northd/datapath-sync.h \ > northd/debug.c \ > northd/debug.h \ > northd/northd.c \ > northd/northd.h \ > northd/ovn-northd.c \ > + northd/en-datapath-logical-switch.c \ > + northd/en-datapath-logical-switch.h \ > + northd/en-datapath-logical-router.c \ > + northd/en-datapath-logical-router.h \ > + northd/en-datapath-sync.c \ > + northd/en-datapath-sync.h \ > northd/en-ecmp-nexthop.c \ > northd/en-ecmp-nexthop.h \ > northd/en-global-config.c \ > diff --git a/northd/datapath-sync.c b/northd/datapath-sync.c > new file mode 100644 > index 000000000..4916ed647 > --- /dev/null > +++ b/northd/datapath-sync.c > @@ -0,0 +1,90 @@ > +/* 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 "datapath-sync.h" > + > +static const char *ovn_datapath_strings[] = { > + [DP_SWITCH] = "logical-switch", > + [DP_ROUTER] = "logical-router", > + [DP_MAX] = "<invalid>", > +}; > + > +enum ovn_datapath_type > +ovn_datapath_type_from_string(const char *type_str) > +{ > + for (enum ovn_datapath_type i = DP_SWITCH; i < DP_MAX; i++) { > + if (!strcmp(type_str, ovn_datapath_strings[i])) { > + return i; > + } > + } > + > + return DP_MAX; > +} > + > +const char * > +ovn_datapath_type_to_string(enum ovn_datapath_type dp_type) > +{ > + if (dp_type > DP_MAX) { > + dp_type = DP_MAX; > + } > + return ovn_datapath_strings[dp_type]; > +} > + > +struct ovn_unsynced_datapath * > +ovn_unsynced_datapath_alloc(const char *name, enum ovn_datapath_type type, > + uint32_t requested_tunnel_key, > + const struct ovsdb_idl_row *nb_row) > +{ > + struct ovn_unsynced_datapath *dp = xmalloc(sizeof *dp); > + *dp = (struct ovn_unsynced_datapath) { > + .name = xstrdup(name), > + .type =type, nit: Missing space. > + .requested_tunnel_key = requested_tunnel_key, > + .nb_row = nb_row, > + .external_ids = SMAP_INITIALIZER(&dp->external_ids), > + }; > + > + return dp; > +} > + > +void > +ovn_unsynced_datapath_destroy(struct ovn_unsynced_datapath *dp) > +{ > + free(dp->name); > + smap_destroy(&dp->external_ids); > +} > + > +void > +ovn_unsynced_datapath_map_init(struct ovn_unsynced_datapath_map *map, > + enum ovn_datapath_type dp_type) > +{ > + *map = (struct ovn_unsynced_datapath_map) { > + .dps = HMAP_INITIALIZER(&map->dps), > + .dp_type = dp_type, > + }; > +} > + > +void > +ovn_unsynced_datapath_map_destroy(struct ovn_unsynced_datapath_map *map) > +{ > + struct ovn_unsynced_datapath *dp; > + HMAP_FOR_EACH_POP (dp, hmap_node, &map->dps) { > + ovn_unsynced_datapath_destroy(dp); > + free(dp); > + } > + hmap_destroy(&map->dps); > +} > diff --git a/northd/datapath-sync.h b/northd/datapath-sync.h > new file mode 100644 > index 000000000..246500c7b > --- /dev/null > +++ b/northd/datapath-sync.h > @@ -0,0 +1,86 @@ > +/* 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 DATAPATH_SYNC_H > +#define DATAPATH_SYNC_H 1 > + > +#include "openvswitch/hmap.h" > +#include "smap.h" > + > +/* Datapath syncing API. This file consists of utility functions > + * that can be used when syncing northbound datapath types (e.g. > + * Logical_Router and Logical_Switch) to southbound Datapath_Bindings. > + * > + * The basic flow of data is as such. > + * 1. A northbound type is converted into an ovn_unsynced_datapath. > + * All ovn_unsynced_datapaths are placed into an > ovn_unsynced_datapath_map. > + * 2. The en_datapath_sync node takes all of the maps in as input and > + * syncs them with southbound datapath bindings. This includes allocating > + * tunnel keys across all datapath types. The output of this node is > + * ovn_synced_datapaths, which contains a list of all synced datapaths. > + * 3. A northbound type-aware node then takes the ovn_synced_datapaths, > + * and decodes the generic synced datapaths back into a type-specific > + * version (e.g. ovn_synced_logical_router). Later nodes can then consume > + * these type-specific synced datapath types in order to perform > + * further processing. > + */ > + > +enum ovn_datapath_type { > + DP_SWITCH, > + DP_ROUTER, > + DP_MAX, > +}; > + > +enum ovn_datapath_type ovn_datapath_type_from_string(const char > *type_str); > +const char *ovn_datapath_type_to_string(enum ovn_datapath_type dp_type); > + > +/* Represents a datapath from the northbound database > + * that has not yet been synced with the southbound database. > + */ > +struct ovn_unsynced_datapath { > + struct hmap_node hmap_node; > + char *name; > + enum ovn_datapath_type type; > + uint32_t requested_tunnel_key; > + struct smap external_ids; > + const struct ovsdb_idl_row *nb_row; > +}; > + > +struct ovn_unsynced_datapath_map { > + /* ovn_unsynced_datapath */ > + struct hmap dps; > + enum ovn_datapath_type dp_type; > +}; > + > +struct ovn_synced_datapath { > + struct hmap_node hmap_node; > + const struct ovsdb_idl_row *nb_row; > + const struct sbrec_datapath_binding *sb_dp; > +}; > + > +struct ovn_synced_datapaths { > + struct hmap synced_dps; > +}; > + > +struct ovn_unsynced_datapath *ovn_unsynced_datapath_alloc( > + const char *name, enum ovn_datapath_type type, > + uint32_t requested_tunnel_key, const struct ovsdb_idl_row *nb_row); > +void ovn_unsynced_datapath_destroy(struct ovn_unsynced_datapath *); > + > +void ovn_unsynced_datapath_map_init(struct ovn_unsynced_datapath_map *, > + enum ovn_datapath_type); > +void ovn_unsynced_datapath_map_destroy(struct ovn_unsynced_datapath_map > *); > + > +#endif /* DATAPATH_SYNC_H */ > diff --git a/northd/en-datapath-logical-router.c > b/northd/en-datapath-logical-router.c > new file mode 100644 > index 000000000..5160c9c70 > --- /dev/null > +++ b/northd/en-datapath-logical-router.c > @@ -0,0 +1,185 @@ > +/* > + * 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 "ovn-nb-idl.h" > +#include "aging.h" > +#include "datapath-sync.h" > +#include "en-datapath-logical-router.h" > +#include "ovn-util.h" > + > +void * > +en_datapath_logical_router_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_ROUTER); > + return map; > +} > + > +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 = > + ovn_unsynced_datapath_alloc(nbr->name, DP_ROUTER, > + smap_get_int(&nbr->options, > + "requested-tnl-key", > 0), > + &nbr->header_); > + > + 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); > + } > + > + /* For backwards-compatibility, also store the NB UUID in > + * external-ids:logical-router. This is useful if ovn-controller > + * has not updated and expects this to be where to find the > + * UUID. > + */ > + smap_add_format(&dp->external_ids, "logical-router", UUID_FMT, > + UUID_ARGS(&nbr->header_.uuid)); > + > + hmap_insert(&map->dps, &dp->hmap_node, > uuid_hash(&nbr->header_.uuid)); > + } > + > + return EN_UPDATED; > +} > + > +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) > +{ > + *router_map = (struct ovn_synced_logical_router_map) { > + .synced_routers = HMAP_INITIALIZER(&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; > + HMAP_FOR_EACH (sdp, hmap_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 = (struct ovn_synced_logical_router) { > + .nb = CONTAINER_OF(sdp->nb_row, struct nbrec_logical_router, > + header_), > + .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..31e70fbf5 > --- /dev/null > +++ b/northd/en-datapath-logical-router.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_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..23fa9725f > --- /dev/null > +++ b/northd/en-datapath-logical-switch.c > @@ -0,0 +1,182 @@ > +/* > + * 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 > + = ovn_unsynced_datapath_alloc(nbs->name, DP_SWITCH, > + requested_tunnel_key, > &nbs->header_); > + > + 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); > + } > + > + /* For backwards-compatibility, also store the NB UUID in > + * external-ids:logical-switch. This is useful if ovn-controller > + * has not updated and expects this to be where to find the > + * UUID. > + */ > + smap_add_format(&dp->external_ids, "logical-switch", UUID_FMT, > + UUID_ARGS(&nbs->header_.uuid)); > + > + 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) > +{ > + *switch_map = (struct ovn_synced_logical_switch_map) { > + .synced_switches = HMAP_INITIALIZER(&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; > + HMAP_FOR_EACH (sdp, hmap_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 = (struct ovn_synced_logical_switch) { > + .nb = CONTAINER_OF(sdp->nb_row, struct nbrec_logical_switch, > + header_), > + .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..1bd5fea83 > --- /dev/null > +++ b/northd/en-datapath-logical-switch.h > @@ -0,0 +1,47 @@ > +/* > + * 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..ec25b0b2c > --- /dev/null > +++ b/northd/en-datapath-sync.c > @@ -0,0 +1,320 @@ > +/* > + * 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); > + *synced_datapaths = (struct ovn_synced_datapaths) { > + .synced_dps = HMAP_INITIALIZER(&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; > + const char *type; > + struct uuid nb_uuid; > + > + if (!datapath_get_nb_uuid_and_type(sb_dp, &nb_uuid, &type)) { > + return NULL; > + } > + > + dp_type = ovn_datapath_type_from_string(type); > + if (dp_type == DP_MAX) { > + /* This record was not created by us. It's invalid. */ > + return NULL; > + } > + > + uint32_t hash = uuid_hash(&nb_uuid); > + struct ovn_unsynced_datapath *dp; > + HMAP_FOR_EACH_WITH_HASH (dp, hmap_node, hash, &maps[dp_type]->dps) { > + if (uuid_equals(&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 = (struct ovn_synced_datapath) { > + .sb_dp = sb_dp, > + .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; > + HMAP_FOR_EACH_POP (sdp, hmap_node, &synced_datapaths->synced_dps) { > + free(sdp); > + } > +} > + > +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 = > find_unsynced_datapath(input_maps, > + sb_dp); > + 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 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); > + hmap_insert(&synced_datapaths->synced_dps, > &candidate->sdp->hmap_node, > + uuid_hash(candidate->sdp->sb_dp->nb_uuid)); > + 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)) { > + hmap_insert(&synced_datapaths->synced_dps, > + &candidate->sdp->hmap_node, > + uuid_hash(candidate->sdp->sb_dp->nb_uuid)); > + 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); > + hmap_insert(&synced_datapaths->synced_dps, > &candidate->sdp->hmap_node, > + uuid_hash(candidate->sdp->sb_dp->nb_uuid)); > + 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; > + > + HMAP_FOR_EACH_POP (sdp, hmap_node, &synced_datapaths->synced_dps) { > + free(sdp); > + } > + hmap_destroy(&synced_datapaths->synced_dps); > +} > 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 e7d6a7e66..76046c265 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 7be01c777..76e35c97c 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..854e7add3 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_sync); > +static ENGINE_NODE(datapath_synced_logical_router); > +static ENGINE_NODE(datapath_synced_logical_switch); > > 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 f39ff77ca..00d1434cb 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; > } > > @@ -748,84 +752,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, > @@ -877,172 +803,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) { > - struct uuid key; > - if (!datapath_get_nb_uuid(sb, &key)) { > - 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, &key)) { > - 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) > { > @@ -1062,88 +822,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 +19182,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 2b2fedec5..05591c07e 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 > @@ -976,8 +973,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 > > Other than that it looks good. Acked-by: Ales Musil <amu...@redhat.com> _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev