]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Table: Fixed feed race condition
authorMaria Matejka <mq@ucw.cz>
Sun, 24 Sep 2023 09:47:24 +0000 (11:47 +0200)
committerMaria Matejka <mq@ucw.cz>
Sun, 24 Sep 2023 18:43:04 +0000 (20:43 +0200)
The problem happened like this:

1. Single route for the given net in table
2. A feed is started
3. The route is deleted (from another thread)
4. The feed finds an empty net, exports nothing, ignores journal (here is bug)
5. The route is added
6. The export transitions from FEEDING to READY
7. While processing the journal, the route deletion and addition combines into noop.

This way routes mysteriously disappeared in specific cases of link instability.

Problem fixed by explicitly marking the empty-net journal entries as processed in step 4.

nest/proto.c
nest/rt-table.c
nest/rt.h

index 0304ef7f927f0da987eda42bf7cdd4fa5761c252..476764b8c58d3baba04e141ff2bf35458abaff40 100644 (file)
@@ -505,6 +505,7 @@ channel_start_export(struct channel *c)
     .trace_routes = c->debug | c->proto->debug,
     .dump_req = channel_dump_export_req,
     .log_state_change = channel_export_log_state_change,
+    .mark_seen = channel_rpe_mark_seen,
   };
 
   bmap_init(&c->export_map, c->proto->pool, 16);
index 6cd445646ce360bc3f07a3c7fc9d85f70a8a923d..a367a4b5989022ecb884659b1ed134eb31539ff8 100644 (file)
@@ -911,7 +911,7 @@ rt_notify_basic(struct channel *c, const net_addr *net, rte *new, const rte *old
   do_rt_notify(c, net, new, old);
 }
 
-static void
+void
 channel_rpe_mark_seen(struct rt_export_request *req, struct rt_pending_export *rpe)
 {
   struct channel *c = SKIP_BACK(struct channel, out_req, req);
@@ -4266,11 +4266,12 @@ typedef struct {
 static int
 rt_prepare_feed(struct rt_table_export_hook *c, net *n, rt_feed_block *b)
 {
-  uint bs = c->h.req->feed_block_size ?: 16384;
+  struct rt_export_request *req = c->h.req;
+  uint bs = req->feed_block_size ?: 16384;
 
   if (n->routes)
   {
-    if (c->h.req->export_bulk)
+    if (req->export_bulk)
     {
       uint cnt = rte_feed_count(n);
       if (b->cnt && (b->cnt + cnt > bs))
@@ -4304,6 +4305,13 @@ rt_prepare_feed(struct rt_table_export_hook *c, net *n, rt_feed_block *b)
       b->rpe[b->pos++] = (struct rt_pending_export) { .new = n->routes, .new_best = n->routes };
     }
   }
+  else
+    if (req->mark_seen)
+      RPE_WALK(n->first, rpe, NULL)
+       req->mark_seen(req, rpe);
+    else
+      RPE_WALK(n->first, rpe, NULL)
+       rpe_mark_seen(&c->h, rpe);
 
   return 1;
 }
index e0a099e1a5adb9568db305f8e47b0ecd90ee5752..796940168283fe4133a8dd37c419860a195947d5 100644 (file)
--- a/nest/rt.h
+++ b/nest/rt.h
@@ -321,6 +321,8 @@ struct rt_export_request {
       struct rt_pending_export *rpe, struct rt_pending_export *last,
       const rte **feed, uint count);
 
+  void (*mark_seen)(struct rt_export_request *req, struct rt_pending_export *rpe);
+
   void (*dump_req)(struct rt_export_request *req);
   void (*log_state_change)(struct rt_export_request *req, u8);
 };
@@ -471,7 +473,7 @@ void rt_feed_any(struct rt_export_request *req, const net_addr *net, struct rt_p
 void rt_notify_accepted(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *first, struct rt_pending_export *last, const rte **feed, uint count);
 void rt_notify_merged(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *first, struct rt_pending_export *last, const rte **feed, uint count);
 
-
+void channel_rpe_mark_seen(struct rt_export_request *req, struct rt_pending_export *rpe);
 
 /* Types of route announcement, also used as flags */
 #define RA_UNDEF       0               /* Undefined RA type */