/* * 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); lfuc_lock(&i->uc); } void net_unlock_index(netindex_hash *h, struct netindex *i) { // log(L_TRACE "Unlock index %p", i); 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); }