diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index f005b20183..c44e067732 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -267,7 +267,7 @@ bgp_sync_init (struct peer *peer) BGP_ADV_FIFO_INIT (&sync->withdraw); BGP_ADV_FIFO_INIT (&sync->withdraw_low); peer->sync[afi][safi] = sync; - peer->hash[afi][safi] = hash_create (baa_hash_key, baa_hash_cmp); + peer->hash[afi][safi] = hash_create (baa_hash_key, baa_hash_cmp, NULL); } } diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index f9daeb2ed3..f304b3a1b7 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -2134,7 +2134,7 @@ aspath_cmp (const void *arg1, const void *arg2) void aspath_init (void) { - ashash = hash_create_size (32768, aspath_key_make, aspath_cmp); + ashash = hash_create_size (32768, aspath_key_make, aspath_cmp, NULL); } void diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 6a7ec473b8..d343ce236b 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -215,7 +215,7 @@ cluster_unintern (struct cluster_list *cluster) static void cluster_init (void) { - cluster_hash = hash_create (cluster_hash_key_make, cluster_hash_cmp); + cluster_hash = hash_create (cluster_hash_key_make, cluster_hash_cmp, NULL); } static void @@ -403,9 +403,9 @@ encap_hash_cmp (const void *p1, const void *p2) static void encap_init (void) { - encap_hash = hash_create (encap_hash_key_make, encap_hash_cmp); + encap_hash = hash_create (encap_hash_key_make, encap_hash_cmp, NULL); #if ENABLE_BGP_VNC - vnc_hash = hash_create (encap_hash_key_make, encap_hash_cmp); + vnc_hash = hash_create (encap_hash_key_make, encap_hash_cmp, NULL); #endif } @@ -517,7 +517,7 @@ transit_hash_cmp (const void *p1, const void *p2) static void transit_init (void) { - transit_hash = hash_create (transit_hash_key_make, transit_hash_cmp); + transit_hash = hash_create (transit_hash_key_make, transit_hash_cmp, NULL); } static void @@ -765,7 +765,7 @@ attrhash_cmp (const void *p1, const void *p2) static void attrhash_init (void) { - attrhash = hash_create (attrhash_key_make, attrhash_cmp); + attrhash = hash_create (attrhash_key_make, attrhash_cmp, "BGP Attributes"); } /* diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index bd67829d77..be4cdac0ac 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -686,7 +686,7 @@ void community_init (void) { comhash = hash_create ((unsigned int (*) (void *))community_hash_make, - (int (*) (const void *, const void *))community_cmp); + (int (*) (const void *, const void *))community_cmp, NULL); } void diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index fa1ad813f1..0555d1bbe3 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -284,7 +284,7 @@ ecommunity_cmp (const void *arg1, const void *arg2) void ecommunity_init (void) { - ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp); + ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp, NULL); } void diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 4a969c8b90..23c19f70ce 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -286,7 +286,7 @@ lcommunity_hash (void) void lcommunity_init (void) { - lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp); + lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp, NULL); } void diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 4216a2d49b..d0c4d2c945 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -140,7 +140,7 @@ void bgp_address_init (struct bgp *bgp) { bgp->address_hash = hash_create (bgp_address_hash_key_make, - bgp_address_hash_cmp); + bgp_address_hash_cmp, NULL); } void diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index b1b3336363..722eed91c0 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -87,7 +87,7 @@ sync_init (struct update_subgroup *subgrp) BGP_ADV_FIFO_INIT (&subgrp->sync->update); BGP_ADV_FIFO_INIT (&subgrp->sync->withdraw); BGP_ADV_FIFO_INIT (&subgrp->sync->withdraw_low); - subgrp->hash = hash_create (baa_hash_key, baa_hash_cmp); + subgrp->hash = hash_create (baa_hash_key, baa_hash_cmp, NULL); /* We use a larger buffer for subgrp->work in the event that: * - We RX a BGP_UPDATE where the attributes alone are just @@ -1559,7 +1559,7 @@ update_bgp_group_init (struct bgp *bgp) AF_FOREACH (afid) bgp->update_groups[afid] = hash_create (updgrp_hash_key_make, - updgrp_hash_cmp); + updgrp_hash_cmp, NULL); } void diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 033a3d194d..65b53be653 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2910,7 +2910,7 @@ bgp_create (as_t *as, const char *name, enum bgp_instance_type inst_type) bgp->peer_self->host = XSTRDUP(MTYPE_BGP_PEER_HOST, "Static announcement"); bgp->peer = list_new (); bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp; - bgp->peerhash = hash_create (peer_hash_key_make, peer_hash_cmp); + bgp->peerhash = hash_create (peer_hash_key_make, peer_hash_cmp, NULL); bgp->group = list_new (); bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp; diff --git a/configure.ac b/configure.ac index c47c185bfe..03951503c1 100755 --- a/configure.ac +++ b/configure.ac @@ -1589,6 +1589,11 @@ AM_CONDITIONAL([SNMP], [test "x${SNMP_METHOD}" != "x"]) AC_SUBST(SNMP_LIBS) AC_SUBST(SNMP_CFLAGS) +dnl --------------- +dnl math +dnl --------------- +AC_SEARCH_LIBS([sqrt], [m]) + dnl --------------- dnl dlopen & dlinfo dnl --------------- diff --git a/lib/command.c b/lib/command.c index f019473308..de8899687c 100644 --- a/lib/command.c +++ b/lib/command.c @@ -233,7 +233,7 @@ install_node (struct cmd_node *node, // add start node struct cmd_token *token = cmd_token_new (START_TKN, CMD_ATTR_NORMAL, NULL, NULL); graph_new_node (node->cmdgraph, token, (void (*)(void *)) &cmd_token_del); - node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp); + node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp, NULL); } /** @@ -2520,6 +2520,7 @@ cmd_init (int terminal) thread_cmd_init (); workqueue_cmd_init (); + hash_cmd_init (); } install_element (CONFIG_NODE, &hostname_cmd); diff --git a/lib/distribute.c b/lib/distribute.c index c771f018c2..79d7b18ff5 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -522,7 +522,8 @@ void distribute_list_init (int node) { disthash = hash_create (distribute_hash_make, - (int (*) (const void *, const void *)) distribute_cmp); + (int (*) (const void *, const void *)) + distribute_cmp, NULL); /* vtysh command-extraction doesn't grok install_element(node, ) */ if (node == RIP_NODE) { diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 614c722be1..4b9bed4524 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -55,7 +55,7 @@ void frr_pthread_init() pthread_mutex_lock(&pthread_table_mtx); { pthread_table = - hash_create(pthread_table_hash_key, pthread_table_hash_cmp); + hash_create(pthread_table_hash_key, pthread_table_hash_cmp, NULL); } pthread_mutex_unlock(&pthread_table_mtx); } diff --git a/lib/hash.c b/lib/hash.c index 553a137eb6..95643bbae0 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -19,23 +19,33 @@ */ #include +#include #include "hash.h" #include "memory.h" +#include "linklist.h" +#include "termtable.h" +#include "vty.h" +#include "command.h" +#include "libfrr.h" DEFINE_MTYPE( LIB, HASH, "Hash") DEFINE_MTYPE( LIB, HASH_BACKET, "Hash Bucket") DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index") +pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER; +static struct list *_hashes; + /* Allocate a new hash. */ struct hash * hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), - int (*hash_cmp) (const void *, const void *)) + int (*hash_cmp) (const void *, const void *), + const char *name) { struct hash *hash; assert ((size & (size-1)) == 0); - hash = XMALLOC (MTYPE_HASH, sizeof (struct hash)); + hash = XCALLOC (MTYPE_HASH, sizeof (struct hash)); hash->index = XCALLOC (MTYPE_HASH_INDEX, sizeof (struct hash_backet *) * size); hash->size = size; @@ -43,6 +53,17 @@ hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), hash->hash_key = hash_key; hash->hash_cmp = hash_cmp; hash->count = 0; + hash->name = name ? XSTRDUP(MTYPE_HASH, name) : NULL; + hash->stats.empty = hash->size; + + pthread_mutex_lock (&_hashes_mtx); + { + if (!_hashes) + _hashes = list_new(); + + listnode_add (_hashes, hash); + } + pthread_mutex_unlock (&_hashes_mtx); return hash; } @@ -50,9 +71,10 @@ hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), /* Allocate a new hash with default hash size. */ struct hash * hash_create (unsigned int (*hash_key) (void *), - int (*hash_cmp) (const void *, const void *)) + int (*hash_cmp) (const void *, const void *), + const char *name) { - return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp); + return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp, name); } /* Utility function for hash_get(). When this function is specified @@ -64,6 +86,15 @@ hash_alloc_intern (void *arg) return arg; } +#define hash_update_ssq(hz, old, new) \ + do { \ + long double res; \ + res = powl(old, 2.0); \ + hz->stats.ssq -= (uint64_t) res;\ + res = powl(new, 2.0); \ + hz->stats.ssq += (uint64_t) res; \ + } while (0); \ + /* Expand hash if the chain length exceeds the threshold. */ static void hash_expand (struct hash *hash) { @@ -75,6 +106,8 @@ static void hash_expand (struct hash *hash) if (new_index == NULL) return; + hash->stats.empty = new_size; + for (i = 0; i < hash->size; i++) for (hb = hash->index[i]; hb; hb = hbnext) { @@ -82,6 +115,19 @@ static void hash_expand (struct hash *hash) hbnext = hb->next; hb->next = new_index[h]; + + int oldlen = hb->next ? hb->next->len : 0; + int newlen = oldlen + 1; + + if (newlen == 1) + hash->stats.empty--; + else + hb->next->len = 0; + + hb->len = newlen; + + hash_update_ssq(hash, oldlen, newlen); + new_index[h] = hb; } @@ -91,19 +137,17 @@ static void hash_expand (struct hash *hash) hash->index = new_index; /* Ideally, new index should have chains half as long as the original. - If expansion didn't help, then not worth expanding again, - the problem is the hash function. */ + * If expansion didn't help, then not worth expanding again, + * the problem is the hash function. */ losers = 0; for (i = 0; i < hash->size; i++) { - unsigned int len = 0; - for (hb = hash->index[i]; hb; hb = hb->next) - { - if (++len > HASH_THRESHOLD/2) - ++losers; - if (len >= HASH_THRESHOLD) - hash->no_expand = 1; - } + unsigned int len = hash->index[i] ? hash->index[i]->len : 0; + + if (len > HASH_THRESHOLD/2) + ++losers; + if (len >= HASH_THRESHOLD) + hash->no_expand = 1; } if (losers > hash->count / 2) @@ -145,12 +189,25 @@ hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) index = key & (hash->size - 1); } - backet = XMALLOC (MTYPE_HASH_BACKET, sizeof (struct hash_backet)); + backet = XCALLOC (MTYPE_HASH_BACKET, sizeof (struct hash_backet)); backet->data = newdata; backet->key = key; backet->next = hash->index[index]; hash->index[index] = backet; hash->count++; + + int oldlen = backet->next ? backet->next->len : 0; + int newlen = oldlen + 1; + + if (newlen == 1) + hash->stats.empty--; + else + backet->next->len = 0; + + backet->len = newlen; + + hash_update_ssq(hash, oldlen, newlen); + return backet->data; } return NULL; @@ -193,11 +250,21 @@ hash_release (struct hash *hash, void *data) { if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) { - if (backet == pp) + int oldlen = hash->index[index]->len; + int newlen = oldlen - 1; + + if (backet == pp) hash->index[index] = backet->next; - else + else pp->next = backet->next; + if (hash->index[index]) + hash->index[index]->len = newlen; + else + hash->stats.empty++; + + hash_update_ssq(hash, oldlen, newlen); + ret = backet->data; XFREE (MTYPE_HASH_BACKET, backet); hash->count--; @@ -275,6 +342,9 @@ hash_clean (struct hash *hash, void (*free_func) (void *)) } hash->index[i] = NULL; } + + hash->stats.ssq = 0; + hash->stats.empty = hash->size; } /* Free hash memory. You may call hash_clean before call this @@ -282,6 +352,136 @@ hash_clean (struct hash *hash, void (*free_func) (void *)) void hash_free (struct hash *hash) { + pthread_mutex_lock (&_hashes_mtx); + { + if (_hashes) + { + listnode_delete (_hashes, hash); + if (_hashes->count == 0) + { + list_delete (_hashes); + _hashes = NULL; + } + } + } + pthread_mutex_unlock (&_hashes_mtx); + + if (hash->name) + XFREE (MTYPE_HASH, hash->name); + XFREE (MTYPE_HASH_INDEX, hash->index); XFREE (MTYPE_HASH, hash); } + + +/* CLI commands ------------------------------------------------------------ */ + +DEFUN(show_hash_stats, + show_hash_stats_cmd, + "show hashtable [statistics]", + SHOW_STR + "Statistics about hash tables\n" + "Statistics about hash tables\n") +{ + struct hash *h; + struct listnode *ln; + struct ttable *tt = ttable_new (&ttable_styles[TTSTYLE_BLANK]); + + ttable_add_row (tt, "Hash table|Buckets|Entries|Empty|LF|SD|FLF|SD"); + tt->style.cell.lpad = 2; + tt->style.cell.rpad = 1; + tt->style.corner = '+'; + ttable_restyle (tt); + ttable_rowseps (tt, 0, BOTTOM, true, '-'); + + /* Summary statistics calculated are: + * + * - Load factor: This is the number of elements in the table divided by the + * number of buckets. Since this hash table implementation uses chaining, + * this value can be greater than 1. This number provides information on + * how 'full' the table is, but does not provide information on how evenly + * distributed the elements are. Notably, a load factor >= 1 does not imply + * that every bucket has an element; with a pathological hash function, all + * elements could be in a single bucket. + * + * - Full load factor: this is the number of elements in the table divided by + * the number of buckets that have some elements in them. + * + * - Std. Dev.: This is the standard deviation calculated from the relevant + * load factor. If the load factor is the mean of number of elements per + * bucket, the standard deviation measures how much any particular bucket + * is likely to deviate from the mean. As a rule of thumb this number + * should be less than 2, and ideally <= 1 for optimal performance. A + * number larger than 3 generally indicates a poor hash function. + */ + + double lf; // load factor + double flf; // full load factor + double var; // overall variance + double fvar; // full variance + double stdv; // overall stddev + double fstdv; // full stddev + + long double x2; // h->count ^ 2 + long double ldc; // (long double) h->count + long double full; // h->size - h->stats.empty + long double ssq; // ssq casted to long double + + pthread_mutex_lock (&_hashes_mtx); + for (ALL_LIST_ELEMENTS_RO (_hashes, ln, h)) + { + if (!h->name) + continue; + + ssq = (long double) h->stats.ssq; + x2 = powl(h->count, 2.0); + ldc = (long double) h->count; + full = h->size - h->stats.empty; + lf = h->count / (double) h->size; + flf = full ? h->count / (double) (full) : 0; + var = ldc ? (1.0 / ldc) * (ssq - x2 / ldc) : 0; + fvar = full ? (1.0 / full) * (ssq - x2 / full) : 0; + var = (var < .0001) ? 0 : var; + fvar = (fvar < .0001) ? 0 : fvar; + stdv = sqrt(var); + fstdv = sqrt(fvar); + + ttable_add_row (tt, "%s|%d|%ld|%.0f%%|%.2lf|%.2lf|%.2lf|%.2lf", h->name, + h->size, h->count, + (h->stats.empty / (double) h->size)*100, lf, stdv, flf, + fstdv); + } + pthread_mutex_unlock (&_hashes_mtx); + + /* display header */ + char header[] = "Showing hash table statistics for "; + char underln[sizeof(header) + strlen(frr_protonameinst)]; + memset (underln, '-', sizeof(underln)); + underln[sizeof(underln) - 1] = '\0'; + vty_outln (vty, "%s%s", header, frr_protonameinst); + vty_outln (vty, "%s", underln); + + vty_outln (vty, "# allocated: %d", _hashes->count); + vty_outln (vty, "# named: %d%s", tt->nrows - 1, VTYNL); + + if (tt->nrows > 1) + { + ttable_colseps (tt, 0, RIGHT, true, '|'); + char *table = ttable_dump (tt, VTYNL); + vty_out (vty, "%s%s", table, VTYNL); + XFREE (MTYPE_TMP, table); + } + else + vty_outln (vty, "No named hash tables to display."); + + ttable_del (tt); + + return CMD_SUCCESS; +} + +void +hash_cmd_init () +{ + _hashes = list_new(); + install_element (ENABLE_NODE, &show_hash_stats_cmd); +} diff --git a/lib/hash.h b/lib/hash.h index bafb35a2a3..9395440acb 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -22,6 +22,7 @@ #define _ZEBRA_HASH_H #include "memory.h" +#include "frratomic.h" DECLARE_MTYPE(HASH) DECLARE_MTYPE(HASH_BACKET) @@ -35,6 +36,10 @@ DECLARE_MTYPE(HASH_BACKET) struct hash_backet { + /* if this backet is the head of the linked listed, len denotes the number of + * elements in the list */ + int len; + /* Linked list. */ struct hash_backet *next; @@ -45,6 +50,14 @@ struct hash_backet void *data; }; +struct hashstats +{ + /* number of empty hash buckets */ + _Atomic int empty; + /* sum of squares of bucket length */ + _Atomic uint64_t ssq; +}; + struct hash { /* Hash backet. */ @@ -64,12 +77,19 @@ struct hash /* Backet alloc. */ unsigned long count; + + struct hashstats stats; + + /* hash name */ + char *name; }; extern struct hash *hash_create (unsigned int (*) (void *), - int (*) (const void *, const void *)); + int (*) (const void *, const void *), + const char *); extern struct hash *hash_create_size (unsigned int, unsigned int (*) (void *), - int (*) (const void *, const void *)); + int (*) (const void *, const void *), + const char *); extern void *hash_get (struct hash *, void *, void * (*) (void *)); extern void *hash_alloc_intern (void *); @@ -87,4 +107,6 @@ extern void hash_free (struct hash *); extern unsigned int string_hash_make (const char *); +extern void hash_cmd_init (void); + #endif /* _ZEBRA_HASH_H */ diff --git a/lib/if_rmap.c b/lib/if_rmap.c index f9c6a55d7b..32bebd67ff 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -316,7 +316,7 @@ if_rmap_reset () void if_rmap_init (int node) { - ifrmaphash = hash_create (if_rmap_hash_make, if_rmap_hash_cmp); + ifrmaphash = hash_create (if_rmap_hash_make, if_rmap_hash_cmp, NULL); if (node == RIPNG_NODE) { } else if (node == RIP_NODE) { install_element (RIP_NODE, &if_rmap_cmd); diff --git a/lib/qobj.c b/lib/qobj.c index 4cf7fbca7b..8fa8163970 100644 --- a/lib/qobj.c +++ b/lib/qobj.c @@ -97,7 +97,7 @@ void qobj_init (void) if (!nodes) { pthread_rwlock_init (&nodes_lock, NULL); - nodes = hash_create (qobj_key, qobj_cmp); + nodes = hash_create (qobj_key, qobj_cmp, NULL); } } diff --git a/lib/routemap.c b/lib/routemap.c index 9eb28888ad..caba8afd71 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1767,7 +1767,7 @@ route_map_dep_hash_alloc(void *p) dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep)); dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); dep_entry->dep_rmap_hash = hash_create(route_map_dep_hash_make_key, - route_map_rmap_hash_cmp); + route_map_rmap_hash_cmp, NULL); dep_entry->this_hash = NULL; return((void *)dep_entry); @@ -2986,11 +2986,11 @@ route_map_init (void) /* Make vector for match and set. */ route_match_vec = vector_init (1); route_set_vec = vector_init (1); - route_map_master_hash = hash_create(route_map_hash_key_make, route_map_hash_cmp); + route_map_master_hash = hash_create(route_map_hash_key_make, route_map_hash_cmp, NULL); for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) route_map_dep_hash[i] = hash_create(route_map_dep_hash_make_key, - route_map_dep_hash_cmp); + route_map_dep_hash_cmp, NULL); cmd_variable_handler_register(rmap_var_handlers); diff --git a/lib/thread.c b/lib/thread.c index 71b0bb2aed..4e72d4c96f 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -388,7 +388,7 @@ thread_master_create (const char *name) rv->cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, (int (*) (const void *, const void *)) - cpu_record_hash_cmp); + cpu_record_hash_cmp, NULL); /* Initialize the timer queues */ diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c index 2d92842b5c..bd884bbc51 100644 --- a/nhrpd/nhrp_cache.c +++ b/nhrpd/nhrp_cache.c @@ -81,7 +81,7 @@ struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote struct nhrp_cache key; if (!nifp->cache_hash) { - nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp); + nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp, NULL); if (!nifp->cache_hash) return NULL; } diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index d9e8627a14..95541b88b6 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -182,7 +182,7 @@ struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *re struct nhrp_vc *vc; if (!nifp->peer_hash) { - nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp); + nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp, NULL); if (!nifp->peer_hash) return NULL; } diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c index 57fb462ab6..a5547a7a7e 100644 --- a/nhrpd/nhrp_vc.c +++ b/nhrpd/nhrp_vc.c @@ -196,7 +196,7 @@ void nhrp_vc_init(void) { size_t i; - nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp); + nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp, NULL); for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) list_init(&childlist_head[i]); } diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c index 24b3199397..61fbfd7795 100644 --- a/nhrpd/reqid.c +++ b/nhrpd/reqid.c @@ -17,7 +17,7 @@ static int nhrp_reqid_cmp(const void *data, const void *key) uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *p, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *)) { if (!p->reqid_hash) { - p->reqid_hash = hash_create(nhrp_reqid_key, nhrp_reqid_cmp); + p->reqid_hash = hash_create(nhrp_reqid_key, nhrp_reqid_cmp, NULL); p->next_request_id = 1; } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index afaa951724..f4125af9b4 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -180,7 +180,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) pim_ifp->pim_ifchannel_list->cmp = (int (*)(void *, void *)) pim_ifchannel_compare; pim_ifp->pim_ifchannel_hash = hash_create (pim_ifchannel_hash_key, - pim_ifchannel_equal); + pim_ifchannel_equal, NULL); ifp->info = pim_ifp; diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index c883a2c8bb..ae5f365b82 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -822,7 +822,7 @@ static struct igmp_sock *igmp_sock_new(int fd, igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free; igmp->igmp_group_hash = hash_create (igmp_group_hash_key, - igmp_group_hash_equal); + igmp_group_hash_equal, NULL); igmp->fd = fd; igmp->interface = ifp; diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 18e24dae32..71a2869818 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -1570,13 +1570,13 @@ pim_msdp_init(struct thread_master *master) msdp->master = master; msdp->peer_hash = hash_create(pim_msdp_peer_hash_key_make, - pim_msdp_peer_hash_eq); + pim_msdp_peer_hash_eq, NULL); msdp->peer_list = list_new(); msdp->peer_list->del = (void (*)(void *))pim_msdp_peer_free; msdp->peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp; msdp->sa_hash = hash_create(pim_msdp_sa_hash_key_make, - pim_msdp_sa_hash_eq); + pim_msdp_sa_hash_eq, NULL); msdp->sa_list = list_new(); msdp->sa_list->del = (void (*)(void *))pim_msdp_sa_free; msdp->sa_list->cmp = (int (*)(void *, void *))pim_msdp_sa_comp; diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 71ca576d93..66be2be6f5 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -103,7 +103,7 @@ void pim_oil_init (void) { pim_channel_oil_hash = hash_create_size (8192, pim_oil_hash_key, - pim_oil_equal); + pim_oil_equal, NULL); pim_channel_oil_list = list_new(); if (!pim_channel_oil_list) { diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index d7ebdea45d..442cb02a15 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -1760,7 +1760,7 @@ pim_upstream_init (void) pim_upstream_hash_key, pim_upstream_sg_running); pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key, - pim_upstream_equal); + pim_upstream_equal, NULL); pim_upstream_list = list_new (); pim_upstream_list->del = (void (*)(void *)) pim_upstream_free; diff --git a/pimd/pimd.c b/pimd/pimd.c index ec1fe5b6d0..b1d566f51b 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -250,7 +250,7 @@ pim_instance_init (vrf_id_t vrf_id, afi_t afi) pim->spt.switchover = PIM_SPT_IMMEDIATE; pim->spt.plist = NULL; - pim->rpf_hash = hash_create_size (256, pim_rpf_hash_key, pim_rpf_equal); + pim->rpf_hash = hash_create_size (256, pim_rpf_hash_key, pim_rpf_equal, NULL); if (PIM_DEBUG_ZEBRA) zlog_debug ("%s: NHT rpf hash init ", __PRETTY_FUNCTION__); diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c index 07f60668e7..792e2696e9 100644 --- a/tests/lib/test_srcdest_table.c +++ b/tests/lib/test_srcdest_table.c @@ -140,7 +140,7 @@ test_state_new(void) rv->table = srcdest_table_init(); assert(rv->table); - rv->log = hash_create(log_key, log_cmp); + rv->log = hash_create(log_key, log_cmp, NULL); return rv; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index d10861a668..0e04f4bf8e 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2103,6 +2103,33 @@ DEFUN (vtysh_show_work_queues_daemon, return ret; } +DEFUN (vtysh_show_hashtable, + vtysh_show_hashtable_cmd, + "show hashtable [statistics]", + SHOW_STR + "Statistics about hash tables\n" + "Statistics about hash tables\n") +{ + char cmd[] = "do show hashtable statistics"; + unsigned long i; + int ret = CMD_SUCCESS; + + fprintf (stdout, "\n"); + fprintf (stdout, "Load factor (LF) - average number of elements across all buckets\n"); + fprintf (stdout, "Full load factor (FLF) - average number of elements across full buckets\n\n"); + + fprintf (stdout, "Standard deviation (SD) is calculated for both the LF and FLF\n"); + fprintf (stdout, "and indicates the typical deviation of bucket chain length\n"); + fprintf (stdout, "from the value in the corresponding load factor.\n\n"); + + for (i = 0; i < array_size(vtysh_client); i++) + if ( vtysh_client[i].fd >= 0 ) { + ret = vtysh_client_execute (&vtysh_client[i], cmd, stdout); + fprintf (stdout, "\n"); + } + return ret; +} + DEFUNSH (VTYSH_ZEBRA, vtysh_link_params, vtysh_link_params_cmd, @@ -3575,6 +3602,8 @@ vtysh_init_vty (void) install_element (VIEW_NODE, &vtysh_show_work_queues_cmd); install_element (VIEW_NODE, &vtysh_show_work_queues_daemon_cmd); + install_element (VIEW_NODE, &vtysh_show_hashtable_cmd); + install_element (VIEW_NODE, &vtysh_show_thread_cmd); /* Logging */ diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index a8e7f5372c..e08ff08cf6 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -2994,8 +2994,8 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) { if (!zvrf) return; - zvrf->slsp_table = hash_create(label_hash, label_cmp); - zvrf->lsp_table = hash_create(label_hash, label_cmp); + zvrf->slsp_table = hash_create(label_hash, label_cmp, NULL); + zvrf->lsp_table = hash_create(label_hash, label_cmp, NULL); zvrf->fec_table[AFI_IP] = route_table_init(); zvrf->fec_table[AFI_IP6] = route_table_init(); zvrf->mpls_flags = 0;