From: Maria Matejka Date: Mon, 4 Dec 2023 09:33:30 +0000 (+0100) Subject: Index of different net_addr values for hashing and bit-marking X-Git-Tag: v3.0.0~320 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=54d5e36ec0bed1226c98ddee3d34e1ae29692951;p=thirdparty%2Fbird.git Index of different net_addr values for hashing and bit-marking 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. --- diff --git a/lib/Makefile b/lib/Makefile index cd40bade6..fafca6bf4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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) diff --git a/lib/hash.h b/lib/hash.h index f46e769da..1c744af0f 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -31,10 +31,15 @@ (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 index 000000000..3194c77b2 --- /dev/null +++ b/lib/netindex.c @@ -0,0 +1,222 @@ +/* + * BIRD Internet Routing Daemon -- Semi-global index of nets + * + * (c) 2023 Maria Matejka + * + * 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 index 000000000..3a711940b --- /dev/null +++ b/lib/netindex.h @@ -0,0 +1,55 @@ +/* + * BIRD Internet Routing Daemon -- Semi-global index of nets + * + * (c) 2023 Maria Matejka + * + * 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 index 000000000..712a6aac9 --- /dev/null +++ b/lib/netindex_private.h @@ -0,0 +1,47 @@ +/* + * BIRD Internet Routing Daemon -- Semi-global index of nets + * + * (c) 2023 Maria Matejka + * + * 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