]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Kernel: feed only once during startup
authorMaria Matejka <mq@ucw.cz>
Mon, 23 Dec 2024 10:58:05 +0000 (11:58 +0100)
committerMaria Matejka <mq@ucw.cz>
Thu, 9 Jan 2025 09:08:27 +0000 (10:08 +0100)
There was an inefficiency in the initial scan state machine,
causing routes to be fed several times instead of just once.
Now the export startup is postponed until first krt_scan()
finishes and we actually can do the pruning with full information.

nest/proto.c
nest/protocol.h
sysdep/unix/krt.c
sysdep/unix/krt.h

index 678697d69b382f4eecdb78816ebfa624f0336c96..6fa74e9f175e51e60b3d5a528935256e168ebc18 100644 (file)
@@ -676,9 +676,11 @@ void channel_notify_basic(void *);
 void channel_notify_accepted(void *);
 void channel_notify_merged(void *);
 
-static void
+void
 channel_start_export(struct channel *c)
 {
+  ASSERT_DIE(birdloop_inside(c->proto->loop));
+
   if (rt_export_get_state(&c->out_req) != TES_DOWN)
     bug("%s.%s: Attempted to start channel's already started export", c->proto->name, c->name);
 
index cf7ecb8988623d05c235a5fa515d25ce746f614f..2bfa1628a92bbf38c5f58f70e6b2965f4c415bea 100644 (file)
@@ -747,6 +747,8 @@ int proto_configure_channel(struct proto *p, struct channel **c, struct channel_
 
 void channel_set_state(struct channel *c, uint state);
 
+void channel_start_export(struct channel *c);
+
 void channel_add_obstacle(struct channel *c);
 void channel_del_obstacle(struct channel *c);
 
index 34882b88f619668ccfa44bb240b2542b515fb642..1658dd6fe6342bfaadf8c833397b66f0245d45e4 100644 (file)
@@ -342,6 +342,8 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
 /* Hook defined in nest/rt-table.c ... to be refactored away later */
 rte *krt_export_net(struct channel *c, const net_addr *a, linpool *lp);
 
+static void krt_rt_notify(struct proto *P, struct channel *ch, const net_addr *net, rte *new, const rte *old);
+
 static int
 krt_same_dest(rte *k, rte *e)
 {
@@ -361,6 +363,11 @@ krt_same_dest(rte *k, rte *e)
 void
 krt_got_route(struct krt_proto *p, rte *e, s8 src)
 {
+  /* If we happen to get an asynchronous route notification
+   * before initialization, we wait for the scan. */
+  if (p->sync_state == KPS_INIT)
+    return;
+
   rte *new = NULL;
   e->pflags = 0;
 
@@ -391,10 +398,6 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src)
 
   /* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
 
-  /* We wait for the initial feed to have correct installed state */
-  if (!p->ready)
-    goto ignore;
-
   /* Get the exported version */
   new = krt_export_net(p->p.main_channel, e->net, krt_filter_lp);
 
@@ -423,10 +426,6 @@ aseen:
   krt_trace_in(p, e, "already seen");
   goto done;
 
-ignore:
-  krt_trace_in(p, e, "ignored");
-  goto done;
-
 update:
   krt_trace_in(p, new, "updating");
   krt_replace_rte(p, e->net, new, e);
@@ -447,12 +446,21 @@ krt_init_scan(struct krt_proto *p)
 {
   switch (p->sync_state)
   {
+    case KPS_INIT:
+      /* Allow exports now */
+      p->p.rt_notify = krt_rt_notify;
+      channel_start_export(p->p.main_channel);
+      rt_refresh_begin(&p->p.main_channel->in_req);
+      p->sync_state = KPS_FIRST_SCAN;
+      return 1;
+
     case KPS_IDLE:
       rt_refresh_begin(&p->p.main_channel->in_req);
       bmap_reset(&p->seen_map, 1024);
       p->sync_state = KPS_SCANNING;
       return 1;
 
+    case KPS_FIRST_SCAN:
     case KPS_SCANNING:
       bug("Kernel scan double-init");
 
@@ -470,14 +478,17 @@ krt_prune(struct krt_proto *p)
 {
   switch (p->sync_state)
   {
+    case KPS_INIT:
     case KPS_IDLE:
       bug("Kernel scan prune without scan");
 
     case KPS_SCANNING:
+      channel_request_full_refeed(p->p.main_channel);
+      /* fall through */
+    case KPS_FIRST_SCAN:
       p->sync_state = KPS_PRUNING;
       KRT_TRACE(p, D_EVENTS, "Pruning table %s", p->p.main_channel->table->name);
       rt_refresh_end(&p->p.main_channel->in_req);
-      channel_request_full_refeed(p->p.main_channel);
       break;
 
     case KPS_PRUNING:
@@ -549,7 +560,7 @@ krt_scan_all(timer *t UNUSED)
   krt_do_scan(NULL);
 
   WALK_LIST2(p, n, krt_proto_list, krt_node)
-    if (p->sync_state == KPS_SCANNING)
+    if ((p->sync_state == KPS_SCANNING) || (p->sync_state == KPS_FIRST_SCAN))
       krt_prune(p);
 }
 
@@ -644,6 +655,9 @@ krt_scan_timer_kick(struct krt_proto *p)
 static int
 krt_preexport(struct channel *C, rte *e)
 {
+  /* The export should not start before proper sync */
+  ASSERT_DIE(SKIP_BACK(struct krt_proto, p, C->proto)->sync_state != KPS_INIT);
+
   if (e->src->owner == &C->proto->sources)
 #ifdef CONFIG_SINGLE_ROUTE
     return 1;
@@ -659,15 +673,6 @@ krt_preexport(struct channel *C, rte *e)
     return -1;
   }
 
-  /* Before first scan we don't touch the routes */
-  if (!SKIP_BACK(struct krt_proto, p, C->proto)->ready)
-  {
-    if (C->debug & D_ROUTES)
-      log(L_TRACE "%s.%s not ready yet to accept route for %N",
-         C->proto->name, C->name, e->net);
-    return -1;
-  }
-
   return 0;
 }
 
@@ -685,18 +690,24 @@ krt_rt_notify(struct proto *P, struct channel *ch, const net_addr *net,
 
   switch (p->sync_state)
   {
+    case KPS_INIT:
+      bug("Routes in init state should have been rejected by preexport.");
+
     case KPS_IDLE:
     case KPS_PRUNING:
       if (new && bmap_test(&p->seen_map, new->id))
+      {
        if (ch->debug & D_ROUTES)
        {
          /* Already installed and seen in the kernel dump */
          log(L_TRACE "%s.%s: %N already in kernel",
              P->name, ch->name, net);
-         return;
        }
+       return;
+      }
 
       /* fall through */
+    case KPS_FIRST_SCAN:
     case KPS_SCANNING:
       /* Actually replace the route */
       krt_replace_rte(p, net, new, old);
@@ -732,7 +743,6 @@ krt_reload_routes(struct channel *C, struct rt_feeding_request *rfr)
 
   if (KRT_CF->learn)
   {
-    p->reload = 1;
     krt_scan_timer_kick(p);
   }
 
@@ -749,15 +759,18 @@ krt_export_fed(struct channel *C)
 {
   struct krt_proto *p = (void *) C->proto;
 
-  p->ready = 1;
-  p->initialized = 1;
-
   switch (p->sync_state)
   {
+    case KPS_INIT:
+      bug("KRT export started before scan");
+
     case KPS_IDLE:
       krt_scan_timer_kick(p);
       break;
 
+    case KPS_FIRST_SCAN:
+      bug("KRT export done before first scan");
+
     case KPS_SCANNING:
       break;
 
@@ -831,7 +844,8 @@ krt_init(struct proto_config *CF)
   p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
 
   p->p.preexport = krt_preexport;
-  p->p.rt_notify = krt_rt_notify;
+  /* Not setting rt_notify here to not start exports, must wait for the first scan
+   * and then we can start exports manually */
   p->p.iface_sub.if_notify = krt_if_notify;
   p->p.reload_routes = krt_reload_routes;
   p->p.export_fed = krt_export_fed;
@@ -887,7 +901,7 @@ krt_shutdown(struct proto *P)
     return PS_FLUSH;
 
   /* FIXME we should flush routes even when persist during reconfiguration */
-  if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN))
+  if ((p->sync_state != KPS_INIT) && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN))
   {
     struct rt_export_feeder req = (struct rt_export_feeder)
     {
@@ -922,8 +936,7 @@ krt_shutdown(struct proto *P)
 static void
 krt_cleanup(struct krt_proto *p)
 {
-  p->ready = 0;
-  p->initialized = 0;
+  p->sync_state = KPS_INIT;
 
   krt_sys_shutdown(p);
   rem_node(&p->krt_node);
index 394e74010de488a9fd5918405321ebb0c0dffa62..14be715f8f65f28aee343a55d50f30aeaafdd0fd 100644 (file)
@@ -59,10 +59,9 @@ struct krt_proto {
   struct bmap seen_map;                /* Routes seen during last periodic scan */
   node krt_node;               /* Node in krt_proto_list */
   byte af;                     /* Kernel address family (AF_*) */
-  byte ready;                  /* Initial feed has been finished */
-  byte initialized;            /* First scan has been finished */
-  byte reload;                 /* Next scan is doing reload */
   PACKED enum krt_prune_state {
+    KPS_INIT,
+    KPS_FIRST_SCAN,
     KPS_IDLE,
     KPS_SCANNING,
     KPS_PRUNING,