On Mon, Dec 16, 2024 at 9:09 AM Frode Nordahl <fnord...@ubuntu.com> wrote: > > 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.
Looking over this again, the existing static storage is only used for the netlink-notifier callback, which I think makes sense to keep as-is. I think I was fooled by the variable naming though, so I'll propose something that makes it more clear. External consumers are expected to set up their own netlink-notifier as needed, so I'll also add some more documentation around the two use cases for the module, i.e. internal OVS use together with ovs-router vs. external consumption of route dump/parse functions. -- Frode Nordahl > -- > 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