From a392c9e106c166f30ed9d41746c4bd14b02ae404 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Fri, 23 May 2025 19:17:53 +0200 Subject: [PATCH] Table: fix a race condition in export The race condition happens as follows: - channel A starts feeding - channel B imports a route ahead of the feeding pointer - channel A exports this route and continues feeding from the pointer - no other import hits this specific prefix - there is at least one channel C which has not cleared this export - channel A computes ecnt=0 for this prefix because all exports have been already cleared - the condition e >= ecnt mistakenly triggers retry If the birdloops involved get assigned to the same thread, this race condition then can't recover and the thread is stuck in an infinite loop. Fixed the race condition by moving the consistency check after actually checking eligibility of the export, not before. Found by randomly observing performance tests. --- nest/rt-table.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index f1fc2e4f0..888753374 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -2530,10 +2530,11 @@ rt_net_feed_index(struct rtable_reading *tr, net *n, struct bmap *seen, bool (*p uint rpos = rcnt; for (const struct rt_pending_export *rpe = first; rpe; rpe = atomic_load_explicit(&rpe->next, memory_order_acquire)) - if (e >= ecnt) - RT_READ_RETRY(tr); - else if (!seen || !bmap_test(seen, rpe->it.seq)) + if (!seen || !bmap_test(seen, rpe->it.seq)) { + if (e >= ecnt) + RT_READ_RETRY(tr); + feed->exports[e++] = rpe->it.seq; /* Copy also obsolete routes */ @@ -2666,10 +2667,11 @@ rt_feed_net_best(struct rt_exporter *e, struct rcu_unwinder *u, u32 index, struc uint e = 0; for (const struct rt_pending_export *rpe = first; rpe; rpe = atomic_load_explicit(&rpe->next, memory_order_acquire)) - if (e >= ecnt) - RT_READ_RETRY(tr); - else if (!seen || !bmap_test(seen, rpe->it.seq)) + if (!seen || !bmap_test(seen, rpe->it.seq)) { + if (e >= ecnt) + RT_READ_RETRY(tr); + feed->exports[e++] = rpe->it.seq; if (rpe->it.old && (!best || (rpe->it.old != &best->rte))) { -- 2.47.3