On Tue, 2025-02-25 at 00:01 +0100, Dumitru Ceara wrote: > Change the en_dynamic_routes I-P node to compute a map of 'advertised > routes' (struct ar_entry) instead of parsed routes. Like that we can > avoid having to re-parse NAT/LB IPs when advertising them. > > Fixes: cd4ad2f56179 ("northd: Redistribution of NAT/LB routes.") > Signed-off-by: Dumitru Ceara <dce...@redhat.com>
Hi Dumitru, Overall, this looks good to me. I have some nits/suggestions that I'll leave up to you to consider. Otherwise consider this acked by me. Now that the dynamic routes don't use "struct parsed_route", we can probably remove the TODO item that calls for splitting parsed_routes [0]. > --- > northd/en-advertised-route-sync.c | 376 +++++++++++++++++----------- > -- > northd/northd.h | 3 +- > 2 files changed, 210 insertions(+), 169 deletions(-) > > diff --git a/northd/en-advertised-route-sync.c b/northd/en- > advertised-route-sync.c > index 486bcc2fd2..ebaed08b69 100644 > --- a/northd/en-advertised-route-sync.c > +++ b/northd/en-advertised-route-sync.c > @@ -27,6 +27,102 @@ > #include "openvswitch/hmap.h" > #include "ovn-util.h" > > +struct ar_entry { > + struct hmap_node hmap_node; > + > + const struct ovn_datapath *od; /* Datapath the route is > + * advertised on. */ > + const struct ovn_port *op; /* Port the route is > advertised > + * on. */ > + char *ip_prefix; > + const struct ovn_port *tracked_port; /* If set, the port whose > chassis > + * advertises this route > with a > + * higher priority. */ > + enum route_source source; > +}; > + > +/* Add a new entries to the to-be-advertised routes. > + * Takes ownership of ip_prefix. */ > +static struct ar_entry * > +ar_entry_add_nocopy(struct hmap *routes, const struct ovn_datapath > *od, > + const struct ovn_port *op, char *ip_prefix, > + const struct ovn_port *tracked_port, > + enum route_source source) > +{ With the "copy version" of this function being more popular, and the "no_copy" used only once. Would it make sense to keep only the "ar_entry_add" function that always copies the ip_prefix, and in the one case where we want "no_copy" just freeing the ip_prefix after calling regular "ar_entry_add"? > + struct ar_entry *route_e = xzalloc(sizeof *route_e); > + > + route_e->od = od; > + route_e->op = op; > + route_e->ip_prefix = ip_prefix; > + route_e->tracked_port = tracked_port; > + route_e->source = source; > + uint32_t hash = uuid_hash(&od->sb->header_.uuid); > + hash = hash_string(op->sb->logical_port, hash); > + hash = hash_string(ip_prefix, hash); > + hmap_insert(routes, &route_e->hmap_node, hash); > + > + return route_e; > +} > + > +/* Add a new entries to the to-be-advertised routes. > + * Makes a copy of ip_prefix. */ > +static struct ar_entry * > +ar_entry_add(struct hmap *routes, const struct ovn_datapath *od, > + const struct ovn_port *op, const char *ip_prefix, > + const struct ovn_port *tracked_port, > + enum route_source source) > +{ > + return ar_entry_add_nocopy(routes, od, op, xstrdup(ip_prefix), > + tracked_port, source); > +} > + > +static struct ar_entry * > +ar_entry_find(struct hmap *route_map, > + const struct sbrec_datapath_binding *sb_db, > + const struct sbrec_port_binding *logical_port, > + const char *ip_prefix, > + const struct sbrec_port_binding *tracked_port) > +{ > + struct ar_entry *route_e; > + uint32_t hash; > + > + hash = uuid_hash(&sb_db->header_.uuid); > + hash = hash_string(logical_port->logical_port, hash); > + hash = hash_string(ip_prefix, hash); > + > + HMAP_FOR_EACH_WITH_HASH (route_e, hmap_node, hash, route_map) { > + if (!uuid_equals(&sb_db->header_.uuid, > + &route_e->od->sb->header_.uuid)) { > + continue; > + } > + if (!uuid_equals(&logical_port->header_.uuid, > + &route_e->op->sb->header_.uuid)) { > + continue; > + } > + if (strcmp(ip_prefix, route_e->ip_prefix)) { > + continue; > + } > + > + if (tracked_port) { > + if (!route_e->tracked_port || > + tracked_port != route_e->tracked_port->sb) { > + continue; > + } > + } > + > + return route_e; > + } > + > + return NULL; > +} > + > +static void > +ar_entry_free(struct ar_entry *route_e) > +{ > + free(route_e->ip_prefix); > + free(route_e); > +} > + > static void > advertised_route_table_sync( > struct ovsdb_idl_txn *ovnsb_txn, > @@ -158,7 +254,7 @@ en_advertised_route_sync_run(struct engine_node > *node, void *data OVS_UNUSED) > sbrec_advertised_route_table, > &lr_stateful_data->table, > &routes_data->parsed_routes, > - &dynamic_routes_data->parsed_routes, > + &dynamic_routes_data->routes, > routes_sync_data); > > stopwatch_stop(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME, > time_msec()); > @@ -184,18 +280,18 @@ build_nat_parsed_route_for_port(const struct > ovn_port *advertising_op, > continue; > } > > - int plen = nat_entry_is_v6(nat) ? 128 : 32; > - struct in6_addr prefix; > - ip46_parse(nat->nb->external_ip, &prefix); > - > const struct ovn_port *tracked_port = > nat->is_distributed > ? ovn_port_find(ls_ports, nat->nb->logical_port) > : nat->l3dgw_port; > - parsed_route_add(advertising_od, NULL, &prefix, plen, false, > - nat->nb->external_ip, advertising_op, 0, > false, > - false, NULL, ROUTE_SOURCE_NAT, &nat->nb- > >header_, > - tracked_port, routes); > + > + if (!ar_entry_find(routes, advertising_od->sb, > advertising_op->sb, > + nat->nb->external_ip, > + tracked_port ? tracked_port->sb : NULL)) > { Is the if clause necessary here? With "routes" being hashmap of ar_routes that uses "advertising_od", "advertising_op" and "external_ip" as a key, isn't that enough to prevent duplicates? > + ar_entry_add(routes, advertising_od, advertising_op, > + nat->nb->external_ip, tracked_port, > + ROUTE_SOURCE_NAT); > + } > } > } > > @@ -294,20 +390,12 @@ build_lb_parsed_route_for_port(const struct > ovn_port *advertising_op, > > const char *ip_address; > SSET_FOR_EACH (ip_address, &lb_ips->ips_v4) { > - struct in6_addr prefix; > - ip46_parse(ip_address, &prefix); > - parsed_route_add(advertising_od, NULL, &prefix, 32, false, > - ip_address, advertising_op, 0, false, > false, > - NULL, ROUTE_SOURCE_LB, &advertising_op- > >nbrp->header_, > - tracked_port, routes); > + ar_entry_add(routes, advertising_od, advertising_op, > + ip_address, tracked_port, ROUTE_SOURCE_LB); > } > SSET_FOR_EACH (ip_address, &lb_ips->ips_v6) { > - struct in6_addr prefix; > - ip46_parse(ip_address, &prefix); > - parsed_route_add(advertising_od, NULL, &prefix, 128, false, > - ip_address, advertising_op, 0, false, > false, > - NULL, ROUTE_SOURCE_LB, &advertising_op- > >nbrp->header_, > - tracked_port, routes); > + ar_entry_add(routes, advertising_od, advertising_op, > + ip_address, tracked_port, ROUTE_SOURCE_LB); > } > } > > @@ -402,7 +490,7 @@ en_dynamic_routes_init(struct engine_node *node > OVS_UNUSED, > { > struct dynamic_routes_data *data = xmalloc(sizeof *data); > *data = (struct dynamic_routes_data) { > - .parsed_routes = HMAP_INITIALIZER(&data->parsed_routes), > + .routes = HMAP_INITIALIZER(&data->routes), > }; > > return data; > @@ -411,9 +499,9 @@ en_dynamic_routes_init(struct engine_node *node > OVS_UNUSED, > static void > en_dynamic_routes_clear(struct dynamic_routes_data *data) > { > - struct parsed_route *r; > - HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) { > - parsed_route_free(r); > + struct ar_entry *ar; > + HMAP_FOR_EACH_POP (ar, hmap_node, &data->routes) { > + ar_entry_free(ar); > } > } > void > @@ -422,7 +510,7 @@ en_dynamic_routes_cleanup(void *data_) > struct dynamic_routes_data *data = data_; > > en_dynamic_routes_clear(data); > - hmap_destroy(&data->parsed_routes); > + hmap_destroy(&data->routes); > } > > void > @@ -447,100 +535,20 @@ en_dynamic_routes_run(struct engine_node > *node, void *data) > } > build_nat_parsed_routes(od, lr_stateful_rec->lrnat_rec, > &northd_data->ls_ports, > - &dynamic_routes_data- > >parsed_routes); > + &dynamic_routes_data->routes); > build_nat_connected_parsed_routes(od, &lr_stateful_data- > >table, > &northd_data->ls_ports, > - &dynamic_routes_data- > >parsed_routes); > + &dynamic_routes_data- > >routes); > > build_lb_parsed_routes(od, lr_stateful_rec->lb_ips, > - &dynamic_routes_data->parsed_routes); > + &dynamic_routes_data->routes); > build_lb_connected_parsed_routes(od, &lr_stateful_data- > >table, > - &dynamic_routes_data- > >parsed_routes); > + &dynamic_routes_data- > >routes); > } > stopwatch_stop(DYNAMIC_ROUTES_RUN_STOPWATCH_NAME, time_msec()); > engine_set_node_state(node, EN_UPDATED); > } > > -struct ar_entry { > - struct hmap_node hmap_node; > - > - const struct ovn_datapath *od; /* Datapath the route is > - * advertised on. */ > - const struct ovn_port *op; /* Port the route is > advertised > - * on. */ > - char *ip_prefix; > - const struct ovn_port *tracked_port; /* If set, the port whose > chassis > - * advertises this route > with a > - * higher priority. */ > -}; > - > -/* Add a new entries to the to-be-advertised routes. > - * Takes ownership of ip_prefix. */ > -static struct ar_entry * > -ar_entry_add(struct hmap *routes, const struct ovn_datapath *od, > - const struct ovn_port *op, char *ip_prefix, > - const struct ovn_port *tracked_port) > -{ > - struct ar_entry *route_e = xzalloc(sizeof *route_e); > - > - route_e->od = od; > - route_e->op = op; > - route_e->ip_prefix = ip_prefix; > - route_e->tracked_port = tracked_port; > - uint32_t hash = uuid_hash(&od->sb->header_.uuid); > - hash = hash_string(op->sb->logical_port, hash); > - hash = hash_string(ip_prefix, hash); > - hmap_insert(routes, &route_e->hmap_node, hash); > - > - return route_e; > -} > - > -static struct ar_entry * > -ar_entry_find(struct hmap *route_map, > - const struct sbrec_datapath_binding *sb_db, > - const struct sbrec_port_binding *logical_port, > - const char *ip_prefix, > - const struct sbrec_port_binding *tracked_port) > -{ > - struct ar_entry *route_e; > - uint32_t hash; > - > - hash = uuid_hash(&sb_db->header_.uuid); > - hash = hash_string(logical_port->logical_port, hash); > - hash = hash_string(ip_prefix, hash); > - > - HMAP_FOR_EACH_WITH_HASH (route_e, hmap_node, hash, route_map) { > - if (!uuid_equals(&sb_db->header_.uuid, > - &route_e->od->sb->header_.uuid)) { > - continue; > - } > - if (!uuid_equals(&logical_port->header_.uuid, > - &route_e->op->sb->header_.uuid)) { > - continue; > - } > - if (strcmp(ip_prefix, route_e->ip_prefix)) { > - continue; > - } > - > - if (tracked_port) { > - if (!route_e->tracked_port || > - tracked_port != route_e->tracked_port->sb) { > - continue; > - } > - } > - return route_e; > - } > - > - return NULL; > -} > - > -static void > -ar_entry_free(struct ar_entry *route_e) > -{ > - free(route_e->ip_prefix); > - free(route_e); > -} > - > static void > publish_lport_addresses(struct hmap *sync_routes, > const struct ovn_datapath *od, > @@ -550,16 +558,16 @@ publish_lport_addresses(struct hmap > *sync_routes, > { > for (size_t i = 0; i < addresses->n_ipv4_addrs; i++) { > const struct ipv4_netaddr *addr = &addresses->ipv4_addrs[i]; > - ar_entry_add(sync_routes, od, logical_port, xstrdup(addr- > >addr_s), > - tracking_port); > + ar_entry_add(sync_routes, od, logical_port, addr->addr_s, > + tracking_port, ROUTE_SOURCE_CONNECTED); > } > for (size_t i = 0; i < addresses->n_ipv6_addrs; i++) { > if (in6_is_lla(&addresses->ipv6_addrs[i].network)) { > continue; > } > const struct ipv6_netaddr *addr = &addresses->ipv6_addrs[i]; > - ar_entry_add(sync_routes, od, logical_port, xstrdup(addr- > >addr_s), > - tracking_port); > + ar_entry_add(sync_routes, od, logical_port, addr->addr_s, > + tracking_port, ROUTE_SOURCE_CONNECTED); > } > } > > @@ -640,78 +648,85 @@ publish_host_routes(struct hmap *sync_routes, > } > } > > -static void > -advertised_route_table_sync_route_add( > - const struct lr_stateful_table *lr_stateful_table, > - struct advertised_route_sync_data *data, > - struct uuidset *host_route_lrps, > - struct hmap *sync_routes, > - const struct parsed_route *route) > +static bool > +should_advertise_route(const struct lr_stateful_table > *lr_stateful_table, > + struct advertised_route_sync_data *data, > + struct uuidset *host_route_lrps, > + struct hmap *sync_routes, > + const struct ovn_datapath *advertising_od, > + const struct ovn_port *advertising_op, > + const struct ovn_port *tracked_op, > + enum route_source source) > { > - if (route->is_discard_route) { > - return; > - } > - if (prefix_is_link_local(&route->prefix, route->plen)) { > - return; > - } > - if (!route->od->dynamic_routing) { > - return; > + if (!advertising_od->dynamic_routing) { > + return false; > } > > enum dynamic_routing_redistribute_mode drr = > - route->out_port->dynamic_routing_redistribute; > - if (route->source == ROUTE_SOURCE_CONNECTED) { > + advertising_op->dynamic_routing_redistribute; > + > + switch (source) { > + case ROUTE_SOURCE_CONNECTED: > if (!drr_mode_CONNECTED_is_set(drr)) { > - return; > + return false; > } > + > /* If we advertise host routes, we only need to do so once > per > * LRP. */ > - const struct uuid *lrp_uuid = &route->out_port->nbrp- > >header_.uuid; > - if (drr_mode_CONNECTED_AS_HOST_is_set(drr) && > - !uuidset_contains(host_route_lrps, lrp_uuid)) { > + const struct uuid *lrp_uuid = &advertising_op->nbrp- > >header_.uuid; > + if (drr_mode_CONNECTED_AS_HOST_is_set(drr)) { > + if (uuidset_contains(host_route_lrps, lrp_uuid)) { > + return false; > + } > uuidset_insert(host_route_lrps, lrp_uuid); > publish_host_routes(sync_routes, lr_stateful_table, > - route->od, route->out_port, > + advertising_od, advertising_op, > data); > - return; > + return false; > } > - } > - if (route->source == ROUTE_SOURCE_STATIC && > !drr_mode_STATIC_is_set(drr)) { > - return; > - } > - if (route->source == ROUTE_SOURCE_NAT) { > + return true; > + > + case ROUTE_SOURCE_STATIC: > + return drr_mode_STATIC_is_set(drr); > + > + case ROUTE_SOURCE_NAT: > if (!drr_mode_NAT_is_set(drr)) { > - return; > + return false; > } > + > /* If NAT route tracks port on a different DP than the one > that > * advertises the route, we need to watch for changes on > that DP as > * well. */ > - if (route->tracked_port && route->tracked_port->od != route- > >od) { > - if (route->tracked_port->od->nbr) { > + if (tracked_op && tracked_op->od != advertising_od) { > + if (tracked_op->od->nbr) { > uuidset_insert(&data->nb_lr, > - &route->tracked_port->od->nbr- > >header_.uuid); > - } else if (route->tracked_port->od->nbs) { > + &tracked_op->od->nbr->header_.uuid); > + } else if (tracked_op->od->nbs) { > uuidset_insert(&data->nb_ls, > - &route->tracked_port->od->nbs- > >header_.uuid); > + &tracked_op->od->nbs->header_.uuid); > } > } > - } > - if (route->source == ROUTE_SOURCE_LB) { > + return true; > + > + case ROUTE_SOURCE_LB: > if (!drr_mode_LB_is_set(drr)) { > - return; > + return false; > } > + > /* If LB route tracks port on a different DP than the one > that > * advertises the route, we need to watch for changes on > that DP as > * well. */ > - if (route->tracked_port && route->tracked_port->od != route- > >od) { > + if (tracked_op && tracked_op->od != advertising_od) { > uuidset_insert(&data->nb_lr, > - &route->tracked_port->od->nbr- > >header_.uuid); > + &tracked_op->od->nbr->header_.uuid); > } > + return true; > + > + case ROUTE_SOURCE_LEARNED: > + return true; > } > > - char *ip_prefix = normalize_v46_prefix(&route->prefix, route- > >plen); > - ar_entry_add(sync_routes, route->od, route->out_port, ip_prefix, > - route->tracked_port); > + OVS_NOT_REACHED(); > } > > static void > @@ -725,23 +740,48 @@ advertised_route_table_sync( > { > struct hmap sync_routes = HMAP_INITIALIZER(&sync_routes); > struct uuidset host_route_lrps = > UUIDSET_INITIALIZER(&host_route_lrps); > - const struct parsed_route *route; > - > - struct ar_entry *route_e; > > /* First build the set of non-dynamic routes that need sync-ing. > */ > + const struct parsed_route *route; > HMAP_FOR_EACH (route, key_node, routes) { > - advertised_route_table_sync_route_add(lr_stateful_table, > - data, > &host_route_lrps, > - &sync_routes, > - route); > + if (route->is_discard_route) { > + continue; > + } > + > + if (prefix_is_link_local(&route->prefix, route->plen)) { > + continue; > + } > + > + if (!should_advertise_route(lr_stateful_table, > + data, &host_route_lrps, > + &sync_routes, > + route->od, route->out_port, > + route->tracked_port, > + route->source)) { > + continue; > + } > + > + ar_entry_add_nocopy(&sync_routes, route->od, route- > >out_port, > + normalize_v46_prefix(&route->prefix, > route->plen), > + route->tracked_port, > + route->source); > } > + > /* Then add the set of dynamic routes that need sync-ing. */ > - HMAP_FOR_EACH (route, key_node, dynamic_routes) { > - advertised_route_table_sync_route_add(lr_stateful_table, > - data, > &host_route_lrps, > - &sync_routes, > - route); > + struct ar_entry *route_e; > + HMAP_FOR_EACH (route_e, hmap_node, dynamic_routes) { > + if (!should_advertise_route(lr_stateful_table, > + data, &host_route_lrps, > + &sync_routes, > + route_e->od, route_e->op, > + route_e->tracked_port, > + route_e->source)) { > + continue; > + } > + > + ar_entry_add(&sync_routes, route_e->od, route_e->op, > + route_e->ip_prefix, route_e->tracked_port, > + route_e->source); With "dynamic_routes" and "sync_routes" both being hmaps of ar_entry structs, would it make sense to pop the "ar_entry" item from dynamic_routes and directly insert it into the "sync_routes"? Instead of sending individual route_e values through the ar_entry_add, to basically create a copy of "route_e". Thanks a lot, Martin. [0]https://github.com/ovn-org/ovn/blob/fe26d5b53f5c1f939c8e03e0677e5268a1f7432d/TODO.rst?plain=1#L163-L168 > } > uuidset_destroy(&host_route_lrps); > > diff --git a/northd/northd.h b/northd/northd.h > index 12fccd7751..c221365e62 100644 > --- a/northd/northd.h > +++ b/northd/northd.h > @@ -192,7 +192,8 @@ struct routes_data { > }; > > struct dynamic_routes_data { > - struct hmap parsed_routes; /* Stores struct parsed_route. */ > + struct hmap routes; /* Stores struct ar_entry, one for each > + * dynamic route. */ > }; > > struct route_policies_data { _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev