]> 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>
Sat, 25 May 2024 17:37:16 +0000 (19:37 +0200)
lib/birdlib.h
nest/route.h
nest/rt-table.c

index 578240ad6164248c27e9af430bf32a9b3249fee0..5271f0121b78d4bedb37bf4514100da58a13d395 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 e44a791867f3db13b142e80832192a7d6e8f7349..2d65053372ed55b7aac014d1da57dc34159a1a34 100644 (file)
@@ -294,6 +294,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;
@@ -566,6 +574,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 b40d2fd6a5bac7a92e1e10acd4b2bf675416a8c7..9743d342a23b639ba0768b963315835ea78faa27 100644 (file)
@@ -685,6 +685,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)
 {
@@ -2013,9 +2030,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 = {};
 
@@ -2023,12 +2120,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);