]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Index of different net_addr values for hashing and bit-marking
authorMaria Matejka <mq@ucw.cz>
Mon, 4 Dec 2023 09:33:30 +0000 (10:33 +0100)
committerMaria Matejka <mq@ucw.cz>
Mon, 8 Jan 2024 08:34:32 +0000 (09:34 +0100)
For many reasons, it's handy to assign a contiguous range of integers to
known net_addr values. This is a data structure keeping this mapping.

lib/Makefile
lib/hash.h
lib/netindex.c [new file with mode: 0644]
lib/netindex.h [new file with mode: 0644]
lib/netindex_private.h [new file with mode: 0644]

index cd40bade6776e780c99f750ecf8267d7223a9124..fafca6bf464d58ce5aba97ebd3531dbd9dcea8bb 100644 (file)
@@ -1,4 +1,4 @@
-src := a-path.c a-set.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c rcu.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
+src := a-path.c a-set.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c netindex.c patmatch.c printf.c rcu.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
 obj := $(src-o-files)
 $(all-daemon)
 
index f46e769dab7c14bc6234d071dbaff5fbf861f8a8..1c744af0fcc6eb6fe29e0ff1aecef8af19147e9d 100644 (file)
     (v) = (typeof(v)){ };                                              \
   })
 
-#define HASH_FIND(v,id,key...)                                         \
+#define HASH_FIND_CHAIN(v,id,key...)                                   \
   ({                                                                   \
     u32 _h = HASH_FN(v, id, key);                                      \
-    HASH_TYPE(v) *_n = (v).data[_h];                                   \
+    (v).data[_h];                                                      \
+  })
+
+#define HASH_FIND(v,id,key...)                                         \
+  ({                                                                   \
+    HASH_TYPE(v) *_n = HASH_FIND_CHAIN(v, id, key);                    \
     while (_n && !HASH_EQ(v, id, id##_KEY(_n), key))                   \
       _n = id##_NEXT(_n);                                              \
     _n;                                                                        \
diff --git a/lib/netindex.c b/lib/netindex.c
new file mode 100644 (file)
index 0000000..3194c77
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ *     BIRD Internet Routing Daemon -- Semi-global index of nets
+ *
+ *     (c) 2023       Maria Matejka <mq@jmq.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "lib/birdlib.h"
+#include "lib/netindex_private.h"
+
+#define NETINDEX_KEY(n)                (n)->hash, (n)->addr
+#define NETINDEX_NEXT(n)       (n)->next
+#define NETINDEX_EQ(h,n,i,o)   ((h == i) && net_equal(n,o))
+#define NETINDEX_FN(h,n)       (h)
+#define NETINDEX_ORDER         4 /* Initial */
+
+#define NETINDEX_REHASH                netindex_rehash
+#define NETINDEX_PARAMS                /8, *1, 2, 2, 4, 28
+
+HASH_DEFINE_REHASH_FN(NETINDEX, struct netindex);
+
+static void netindex_hash_cleanup(void *netindex_hash);
+
+/*
+ * Handle for persistent or semipersistent usage
+ */
+struct netindex_handle {
+  resource r;
+  struct netindex *index;
+  netindex_hash *h;
+};
+
+static void
+net_unlock_index_persistent(resource *r)
+{
+  struct netindex_handle *nh = SKIP_BACK(struct netindex_handle, r, r);
+  net_unlock_index(nh->h, nh->index);
+}
+
+static void
+netindex_handle_dump(resource *r, unsigned indent UNUSED)
+{
+  struct netindex_handle *nh = SKIP_BACK(struct netindex_handle, r, r);
+  debug("index=%u, net=%N", nh->index->index, nh->index->addr);
+}
+
+static struct resclass netindex_handle_class = {
+  .name = "Netindex handle",
+  .size = sizeof(struct netindex_handle),
+  .free = net_unlock_index_persistent,
+  .dump = netindex_handle_dump,
+};
+
+static struct netindex *
+net_lock_index_persistent(struct netindex_hash_private *hp, struct netindex *ni, pool *p)
+{
+  if (!ni)
+    return NULL;
+
+  struct netindex_handle *nh = ralloc(p, &netindex_handle_class);
+//  log(L_TRACE "Revive index %p", ni);
+  lfuc_lock_revive(&ni->uc);
+  nh->index = ni;
+  nh->h = SKIP_BACK(netindex_hash, priv, hp);
+  return ni;
+}
+
+/*
+ * Index initialization
+ */
+netindex_hash *
+netindex_hash_new(pool *sp)
+{
+  DOMAIN(attrs) dom = DOMAIN_NEW(attrs);
+  LOCK_DOMAIN(attrs, dom);
+
+  pool *p = rp_new(sp, dom.attrs, "Network index");
+
+  struct netindex_hash_private *nh = mb_allocz(p, sizeof *nh);
+  nh->lock = dom;
+  nh->pool = p;
+
+  nh->cleanup_list = &global_event_list;
+  nh->cleanup_event = (event) { .hook = netindex_hash_cleanup, nh };
+
+  UNLOCK_DOMAIN(attrs, dom);
+  return SKIP_BACK(netindex_hash, priv, nh);
+}
+
+static void
+netindex_hash_cleanup(void *_nh)
+{
+  NH_LOCK((netindex_hash *) _nh, nh);
+
+  for (uint t = 0; t < NET_MAX; t++)
+  {
+    if (!nh->net[t].hash.data)
+      continue;
+
+    HASH_WALK_FILTER(nh->net[t].hash, next, i, ii)
+      if (lfuc_finished(&i->uc))
+      {
+       HASH_DO_REMOVE(nh->net[t].hash, NETINDEX, ii);
+       hmap_clear(&nh->net[t].id_map, i->index);
+       if (nh->net[t].slab)
+         sl_free(i);
+       else
+         mb_free(i);
+      }
+    HASH_WALK_DELSAFE_END;
+  }
+}
+
+
+static void
+netindex_hash_init(struct netindex_hash_private *hp, u8 type)
+{
+  ASSERT_DIE(hp->net[type].block == NULL);
+
+  hp->net[type].slab = net_addr_length[type] ? sl_new(hp->pool, sizeof (struct netindex) + net_addr_length[type]) : NULL;
+  HASH_INIT(hp->net[type].hash, hp->pool, NETINDEX_ORDER);
+  hp->net[type].block_size = 128;
+  hp->net[type].block = mb_allocz(hp->pool, hp->net[type].block_size * sizeof (struct netindex *));
+  hmap_init(&hp->net[type].id_map, hp->pool, 128);
+};
+
+/*
+ * Private index manipulation
+ */
+struct netindex *
+net_find_index_fragile_chain(struct netindex_hash_private *hp, const net_addr *n)
+{
+  ASSERT_DIE(n->type < NET_MAX);
+  if (!hp->net[n->type].block)
+    return NULL;
+
+  u32 h = net_hash(n);
+  return HASH_FIND_CHAIN(hp->net[n->type].hash, NETINDEX, h, n);
+}
+
+struct netindex *
+net_find_index_fragile(struct netindex_hash_private *hp, const net_addr *n)
+{
+  ASSERT_DIE(n->type < NET_MAX);
+  if (!hp->net[n->type].block)
+    return NULL;
+
+  u32 h = net_hash(n);
+  return HASH_FIND(hp->net[n->type].hash, NETINDEX, h, n);
+}
+
+static struct netindex *
+net_find_index_locked(struct netindex_hash_private *hp, const net_addr *n, pool *p)
+{
+  struct netindex *ni = net_find_index_fragile(hp, n);
+  return ni ? net_lock_index_persistent(hp, ni, p) : NULL;
+}
+
+static struct netindex *
+net_new_index_locked(struct netindex_hash_private *hp, const net_addr *n, pool *p)
+{
+  if (!hp->net[n->type].block)
+    netindex_hash_init(hp, n->type);
+
+  u32 i = hmap_first_zero(&hp->net[n->type].id_map);
+  hmap_set(&hp->net[n->type].id_map, i);
+
+  struct netindex *ni = hp->net[n->type].slab ?
+    sl_alloc(hp->net[n->type].slab) :
+    mb_alloc(hp->pool, n->length + sizeof *ni);
+
+  *ni = (struct netindex) {
+    .hash = net_hash(n),
+    .index = i,
+  };
+  net_copy(ni->addr, n);
+
+  HASH_INSERT2(hp->net[n->type].hash, NETINDEX, hp->pool, ni);
+
+  return net_lock_index_persistent(hp, ni, p);
+}
+
+
+/*
+ * Public entry points
+ */
+
+void net_lock_index(netindex_hash *h UNUSED, struct netindex *i)
+{
+//  log(L_TRACE "Lock index %p", i);
+  return lfuc_lock(&i->uc);
+}
+
+void net_unlock_index(netindex_hash *h, struct netindex *i)
+{
+//  log(L_TRACE "Unlock index %p", i);
+  return lfuc_unlock(&i->uc, h->cleanup_list, &h->cleanup_event);
+}
+
+struct netindex *
+net_find_index_persistent(netindex_hash *h, const net_addr *n, pool *p)
+{
+  NH_LOCK(h, hp);
+  return net_find_index_locked(hp, n, p);
+}
+
+struct netindex *
+net_get_index_persistent(netindex_hash *h, const net_addr *n, pool *p)
+{
+  NH_LOCK(h, hp);
+  return
+    net_find_index_locked(hp, n, p) ?:
+    net_new_index_locked(hp, n, p);
+}
+
+struct netindex *
+net_resolve_index_persistent(netindex_hash *h, u8 net_type, u32 i, pool *p)
+{
+  NH_LOCK(h, hp);
+  return net_lock_index_persistent(hp, hp->net[net_type].block_size > i ? hp->net[net_type].block[i] : NULL, p);
+}
diff --git a/lib/netindex.h b/lib/netindex.h
new file mode 100644 (file)
index 0000000..3a71194
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *     BIRD Internet Routing Daemon -- Semi-global index of nets
+ *
+ *     (c) 2023       Maria Matejka <mq@jmq.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_LIB_NETINDEX_H_
+#define _BIRD_LIB_NETINDEX_H_
+
+#include "lib/bitmap.h"
+#include "lib/hash.h"
+#include "lib/lockfree.h"
+#include "lib/net.h"
+#include "lib/resource.h"
+
+/* Index object */
+struct netindex {
+  struct netindex *next;       /* Next in hash chain */
+  u32 hash;                    /* Cached hash value */
+  u32 index;                   /* Assigned index */
+  struct lfuc uc;              /* Atomic usecount */
+  net_addr addr[0];            /* The net itself (one) */
+};
+
+/* Index hash: data structure completely opaque, use handlers */
+typedef union netindex_hash netindex_hash;
+
+/* Initialization */
+netindex_hash *netindex_hash_new(pool *);
+
+/* Find/get/resolve index and allocate its usecount to the given pool */
+struct netindex *net_find_index_persistent(netindex_hash *, const net_addr *, pool *);
+struct netindex *net_get_index_persistent(netindex_hash *, const net_addr *, pool *);
+struct netindex *net_resolve_index_persistent(netindex_hash *, u8, u32, pool *);
+
+/* Find/get/resolve index; pointer valid until end of task */ 
+static inline struct netindex *net_find_index(netindex_hash *h, const net_addr *n)
+{ return net_find_index_persistent(h, n, tmp_res.pool); }
+static inline struct netindex *net_get_index(netindex_hash *h, const net_addr *n)
+{ return net_get_index_persistent(h, n, tmp_res.pool); }
+static inline struct netindex *net_resolve_index(netindex_hash *h, u8 net_type, u32 index)
+{ return net_resolve_index_persistent(h, net_type, index, tmp_res.pool); }
+
+/* Update use-count without allocating a handle. Take same care
+ * to ensure that your locks and unlocks are always balanced. */
+void net_lock_index(netindex_hash *h, struct netindex *i);
+void net_unlock_index(netindex_hash *h, struct netindex *i);
+
+/* Retrieve the index from its addr pointer */
+#define NET_TO_INDEX(a) \
+  SKIP_BACK(struct netindex, addr, TYPE_CAST(net_addr *, net_addr (*)[0], a))
+
+#endif //_BIRD_LIB_NETINDEX_H_
diff --git a/lib/netindex_private.h b/lib/netindex_private.h
new file mode 100644 (file)
index 0000000..712a6aa
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *     BIRD Internet Routing Daemon -- Semi-global index of nets
+ *
+ *     (c) 2023       Maria Matejka <mq@jmq.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_LIB_NETINDEX_PRIVATE_H_
+#define _BIRD_LIB_NETINDEX_PRIVATE_H_
+
+#include "lib/netindex.h"
+
+#define NETINDEX_HASH_PUBLIC \
+  DOMAIN(attrs) lock;          /* Assigned lock */             \
+  event_list *cleanup_list;    /* Cleanup event list */        \
+  event cleanup_event;         /* Cleanup event */             \
+
+struct netindex_hash_private {
+  struct { NETINDEX_HASH_PUBLIC; };
+  struct netindex_hash_private **locked_at;
+  pool *pool;
+  struct {
+    slab *slab;
+    HASH(struct netindex) hash;
+    uint block_size;
+    struct netindex **block;
+    struct hmap id_map;
+  } net[NET_MAX];
+};
+
+typedef union netindex_hash {
+  struct { NETINDEX_HASH_PUBLIC; };
+  struct netindex_hash_private priv;
+} netindex_hash;
+
+LOBJ_UNLOCK_CLEANUP(netindex_hash, attrs);
+#define NH_LOCK(h, hp) LOBJ_LOCK(h, hp, netindex_hash, attrs)
+
+/* Find indices in a locked context with no usecounting */
+struct netindex *net_find_index_fragile(struct netindex_hash_private *hp, const net_addr *n);
+
+/* The same but instead of returning the exact match,
+ * return the first item in hash chain */
+struct netindex *net_find_index_fragile_chain(struct netindex_hash_private *hp, const net_addr *n);
+
+#endif