]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Table prune inhibited during reconfiguration
authorMaria Matejka <mq@ucw.cz>
Sun, 15 Dec 2024 20:04:22 +0000 (21:04 +0100)
committerMaria Matejka <mq@ucw.cz>
Sun, 15 Dec 2024 21:21:34 +0000 (22:21 +0100)
When many changes are done during reconfiguration, the table may
start pruning old routes before everything is settled down, slowing
down not only the reconfiguration, but also the shutdown process.

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

index 89502f7a4d72da453c47dc936053077f10043262..e512dc36b64d069a953407d39a7cee9161be98f0 100644 (file)
@@ -413,6 +413,8 @@ struct rtable_private {
                                         * delete as soon as use_count becomes 0 and remove
                                         * obstacle from this routing table.
                                         */
+
+  struct deferred_call *reconf_end;    /* Reconfiguration done callback */
   struct rt_export_request best_req;   /* Internal request from best route announcement cleanup */
   struct rt_uncork_callback nhu_uncork;        /* Helper event to schedule NHU on uncork */
   struct rt_uncork_callback hcu_uncork;        /* Helper event to schedule HCU on uncork */
index fcbc6db8a4b33db03841022fd7ce957e2c6b8c0b..9b0e86dd0a01cff16f9411a8e1e7eb861cbb7d2e 100644 (file)
@@ -2881,7 +2881,34 @@ rt_schedule_prune(struct rtable_private *tab)
 {
   /* state change 0->1, 2->3 */
   tab->prune_state |= 1;
-  ev_send_loop(tab->loop, tab->prune_event);
+  if (!tab->reconf_end)
+    ev_send_loop(tab->loop, tab->prune_event);
+
+  /* If reconfiguring, we explicitly activate the prune after done
+   * to stop expensive operations from happening too early. */
+}
+
+struct rt_reconf_finished_deferred_call {
+  struct deferred_call dc;
+  rtable *tab;
+};
+
+static void
+rt_reconf_finished(struct deferred_call *dc)
+{
+  /* Reconfiguration ended, let's reinstate prune events */
+  ASSERT_DIE(birdloop_inside(&main_birdloop));
+  RT_LOCKED(SKIP_BACK(struct rt_reconf_finished_deferred_call, dc, dc)->tab, tab)
+  {
+    rt_unlock_table(tab);
+    if (dc == tab->reconf_end)
+    {
+      tab->reconf_end = NULL;
+
+      if (tab->prune_state & 1)
+       ev_send_loop(tab->loop, tab->prune_event);
+    }
+  }
 }
 
 static void
@@ -3569,8 +3596,10 @@ rt_prune_table(void *_tab)
   lfjour_announce_now(&tab->export_all.journal);
   lfjour_announce_now(&tab->export_best.journal);
 
-  /* state change 2->0, 3->1 */
-  if (tab->prune_state &= 1)
+  /* state change 2->0, 3->1
+   * pausing expensive prune while reconfiguring to allow for
+   * the imports to settle */
+  if ((tab->prune_state &= 1) && !tab->reconf_end)
     ev_send_loop(tab->loop, tab->prune_event);
 
   struct f_trie *trie = atomic_load_explicit(&tab->trie, memory_order_relaxed);
@@ -4652,6 +4681,14 @@ rt_reconfigure(struct rtable_private *tab, struct rtable_config *new, struct rta
     ||  (new->digest_settle.max != tab->export_digest->settle.cf.max)))
     tab->export_digest->settle.cf = new->digest_settle;
 
+  rt_lock_table(tab); /* Unlocked in rt_reconf_finished() */
+  struct rt_reconf_finished_deferred_call rrfdc = {
+    .dc.hook = rt_reconf_finished,
+    .tab = RT_PUB(tab),
+  };
+
+  tab->reconf_end = defer_call(&rrfdc.dc, sizeof rrfdc);
+
   return 1;
 }
 
@@ -4702,7 +4739,15 @@ rt_commit(struct config *new, struct config *old)
          OBSREF_SET(tab->deleted, old);
          rt_check_cork_low(tab);
          rt_lock_table(tab);
-         rt_unlock_table(tab);
+
+         /* No actual table stopping before reconfiguring the rest.
+          * Table unlocked in the deferred call. */
+         struct rt_reconf_finished_deferred_call rrfdc = {
+           .dc.hook = rt_reconf_finished,
+           .tab = RT_PUB(tab),
+         };
+
+         tab->reconf_end = defer_call(&rrfdc.dc, sizeof rrfdc);
        }
 
        CALL(o->table->config->master.stop, o->table);