From de3bd705f820d36fbd1b043c90249cf23498446c Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Tue, 23 Apr 2024 18:50:22 +0200 Subject: [PATCH] Lockless feed of a single net --- lib/birdlib.h | 4 ++ nest/route.h | 10 ++++ nest/rt-table.c | 120 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 128 insertions(+), 6 deletions(-) diff --git a/lib/birdlib.h b/lib/birdlib.h index df2176d4..4c18242f 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -32,6 +32,10 @@ SAME_TYPE(&_ptr->i, _orig); \ _ptr; }) #define BIRD_ALIGN(s, a) (((s)+a-1)&~(a-1)) +#define BIRD_SET_ALIGNED_POINTER(ptr, val) do { \ + size_t _alignment = _Alignof(typeof(*ptr)); \ + ptr = (typeof(ptr)) BIRD_ALIGN((uintptr_t)(val), _alignment); \ +} while (0) #define CPU_STRUCT_ALIGN (MAX_(_Alignof(void*), _Alignof(u64))) #define BIRD_CPU_ALIGN(s) BIRD_ALIGN((s), CPU_STRUCT_ALIGN) diff --git a/nest/route.h b/nest/route.h index 41c6aee3..9b57cf5a 100644 --- a/nest/route.h +++ b/nest/route.h @@ -298,6 +298,14 @@ struct rt_pending_export { const rte *new, *new_best, *old, *old_best; }; +struct rt_export_feed { + uint count_routes, count_exports; + struct netindex *ni; + rte *block; + u64 *exports; + char data[0]; +}; + struct rt_export_request { struct rt_export_hook *hook; /* Table part of the export */ char *name; @@ -570,6 +578,8 @@ void rt_flowspec_link(rtable *src, rtable *dst); void rt_flowspec_unlink(rtable *src, rtable *dst); rtable *rt_setup(pool *, struct rtable_config *); +struct rt_export_feed *rt_net_feed(rtable *t, net_addr *a); +rte rt_net_best(rtable *t, net_addr *a); int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter); rte *rt_export_merged(struct channel *c, const net_addr *n, const rte ** feed, uint count, linpool *pool, int silent); void rt_refresh_begin(struct rt_import_request *); diff --git a/nest/rt-table.c b/nest/rt-table.c index 59c863f9..30e42823 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -696,6 +696,23 @@ rte_feed_obtain(struct rtable_reading *tr, net *n, const rte **feed, uint count) RT_READ_RETRY(tr); } +static void +rte_feed_obtain_copy(struct rtable_reading *tr, net *n, rte *feed, uint count) +{ + uint i = 0; + NET_READ_WALK_ROUTES(tr, n, ep, e) + { + if (i >= count) + RT_READ_RETRY(tr); + + feed[i++] = e->rte; + ea_free_later(ea_ref(e->rte.attrs)); + } + + if (i != count) + RT_READ_RETRY(tr); +} + static rte * export_filter(struct channel *c, rte *rt, int silent) { @@ -2023,9 +2040,89 @@ rte_import(struct rt_import_request *req, const net_addr *n, rte *new, struct rt } } -/* Check rtable for best route to given net whether it would be exported do p */ -int -rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter) +struct rt_export_feed * +rt_net_feed(rtable *t, net_addr *a) +{ + RT_READ(t, tr); + + const struct netindex *i = net_find_index(t->netindex, a); + net *n = i ? net_find(tr, i) : NULL; + if (!n) + return 0; + + /* Get the feed itself. It may change under our hands tho. */ + struct rt_pending_export *first = atomic_load_explicit(&n->first, memory_order_acquire); + struct rt_pending_export *last = atomic_load_explicit(&n->last, memory_order_acquire); + + /* Count the elements */ + uint rcnt = rte_feed_count(tr, n); + uint ecnt = 0; + uint ocnt = 0; + for (struct rt_pending_export *rpe = first; rpe; + rpe = atomic_load_explicit(&rpe->next, memory_order_acquire)) + { + ecnt++; + if (rpe->old) + ocnt++; + } + + struct rt_export_feed *feed = NULL; + + if (rcnt || ocnt || ecnt) + { + uint size = sizeof *feed + + (rcnt+ocnt) * sizeof *feed->block + _Alignof(typeof(*feed->block)) + + ecnt * sizeof *feed->exports + _Alignof(typeof(*feed->exports)); + + feed = tmp_alloc(size); + + feed->ni = i; + feed->count_routes = rcnt+ocnt; + feed->count_exports = ecnt; + BIRD_SET_ALIGNED_POINTER(feed->block, feed->data); + BIRD_SET_ALIGNED_POINTER(feed->exports, &feed->block[rcnt+ocnt]); + + /* Consistency check */ + ASSERT_DIE(((void *) &feed->exports[ecnt]) <= ((void *) feed) + size); + + if (rcnt) + rte_feed_obtain_copy(tr, n, feed->block, rcnt); + + if (ecnt) + { + uint e = 0; + uint rpos = rcnt; + for (struct rt_pending_export *rpe = first; rpe; + rpe = atomic_load_explicit(&rpe->next, memory_order_acquire)) + if (e >= ecnt) + RT_READ_RETRY(tr); + else + { + feed->exports[e++] = rpe->seq; + + /* Copy also obsolete routes */ + if (rpe->old) + { + ASSERT_DIE(rpos < rcnt + ocnt); + feed->block[rpos++] = *rpe->old; + ea_free_later(ea_ref(rpe->old->attrs)); + } + } + + ASSERT_DIE(e == ecnt); + } + } + + /* Check that it indeed didn't change and the last export is still the same. */ + if (last != atomic_load_explicit(&n->last, memory_order_acquire) || + first != atomic_load_explicit(&n->first, memory_order_acquire)) + RT_READ_RETRY(tr); + + return feed; +} + +rte +rt_net_best(rtable *t, net_addr *a) { rte rt = {}; @@ -2033,12 +2130,23 @@ rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filte const struct netindex *i = net_find_index(t->netindex, a); net *n = i ? net_find(tr, i) : NULL; + if (!n) + return rt; + struct rte_storage *e = NET_READ_BEST_ROUTE(tr, n); - if (!e || !rte_is_valid(&e->rte)) - return 0; + return rt; + + ea_free_later(ea_ref(e->rte.attrs)); + return RTE_COPY(e); +} + +/* Check rtable for best route to given net whether it would be exported do p */ +int +rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter) +{ + rte rt = rt_net_best(t, a); - rt = RTE_COPY(e); int v = c->proto->preexport ? c->proto->preexport(c, &rt) : 0; if (v == RIC_PROCESS) v = (f_run(filter, &rt, FF_SILENT) <= F_ACCEPT);