]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Lockless feed of a single net
authorMaria Matejka <mq@ucw.cz>
Tue, 23 Apr 2024 16:50:22 +0000 (18:50 +0200)
committerMaria Matejka <mq@ucw.cz>
Fri, 17 May 2024 07:12:52 +0000 (09:12 +0200)
lib/birdlib.h
nest/route.h
nest/rt-table.c

index df2176d480377ac75af43ffc9294dc5239bd3302..4c18242fa6a0b31fed4595951b7323b3164e1402 100644 (file)
     SAME_TYPE(&_ptr->i, _orig); \
     _ptr; })
 #define BIRD_ALIGN(s, a) (((s)+a-1)&~(a-1))
+#define BIRD_SET_ALIGNED_POINTER(ptr, val)  do { \
+  size_t _alignment = _Alignof(typeof(*ptr)); \
+  ptr = (typeof(ptr)) BIRD_ALIGN((uintptr_t)(val), _alignment); \
+} while (0)
 #define CPU_STRUCT_ALIGN  (MAX_(_Alignof(void*), _Alignof(u64)))
 #define BIRD_CPU_ALIGN(s) BIRD_ALIGN((s), CPU_STRUCT_ALIGN)
 
index 41c6aee3e06b004e6423e8c9fcb0e5e9aa258b2a..9b57cf5a6641803f1e42ad992203be0320fb0e51 100644 (file)
@@ -298,6 +298,14 @@ struct rt_pending_export {
   const rte *new, *new_best, *old, *old_best;
 };
 
+struct rt_export_feed {
+  uint count_routes, count_exports;
+  struct netindex *ni;
+  rte *block;
+  u64 *exports;
+  char data[0];
+};
+
 struct rt_export_request {
   struct rt_export_hook *hook;         /* Table part of the export */
   char *name;
@@ -570,6 +578,8 @@ void rt_flowspec_link(rtable *src, rtable *dst);
 void rt_flowspec_unlink(rtable *src, rtable *dst);
 rtable *rt_setup(pool *, struct rtable_config *);
 
+struct rt_export_feed *rt_net_feed(rtable *t, net_addr *a);
+rte rt_net_best(rtable *t, net_addr *a);
 int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
 rte *rt_export_merged(struct channel *c, const net_addr *n, const rte ** feed, uint count, linpool *pool, int silent);
 void rt_refresh_begin(struct rt_import_request *);
index 59c863f9f34d2df2f5081a1102964dfd774098b3..30e428232dad3f9055c9358b203f0fd4c0dcfb75 100644 (file)
@@ -696,6 +696,23 @@ rte_feed_obtain(struct rtable_reading *tr, net *n, const rte **feed, uint count)
     RT_READ_RETRY(tr);
 }
 
+static void
+rte_feed_obtain_copy(struct rtable_reading *tr, net *n, rte *feed, uint count)
+{
+  uint i = 0;
+  NET_READ_WALK_ROUTES(tr, n, ep, e)
+  {
+    if (i >= count)
+      RT_READ_RETRY(tr);
+
+    feed[i++] = e->rte;
+    ea_free_later(ea_ref(e->rte.attrs));
+  }
+
+  if (i != count)
+    RT_READ_RETRY(tr);
+}
+
 static rte *
 export_filter(struct channel *c, rte *rt, int silent)
 {
@@ -2023,9 +2040,89 @@ rte_import(struct rt_import_request *req, const net_addr *n, rte *new, struct rt
   }
 }
 
-/* Check rtable for best route to given net whether it would be exported do p */
-int
-rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter)
+struct rt_export_feed *
+rt_net_feed(rtable *t, net_addr *a)
+{
+  RT_READ(t, tr);
+
+  const struct netindex *i = net_find_index(t->netindex, a);
+  net *n = i ? net_find(tr, i) : NULL;
+  if (!n)
+    return 0;
+
+  /* Get the feed itself. It may change under our hands tho. */
+  struct rt_pending_export *first = atomic_load_explicit(&n->first, memory_order_acquire);
+  struct rt_pending_export *last = atomic_load_explicit(&n->last, memory_order_acquire);
+
+  /* Count the elements */
+  uint rcnt = rte_feed_count(tr, n);
+  uint ecnt = 0;
+  uint ocnt = 0;
+  for (struct rt_pending_export *rpe = first; rpe;
+      rpe = atomic_load_explicit(&rpe->next, memory_order_acquire))
+  {
+    ecnt++;
+    if (rpe->old)
+      ocnt++;
+  }
+
+  struct rt_export_feed *feed = NULL;
+
+  if (rcnt || ocnt || ecnt)
+  {
+    uint size = sizeof *feed
+       + (rcnt+ocnt) * sizeof *feed->block + _Alignof(typeof(*feed->block))
+       + ecnt * sizeof *feed->exports + _Alignof(typeof(*feed->exports));
+
+    feed = tmp_alloc(size);
+
+    feed->ni = i;
+    feed->count_routes = rcnt+ocnt;
+    feed->count_exports = ecnt;
+    BIRD_SET_ALIGNED_POINTER(feed->block, feed->data);
+    BIRD_SET_ALIGNED_POINTER(feed->exports, &feed->block[rcnt+ocnt]);
+
+    /* Consistency check */
+    ASSERT_DIE(((void *) &feed->exports[ecnt]) <= ((void *) feed) + size);
+
+    if (rcnt)
+      rte_feed_obtain_copy(tr, n, feed->block, rcnt);
+
+    if (ecnt)
+    {
+      uint e = 0;
+      uint rpos = rcnt;
+      for (struct rt_pending_export *rpe = first; rpe;
+         rpe = atomic_load_explicit(&rpe->next, memory_order_acquire))
+       if (e >= ecnt)
+         RT_READ_RETRY(tr);
+       else
+       {
+         feed->exports[e++] = rpe->seq;
+
+         /* Copy also obsolete routes */
+         if (rpe->old)
+         {
+           ASSERT_DIE(rpos < rcnt + ocnt);
+           feed->block[rpos++] = *rpe->old;
+           ea_free_later(ea_ref(rpe->old->attrs));
+         }
+       }
+
+      ASSERT_DIE(e == ecnt);
+    }
+  }
+
+  /* Check that it indeed didn't change and the last export is still the same. */
+  if (last != atomic_load_explicit(&n->last, memory_order_acquire) ||
+      first != atomic_load_explicit(&n->first, memory_order_acquire))
+    RT_READ_RETRY(tr);
+
+  return feed;
+}
+
+rte
+rt_net_best(rtable *t, net_addr *a)
 {
   rte rt = {};
 
@@ -2033,12 +2130,23 @@ rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filte
 
   const struct netindex *i = net_find_index(t->netindex, a);
   net *n = i ? net_find(tr, i) : NULL;
-  struct rte_storage *e = NET_READ_BEST_ROUTE(tr, n);
+  if (!n)
+    return rt;
 
+  struct rte_storage *e = NET_READ_BEST_ROUTE(tr, n);
   if (!e || !rte_is_valid(&e->rte))
-    return 0;
+    return rt;
+
+  ea_free_later(ea_ref(e->rte.attrs));
+  return RTE_COPY(e);
+}
+
+/* Check rtable for best route to given net whether it would be exported do p */
+int
+rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter)
+{
+  rte rt = rt_net_best(t, a);
 
-  rt = RTE_COPY(e);
   int v = c->proto->preexport ? c->proto->preexport(c, &rt) : 0;
   if (v == RIC_PROCESS)
     v = (f_run(filter, &rt, FF_SILENT) <= F_ACCEPT);