On Fri, Dec 13, 2024 at 2:54 PM Eelco Chaudron <echau...@redhat.com> wrote: > > > > On 10 Dec 2024, at 23:18, Frode Nordahl wrote: > > > In preparation for adding support for parsing attributes such as > > RTA_VIA and RTA_MULTIPATH, we need dynamic storage for route > > nexthops. > > > > For the common case of one nexthop we store nexthop information in > > a static variable to avoid unnecessary memory allocations. > > > > This patch makes this change in isolation so that it is easier to > > review subsequent patches. > > > > Signed-off-by: Frode Nordahl <fnord...@ubuntu.com> > > See some comments below. > > > --- > > lib/route-table.c | 63 ++++++++++++++++++++++++++++++++++++++--------- > > 1 file changed, 51 insertions(+), 12 deletions(-) > > > > diff --git a/lib/route-table.c b/lib/route-table.c > > index 33e0df2b8..fd26ac688 100644 > > --- a/lib/route-table.c > > +++ b/lib/route-table.c > > @@ -32,6 +32,7 @@ > > #include "netlink.h" > > #include "netlink-notifier.h" > > #include "netlink-socket.h" > > +#include "openvswitch/list.h" > > #include "openvswitch/ofpbuf.h" > > #include "ovs-router.h" > > #include "packets.h" > > @@ -47,7 +48,16 @@ VLOG_DEFINE_THIS_MODULE(route_table); > > > > COVERAGE_DEFINE(route_table_dump); > > > > +struct route_data_nexthop { > > + struct ovs_list nexthop_node; > > + > > + struct in6_addr addr; > > + char ifname[IFNAMSIZ]; /* Interface name. */ > > +}; > > + > > struct route_data { > > + struct ovs_list nexthops; > > + > > /* Copied from struct rtmsg. */ > > unsigned char rtm_dst_len; > > unsigned char rtm_protocol; > > @@ -56,8 +66,6 @@ struct route_data { > > /* Extracted from Netlink attributes. */ > > struct in6_addr rta_dst; /* 0 if missing. */ > > struct in6_addr rta_prefsrc; /* 0 if missing. */ > > - struct in6_addr rta_gw; > > - char ifname[IFNAMSIZ]; /* Interface name. */ > > uint32_t mark; > > uint32_t rta_table_id; /* 0 if missing. */ > > uint32_t rta_priority; /* 0 if missing. */ > > @@ -80,6 +88,7 @@ static uint64_t rt_change_seq; > > > > static struct nln *nln = NULL; > > static struct route_table_msg rtmsg; > > +static struct route_data_nexthop rdnh_single; > > static struct nln_notifier *route_notifier = NULL; > > static struct nln_notifier *route6_notifier = NULL; > > static struct nln_notifier *name_notifier = NULL; > > @@ -89,12 +98,24 @@ static bool route_table_valid = false; > > static void route_table_reset(void); > > static void route_table_handle_msg(const struct route_table_msg *); > > static int route_table_parse(struct ofpbuf *, void *change); > > -static void route_table_change(const struct route_table_msg *, void *); > > +static void route_table_change(struct route_table_msg *, void *); > > static void route_map_clear(void); > > > > static void name_table_init(void); > > static void name_table_change(const struct rtnetlink_change *, void *); > > > > +static void > > +route_data_destroy(struct route_data *rd) > > +{ > > + struct route_data_nexthop *rdnh; > > + > > + LIST_FOR_EACH_POP (rdnh, nexthop_node, &rd->nexthops) { > > + if (rdnh && rdnh != &rdnh_single) { > > + free(rdnh); > > + } > > + } > > +} > > + > > uint64_t > > route_table_get_change_seq(void) > > { > > @@ -190,6 +211,7 @@ route_table_dump_one_table(unsigned char id) > > filtered = false; > > } > > route_table_handle_msg(&msg); > > + route_data_destroy(&msg.rd); > > If we return an error and have parsed some route entries (patch 6), memory > will be lost.
Thanks, the intention was to make sure route_table_parse() would free any allocated memory before error return, as there is currently no way for the caller to discern between error modes. But it may be I have missed a scenario, will revisit. > > } > > } > > ofpbuf_uninit(&buf); > > @@ -230,6 +252,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, > > const struct rtmsg *rtm, void *change_) > > { > > struct route_table_msg *change = change_; > > + struct route_data_nexthop *rdnh = NULL; > > bool parsed, ipv4 = false; > > > > static const struct nl_policy policy[] = { > > @@ -270,6 +293,9 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, > > int rta_oif; /* Output interface index. */ > > > > memset(change, 0, sizeof *change); > > + ovs_list_init(&change->rd.nexthops); > > + memset(&rdnh_single, 0, sizeof *rdnh); > Having a global variable does not make this thread-safe. We should have a > thread-safe solution for this. I assume you want to avoid allocating memory > for the single next-hop case (which is the most common one). > > I would suggest doing something like this (with some detailed comments, i.e. > that the primary entry is private and should not be touched, etc. etc.): > > struct route_data { > > + /* LONG COMMENT EXPLAINING ALL OF THE NEXT-HOP STUFF. */ > struct ovs_list nexthops; > + struct route_data_nexthop _primary_next_hop; > > /* Copied from struct rtmsg. */ > unsigned char rtm_dst_len; > unsigned char rtm_protocol; > bool local; > > /* Extracted from Netlink attributes. */ > struct in6_addr rta_dst; /* 0 if missing. */ > struct in6_addr rta_prefsrc; /* 0 if missing. */ > uint32_t mark; > uint32_t rta_table_id; /* 0 if missing. */ > uint32_t rta_priority; /* 0 if missing. */ > }; Ack, and thank you for the suggestion. The existing implementation is also not thread safe as it uses a static variable for storage of struct route_table_msg that holds struct route_data, so I only continued the same mode of operation. But if an external consumer wants to make use of threads, I guess it would be much better if we were to allow that. Storing this in a different way and adding a once per thread initializer would not be that hard, so I'll make an attempt at doing that as part of this series. -- Frode Nordahl > > + rdnh = &rdnh_single; > > change->relevant = true; > > > > if (rtm->rtm_scope == RT_SCOPE_NOWHERE) { > > @@ -293,7 +319,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, > > if (attrs[RTA_OIF]) { > > rta_oif = nl_attr_get_u32(attrs[RTA_OIF]); > > > > - if (!if_indextoname(rta_oif, change->rd.ifname)) { > > + if (!if_indextoname(rta_oif, rdnh->ifname)) { > > int error = errno; > > > > VLOG_DBG_RL(&rl, "Could not find interface name[%u]: %s", > > @@ -301,7 +327,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, > > if (error == ENXIO) { > > change->relevant = false; > > } else { > > - return 0; > > + goto error_out; > > } > > } > > } > > @@ -331,9 +357,9 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, > > if (ipv4) { > > ovs_be32 gw; > > gw = nl_attr_get_be32(attrs[RTA_GATEWAY]); > > - in6_addr_set_mapped_ipv4(&change->rd.rta_gw, gw); > > + in6_addr_set_mapped_ipv4(&rdnh->addr, gw); > > } else { > > - change->rd.rta_gw = > > nl_attr_get_in6_addr(attrs[RTA_GATEWAY]); > > + rdnh->addr = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]); > > } > > } > > if (attrs[RTA_MARK]) { > > @@ -342,13 +368,20 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, > > if (attrs[RTA_PRIORITY]) { > > change->rd.rta_priority = nl_attr_get_u32(attrs[RTA_PRIORITY]); > > } > > + ovs_list_insert(&change->rd.nexthops, &rdnh->nexthop_node); > > I think we need to add some proper error handling if there is no RTA_VIA or > RTA_GATEWAY, we should error out as the message is not valid. > > > } else { > > VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message"); > > - return 0; > > + goto error_out; > > } > > > > /* Success. */ > > return ipv4 ? RTNLGRP_IPV4_ROUTE : RTNLGRP_IPV6_ROUTE; > > + > > +error_out: > > + if (rdnh && rdnh != &rdnh_single) { > > + free (rdnh); > > + } > > + return 0; > > } > > > > static int > > @@ -374,7 +407,7 @@ route_table_standard_table(uint32_t table_id) > > } > > > > static void > > -route_table_change(const struct route_table_msg *change OVS_UNUSED, > > +route_table_change(struct route_table_msg *change, > > void *aux OVS_UNUSED) > > This will now fit on one line. > > > { > > if (!change > > @@ -382,6 +415,9 @@ route_table_change(const struct route_table_msg *change > > OVS_UNUSED, > > && route_table_standard_table(change->rd.rta_table_id))) { > > route_table_valid = false; > > } > > + if (change && !ovs_list_is_empty(&change->rd.nexthops)) { > > Maybe this ovs_list_is_empty should be skipped as these details should be > dealt with in route_data_destroy(). > > > + route_data_destroy(&change->rd); > > + } > > } > > > > static void > > @@ -389,10 +425,13 @@ route_table_handle_msg(const struct route_table_msg > > *change) > > { > > if (change->relevant && change->nlmsg_type == RTM_NEWROUTE) { > > const struct route_data *rd = &change->rd; > > + const struct route_data_nexthop *rdnh; > > > > - ovs_router_insert(rd->mark, &rd->rta_dst, rd->rtm_dst_len, > > - rd->local, rd->ifname, &rd->rta_gw, > > - &rd->rta_prefsrc); > > + LIST_FOR_EACH (rdnh, nexthop_node, &rd->nexthops) { > > + ovs_router_insert(rd->mark, &rd->rta_dst, rd->rtm_dst_len, > > + rd->local, rdnh->ifname, &rdnh->addr, > > + &rd->rta_prefsrc); > > + } > > } > > } > > > > -- > > 2.45.2 > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev