The hash table for communities is not great. Instead of implementing dynamic hash resize use a RB tree. Also drop the hash calculation and just use memcmp() for now. My non scientific test seems to indicate that the overhead of SipHash is about the same as the memcmp().
-- :wq Claudio Index: bgpd/rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.566 diff -u -p -r1.566 rde.c --- bgpd/rde.c 29 Aug 2022 14:57:27 -0000 1.566 +++ bgpd/rde.c 29 Aug 2022 14:58:52 -0000 @@ -199,7 +199,6 @@ rde_main(int debug, int verbose) pt_init(); path_init(pathhashsize); aspath_init(pathhashsize); - communities_init(attrhashsize); attr_init(attrhashsize); nexthop_init(nexthophashsize); peer_init(peerhashsize); @@ -636,9 +635,6 @@ badnetdel: imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_RIB_HASH, 0, imsg.hdr.pid, -1, &rdehash, sizeof(rdehash)); aspath_hash_stats(&rdehash); - imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_RIB_HASH, 0, - imsg.hdr.pid, -1, &rdehash, sizeof(rdehash)); - communities_hash_stats(&rdehash); imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_RIB_HASH, 0, imsg.hdr.pid, -1, &rdehash, sizeof(rdehash)); attr_hash_stats(&rdehash); Index: bgpd/rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.263 diff -u -p -r1.263 rde.h --- bgpd/rde.h 26 Aug 2022 14:10:52 -0000 1.263 +++ bgpd/rde.h 29 Aug 2022 14:58:52 -0000 @@ -183,9 +183,9 @@ struct mpattr { }; struct rde_community { - LIST_ENTRY(rde_community) entry; - size_t size; - size_t nentries; + RB_ENTRY(rde_community) entry; + int size; + int nentries; int flags; int refcnt; struct community *communities; @@ -486,9 +486,7 @@ int community_large_write(struct rde_com int community_ext_write(struct rde_community *, int, void *, uint16_t); int community_writebuf(struct ibuf *, struct rde_community *); -void communities_init(uint32_t); void communities_shutdown(void); -void communities_hash_stats(struct rde_hashstats *); struct rde_community *communities_lookup(struct rde_community *); struct rde_community *communities_link(struct rde_community *); void communities_unlink(struct rde_community *); Index: bgpd/rde_community.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_community.c,v retrieving revision 1.7 diff -u -p -r1.7 rde_community.c --- bgpd/rde_community.c 28 Jul 2022 13:11:51 -0000 1.7 +++ bgpd/rde_community.c 29 Aug 2022 14:58:52 -0000 @@ -209,12 +209,12 @@ mask_match(struct community *a, struct c static void insert_community(struct rde_community *comm, struct community *c) { - size_t l; + int l; int r; if (comm->nentries + 1 > comm->size) { struct community *new; - size_t newsize = comm->size + 8; + int newsize = comm->size + 8; if ((new = reallocarray(comm->communities, newsize, sizeof(struct community))) == NULL) @@ -261,7 +261,7 @@ community_match(struct rde_community *co struct rde_peer *peer) { struct community test, mask; - size_t l; + int l; if (fc->flags >> 8 == 0) { /* fast path */ @@ -288,7 +288,7 @@ struct rde_peer *peer) int community_count(struct rde_community *comm, uint8_t type) { - size_t l; + int l; int count = 0; /* use the fact that the array is ordered by type */ @@ -351,7 +351,7 @@ struct rde_peer *peer) { struct community test, mask; struct community *match; - size_t l = 0; + int l = 0; if (fc->flags >> 8 == 0) { /* fast path */ @@ -501,8 +501,8 @@ community_write(struct rde_community *co { uint8_t *b = buf; uint16_t c; - size_t l, n = 0; - int r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE; + size_t n = 0; + int l, r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE; if (comm->flags & PARTIAL_COMMUNITIES) flags |= ATTR_PARTIAL; @@ -545,8 +545,8 @@ community_large_write(struct rde_communi { uint8_t *b = buf; uint32_t c; - size_t l, n = 0; - int r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE; + size_t n = 0; + int l, r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE; if (comm->flags & PARTIAL_LARGE_COMMUNITIES) flags |= ATTR_PARTIAL; @@ -596,8 +596,8 @@ community_ext_write(struct rde_community struct community *cp; uint8_t *b = buf; uint64_t ext; - size_t l, n = 0; - int r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE; + size_t n = 0; + int l, r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE; if (comm->flags & PARTIAL_EXT_COMMUNITIES) flags |= ATTR_PARTIAL; @@ -654,8 +654,8 @@ community_ext_write(struct rde_community int community_writebuf(struct ibuf *buf, struct rde_community *comm) { - size_t l, basic_n = 0, large_n = 0, ext_n = 0; - int flags; + size_t basic_n = 0, large_n = 0, ext_n = 0; + int l, flags; /* first count how many communities will be written */ for (l = 0; l < comm->nentries; l++) @@ -764,113 +764,44 @@ community_writebuf(struct ibuf *buf, str /* * Global RIB cache for communities */ -LIST_HEAD(commhead, rde_community); - -static struct comm_table { - struct commhead *hashtbl; - uint64_t hashmask; -} commtable; - -static SIPHASH_KEY commtablekey; - -static inline struct commhead * -communities_hash(struct rde_community *comm) -{ - SIPHASH_CTX ctx; - uint64_t hash; - - SipHash24_Init(&ctx, &commtablekey); - SipHash24_Update(&ctx, &comm->nentries, sizeof(comm->nentries)); - SipHash24_Update(&ctx, &comm->flags, sizeof(comm->flags)); - if (comm->nentries > 0) - SipHash24_Update(&ctx, comm->communities, - comm->nentries * sizeof(*comm->communities)); - hash = SipHash24_End(&ctx); - - return &commtable.hashtbl[hash & commtable.hashmask]; -} - -void -communities_init(uint32_t hashsize) +static inline int +communities_compare(struct rde_community *a, struct rde_community *b) { - uint32_t hs, i; + if (a->nentries != b->nentries) + return a->nentries - b->nentries; + if (a->flags != b->flags) + return a->flags - b->flags; - arc4random_buf(&commtablekey, sizeof(commtablekey)); - for (hs = 1; hs < hashsize; hs <<= 1) - ; - commtable.hashtbl = calloc(hs, sizeof(*commtable.hashtbl)); - if (commtable.hashtbl == NULL) - fatal(__func__); - - for (i = 0; i < hs; i++) - LIST_INIT(&commtable.hashtbl[i]); - commtable.hashmask = hs - 1; + return memcmp(a->communities, b->communities, + a->nentries * sizeof(struct community)); } -void -communities_shutdown(void) -{ - uint64_t i; - - for (i = 0; i <= commtable.hashmask; i++) - if (!LIST_EMPTY(&commtable.hashtbl[i])) - log_warnx("%s: free non-free table", __func__); - - free(commtable.hashtbl); -} +RB_HEAD(comm_tree, rde_community) commtable = RB_INITIALIZER(&commtable); +RB_GENERATE_STATIC(comm_tree, rde_community, entry, communities_compare); void -communities_hash_stats(struct rde_hashstats *hs) +communities_shutdown(void) { - struct rde_community *c; - uint64_t i; - int64_t n; - - memset(hs, 0, sizeof(*hs)); - strlcpy(hs->name, "comm hash", sizeof(hs->name)); - hs->min = LLONG_MAX; - hs->num = commtable.hashmask + 1; - - for (i = 0; i <= commtable.hashmask; i++) { - n = 0; - LIST_FOREACH(c, &commtable.hashtbl[i], entry) - n++; - if (n < hs->min) - hs->min = n; - if (n > hs->max) - hs->max = n; - hs->sum += n; - hs->sumq += n * n; - } + if (!RB_EMPTY(&commtable)) + log_warnx("%s: free non-free table", __func__); } struct rde_community * communities_lookup(struct rde_community *comm) { - struct rde_community *c; - struct commhead *head; - - head = communities_hash(comm); - LIST_FOREACH(c, head, entry) { - if (communities_equal(comm, c)) - return c; - } - return NULL; + return RB_FIND(comm_tree, &commtable, comm); } struct rde_community * communities_link(struct rde_community *comm) { struct rde_community *n; - struct commhead *head; if ((n = malloc(sizeof(*n))) == NULL) fatal(__func__); - communities_copy(n, comm); - head = communities_hash(n); - LIST_INSERT_HEAD(head, n, entry); + RB_INSERT(comm_tree, &commtable, n); n->refcnt = 1; /* initial reference by the cache */ rdemem.comm_size += n->size; @@ -886,7 +817,7 @@ communities_unlink(struct rde_community if (comm->refcnt != 1) fatalx("%s: unlinking still referenced communities", __func__); - LIST_REMOVE(comm, entry); + RB_REMOVE(comm_tree, &commtable, comm); rdemem.comm_size -= comm->size; rdemem.comm_nmemb -= comm->nentries;