From 724bb99f1160f9bed4b774141a6e08967202a718 Mon Sep 17 00:00:00 2001 From: Rayhaan Jaufeerally Date: Tue, 6 Oct 2026 11:21:53 +0200 Subject: [PATCH] Kernel: read what the table has before letting kept routes go Once every peer had sent all its routes, the kernel task let go of the routes kept from the earlier run that the table did not have, at once. But "the table has" and "the kernel task has read" are not the same moment: a route a peer sent just before its End-of-RIB could still be unread in the table's journal, in which case it was taken out of the kernel and put back a moment later. The interop lab saw this now and then as "N routes were taken out of the kernel over the restart". The task now reads the table to the end before it decides what is unconfirmed; follow() says whether the reader is caught up. Co-Authored-By: Claude Fable 5.1 --- crates/fib/src/kernel.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/fib/src/kernel.rs b/crates/fib/src/kernel.rs index 614eb46..3275e4d 100644 --- a/crates/fib/src/kernel.rs +++ b/crates/fib/src/kernel.rs @@ -746,11 +746,12 @@ impl Task { } /// Takes the next changes of best route from the routing table and - /// hands over what brings the kernel in step with them. - async fn follow(&mut self, reader: &mut Reader) -> Result<(), FibError> { + /// hands over what brings the kernel in step with them. Returns whether + /// the reader has read everything the table has for it. + async fn follow(&mut self, reader: &mut Reader) -> Result { let mut news = Vec::with_capacity(BATCH); // Only what is needed is taken while the table is held. - reader.poll(BATCH, |event| { + let progress = reader.poll(BATCH, |event| { news.push(match event { Event::Reset => News::Reset, Event::Synced => News::Synced, @@ -822,7 +823,8 @@ impl Task { News::Withdrawn(prefix) => ops.extend(self.plan.withdrawn(prefix)), } } - self.send(ops, whole_table).await + self.send(ops, whole_table).await?; + Ok(progress.caught_up) } /// Takes in what the writer says became of what it was given. @@ -1130,6 +1132,11 @@ pub(crate) async fn run( } }, if release_at.is_some() => { release_at = None; + // What the table has by now, the kernel is to have before + // anything is let go of: a route a peer sent just before + // its End-of-RIB may not have been read yet, and would be + // taken out only to be put back. + while !task.follow(&mut reader).await? {} task.keeping = false; let stale = task.plan.release(); info!(