2002-12-13 21:15:29 +01:00
|
|
|
/* Hash routine.
|
|
|
|
* Copyright (C) 1998 Kunihiro Ishiguro
|
|
|
|
*
|
|
|
|
* This file is part of GNU Zebra.
|
|
|
|
*
|
|
|
|
* GNU Zebra is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published
|
|
|
|
* by the Free Software Foundation; either version 2, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* GNU Zebra is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
2017-05-13 10:25:29 +02:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; see the file COPYING; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2002-12-13 21:15:29 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
2017-05-30 02:16:52 +02:00
|
|
|
#include <math.h>
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
#include "hash.h"
|
|
|
|
#include "memory.h"
|
2017-05-30 02:16:52 +02:00
|
|
|
#include "linklist.h"
|
|
|
|
#include "termtable.h"
|
|
|
|
#include "vty.h"
|
|
|
|
#include "command.h"
|
2017-06-19 16:22:26 +02:00
|
|
|
#include "libfrr.h"
|
2019-06-21 10:58:02 +02:00
|
|
|
#include "frr_pthread.h"
|
2020-09-28 21:49:22 +02:00
|
|
|
#include "libfrr_trace.h"
|
2002-12-13 21:15:29 +01:00
|
|
|
|
2019-06-21 08:25:42 +02:00
|
|
|
DEFINE_MTYPE_STATIC(LIB, HASH, "Hash")
|
|
|
|
DEFINE_MTYPE_STATIC(LIB, HASH_BACKET, "Hash Bucket")
|
2015-05-29 05:48:31 +02:00
|
|
|
DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index")
|
|
|
|
|
2019-04-03 22:34:18 +02:00
|
|
|
static pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER;
|
2017-05-30 02:16:52 +02:00
|
|
|
static struct list *_hashes;
|
|
|
|
|
2005-05-06 Paul Jakma <paul@dishone.st>
* (general) extern and static'ification of functions in code and
header.
Cleanup any definitions with unspecified arguments.
Add casts for callback assignments where the callback is defined,
typically, as passing void *, but the function being assigned has
some other pointer type defined as its argument, as gcc complains
about casts from void * to X* via function arguments.
Fix some old K&R style function argument definitions.
Add noreturn gcc attribute to some functions, as appropriate.
Add unused gcc attribute to some functions (eg ones meant to help
while debugging)
Add guard defines to headers which were missing them.
* command.c: (install_node) add const qualifier, still doesnt shut
up the warning though, because of the double pointer.
(cmp_node) ditto
* keychain.c: (key_str2time) Add GET_LONG_RANGE() macro, derived
fromn vty.h ones to fix some of the (long) < 0 warnings.
* thread.c: (various) use thread_empty
(cpu_record_hash_key) should cast to uintptr_t, a stdint.h type
* vty.h: Add VTY_GET_IPV4_ADDRESS and VTY_GET_IPV4_PREFIX so they
removed from ospfd/ospf_vty.h
* zebra.h: Move definition of ZEBRA_PORT to here, to remove
dependence of lib on zebra/zserv.h
2005-05-06 23:25:49 +02:00
|
|
|
struct hash *hash_create_size(unsigned int size,
|
2019-05-14 22:19:07 +02:00
|
|
|
unsigned int (*hash_key)(const void *),
|
2018-10-17 21:27:12 +02:00
|
|
|
bool (*hash_cmp)(const void *, const void *),
|
2017-06-19 16:22:26 +02:00
|
|
|
const char *name)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
|
|
|
struct hash *hash;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2013-01-04 23:29:21 +01:00
|
|
|
assert((size & (size - 1)) == 0);
|
2017-06-19 16:22:26 +02:00
|
|
|
hash = XCALLOC(MTYPE_HASH, sizeof(struct hash));
|
2008-08-18 23:13:29 +02:00
|
|
|
hash->index =
|
2019-02-19 16:46:52 +01:00
|
|
|
XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_bucket *) * size);
|
2002-12-13 21:15:29 +01:00
|
|
|
hash->size = size;
|
|
|
|
hash->hash_key = hash_key;
|
|
|
|
hash->hash_cmp = hash_cmp;
|
|
|
|
hash->count = 0;
|
2017-06-19 16:22:26 +02:00
|
|
|
hash->name = name ? XSTRDUP(MTYPE_HASH, name) : NULL;
|
|
|
|
hash->stats.empty = hash->size;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-06-21 10:58:02 +02:00
|
|
|
frr_with_mutex(&_hashes_mtx) {
|
2017-06-19 16:22:26 +02:00
|
|
|
if (!_hashes)
|
|
|
|
_hashes = list_new();
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
listnode_add(_hashes, hash);
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2019-05-14 22:19:07 +02:00
|
|
|
struct hash *hash_create(unsigned int (*hash_key)(const void *),
|
2018-10-17 21:27:12 +02:00
|
|
|
bool (*hash_cmp)(const void *, const void *),
|
2017-06-19 16:22:26 +02:00
|
|
|
const char *name)
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2017-06-19 16:22:26 +02:00
|
|
|
return hash_create_size(HASH_INITIAL_SIZE, hash_key, hash_cmp, name);
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void *hash_alloc_intern(void *arg)
|
|
|
|
{
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
|
2020-09-02 22:02:55 +02:00
|
|
|
/*
|
|
|
|
* ssq = ssq + (new^2 - old^2)
|
|
|
|
* = ssq + ((new + old) * (new - old))
|
|
|
|
*/
|
2017-06-19 16:22:26 +02:00
|
|
|
#define hash_update_ssq(hz, old, new) \
|
2020-09-02 22:02:55 +02:00
|
|
|
do { \
|
|
|
|
int _adjust = (new + old) * (new - old); \
|
|
|
|
if (_adjust < 0) \
|
|
|
|
atomic_fetch_sub_explicit(&hz->stats.ssq, -_adjust, \
|
|
|
|
memory_order_relaxed); \
|
|
|
|
else \
|
|
|
|
atomic_fetch_add_explicit(&hz->stats.ssq, _adjust, \
|
|
|
|
memory_order_relaxed); \
|
|
|
|
} while (0)
|
2017-06-19 16:22:26 +02:00
|
|
|
|
2013-01-11 19:25:26 +01:00
|
|
|
/* Expand hash if the chain length exceeds the threshold. */
|
|
|
|
static void hash_expand(struct hash *hash)
|
|
|
|
{
|
2017-07-27 17:08:40 +02:00
|
|
|
unsigned int i, new_size;
|
2019-02-19 16:46:52 +01:00
|
|
|
struct hash_bucket *hb, *hbnext, **new_index;
|
2013-01-11 19:25:26 +01:00
|
|
|
|
|
|
|
new_size = hash->size * 2;
|
2017-07-31 23:22:23 +02:00
|
|
|
|
|
|
|
if (hash->max_size && new_size > hash->max_size)
|
|
|
|
return;
|
|
|
|
|
2013-01-11 19:25:26 +01:00
|
|
|
new_index = XCALLOC(MTYPE_HASH_INDEX,
|
2019-02-19 16:46:52 +01:00
|
|
|
sizeof(struct hash_bucket *) * new_size);
|
2013-01-11 19:25:26 +01:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
hash->stats.empty = new_size;
|
|
|
|
|
2013-01-11 19:25:26 +01:00
|
|
|
for (i = 0; i < hash->size; i++)
|
|
|
|
for (hb = hash->index[i]; hb; hb = hbnext) {
|
|
|
|
unsigned int h = hb->key & (new_size - 1);
|
|
|
|
|
|
|
|
hbnext = hb->next;
|
|
|
|
hb->next = new_index[h];
|
2017-06-19 16:22:26 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2013-01-11 19:25:26 +01:00
|
|
|
new_index[h] = hb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Switch to new table */
|
|
|
|
XFREE(MTYPE_HASH_INDEX, hash->index);
|
|
|
|
hash->size = new_size;
|
|
|
|
hash->index = new_index;
|
|
|
|
}
|
|
|
|
|
2005-05-06 Paul Jakma <paul@dishone.st>
* (general) extern and static'ification of functions in code and
header.
Cleanup any definitions with unspecified arguments.
Add casts for callback assignments where the callback is defined,
typically, as passing void *, but the function being assigned has
some other pointer type defined as its argument, as gcc complains
about casts from void * to X* via function arguments.
Fix some old K&R style function argument definitions.
Add noreturn gcc attribute to some functions, as appropriate.
Add unused gcc attribute to some functions (eg ones meant to help
while debugging)
Add guard defines to headers which were missing them.
* command.c: (install_node) add const qualifier, still doesnt shut
up the warning though, because of the double pointer.
(cmp_node) ditto
* keychain.c: (key_str2time) Add GET_LONG_RANGE() macro, derived
fromn vty.h ones to fix some of the (long) < 0 warnings.
* thread.c: (various) use thread_empty
(cpu_record_hash_key) should cast to uintptr_t, a stdint.h type
* vty.h: Add VTY_GET_IPV4_ADDRESS and VTY_GET_IPV4_PREFIX so they
removed from ospfd/ospf_vty.h
* zebra.h: Move definition of ZEBRA_PORT to here, to remove
dependence of lib on zebra/zserv.h
2005-05-06 23:25:49 +02:00
|
|
|
void *hash_get(struct hash *hash, void *data, void *(*alloc_func)(void *))
|
2002-12-13 21:15:29 +01:00
|
|
|
{
|
2020-09-15 00:04:33 +02:00
|
|
|
tracepoint(frr_libfrr, hash_get, hash, data);
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
unsigned int key;
|
|
|
|
unsigned int index;
|
|
|
|
void *newdata;
|
2019-02-19 16:46:52 +01:00
|
|
|
struct hash_bucket *bucket;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-10-16 19:56:01 +02:00
|
|
|
if (!alloc_func && !hash->count)
|
|
|
|
return NULL;
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
key = (*hash->hash_key)(data);
|
2013-01-04 23:29:21 +01:00
|
|
|
index = key & (hash->size - 1);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
for (bucket = hash->index[index]; bucket != NULL;
|
|
|
|
bucket = bucket->next) {
|
|
|
|
if (bucket->key == key && (*hash->hash_cmp)(bucket->data, data))
|
|
|
|
return bucket->data;
|
2013-01-11 19:25:26 +01:00
|
|
|
}
|
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
if (alloc_func) {
|
2002-12-13 21:15:29 +01:00
|
|
|
newdata = (*alloc_func)(data);
|
|
|
|
if (newdata == NULL)
|
|
|
|
return NULL;
|
2017-06-19 16:22:26 +02:00
|
|
|
|
2017-08-09 17:57:13 +02:00
|
|
|
if (HASH_THRESHOLD(hash->count + 1, hash->size)) {
|
2013-01-11 19:25:26 +01:00
|
|
|
hash_expand(hash);
|
2017-06-19 16:22:26 +02:00
|
|
|
index = key & (hash->size - 1);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-06-19 16:22:26 +02:00
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
bucket = XCALLOC(MTYPE_HASH_BACKET, sizeof(struct hash_bucket));
|
|
|
|
bucket->data = newdata;
|
|
|
|
bucket->key = key;
|
|
|
|
bucket->next = hash->index[index];
|
|
|
|
hash->index[index] = bucket;
|
2002-12-13 21:15:29 +01:00
|
|
|
hash->count++;
|
2017-06-19 16:22:26 +02:00
|
|
|
|
2020-09-17 01:29:32 +02:00
|
|
|
tracepoint(frr_libfrr, hash_insert, hash, data, key);
|
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
int oldlen = bucket->next ? bucket->next->len : 0;
|
2017-06-19 16:22:26 +02:00
|
|
|
int newlen = oldlen + 1;
|
|
|
|
|
|
|
|
if (newlen == 1)
|
|
|
|
hash->stats.empty--;
|
2017-07-17 14:03:14 +02:00
|
|
|
else
|
2019-02-19 16:46:52 +01:00
|
|
|
bucket->next->len = 0;
|
2017-06-19 16:22:26 +02:00
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
bucket->len = newlen;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
hash_update_ssq(hash, oldlen, newlen);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
return bucket->data;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *hash_lookup(struct hash *hash, void *data)
|
|
|
|
{
|
|
|
|
return hash_get(hash, data, NULL);
|
|
|
|
}
|
|
|
|
|
2010-08-27 23:11:14 +02:00
|
|
|
unsigned int string_hash_make(const char *str)
|
|
|
|
{
|
|
|
|
unsigned int hash = 0;
|
|
|
|
|
|
|
|
while (*str)
|
|
|
|
hash = (hash * 33) ^ (unsigned int)*str++;
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
void *hash_release(struct hash *hash, void *data)
|
|
|
|
{
|
2020-09-15 00:04:33 +02:00
|
|
|
void *ret = NULL;
|
2002-12-13 21:15:29 +01:00
|
|
|
unsigned int key;
|
|
|
|
unsigned int index;
|
2019-02-19 16:46:52 +01:00
|
|
|
struct hash_bucket *bucket;
|
|
|
|
struct hash_bucket *pp;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
key = (*hash->hash_key)(data);
|
2013-01-04 23:29:21 +01:00
|
|
|
index = key & (hash->size - 1);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
for (bucket = pp = hash->index[index]; bucket; bucket = bucket->next) {
|
|
|
|
if (bucket->key == key
|
|
|
|
&& (*hash->hash_cmp)(bucket->data, data)) {
|
2017-06-19 16:22:26 +02:00
|
|
|
int oldlen = hash->index[index]->len;
|
|
|
|
int newlen = oldlen - 1;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
if (bucket == pp)
|
|
|
|
hash->index[index] = bucket->next;
|
2017-06-19 16:22:26 +02:00
|
|
|
else
|
2019-02-19 16:46:52 +01:00
|
|
|
pp->next = bucket->next;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
if (hash->index[index])
|
|
|
|
hash->index[index]->len = newlen;
|
|
|
|
else
|
|
|
|
hash->stats.empty++;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
hash_update_ssq(hash, oldlen, newlen);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
ret = bucket->data;
|
|
|
|
XFREE(MTYPE_HASH_BACKET, bucket);
|
2002-12-13 21:15:29 +01:00
|
|
|
hash->count--;
|
2020-09-15 00:04:33 +02:00
|
|
|
break;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2019-02-19 16:46:52 +01:00
|
|
|
pp = bucket;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
2020-09-15 00:04:33 +02:00
|
|
|
|
|
|
|
tracepoint(frr_libfrr, hash_release, hash, data, ret);
|
|
|
|
|
|
|
|
return ret;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
void hash_iterate(struct hash *hash, void (*func)(struct hash_bucket *, void *),
|
2002-12-13 21:15:29 +01:00
|
|
|
void *arg)
|
|
|
|
{
|
2004-10-05 23:01:23 +02:00
|
|
|
unsigned int i;
|
2019-02-19 16:46:52 +01:00
|
|
|
struct hash_bucket *hb;
|
|
|
|
struct hash_bucket *hbnext;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-07-06 16:18:11 +02:00
|
|
|
for (i = 0; i < hash->size; i++)
|
2004-08-31 19:28:41 +02:00
|
|
|
for (hb = hash->index[i]; hb; hb = hbnext) {
|
2019-02-19 16:46:52 +01:00
|
|
|
/* get pointer to next hash bucket here, in case (*func)
|
2004-08-31 19:28:41 +02:00
|
|
|
* decides to delete hb by calling hash_release
|
|
|
|
*/
|
|
|
|
hbnext = hb->next;
|
|
|
|
(*func)(hb, arg);
|
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
void hash_walk(struct hash *hash, int (*func)(struct hash_bucket *, void *),
|
2015-05-20 03:03:47 +02:00
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
2019-02-19 16:46:52 +01:00
|
|
|
struct hash_bucket *hb;
|
|
|
|
struct hash_bucket *hbnext;
|
2015-05-20 03:03:47 +02:00
|
|
|
int ret = HASHWALK_CONTINUE;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2015-05-20 03:03:47 +02:00
|
|
|
for (i = 0; i < hash->size; i++) {
|
|
|
|
for (hb = hash->index[i]; hb; hb = hbnext) {
|
2019-02-19 16:46:52 +01:00
|
|
|
/* get pointer to next hash bucket here, in case (*func)
|
2015-05-20 03:03:47 +02:00
|
|
|
* decides to delete hb by calling hash_release
|
|
|
|
*/
|
|
|
|
hbnext = hb->next;
|
|
|
|
ret = (*func)(hb, arg);
|
|
|
|
if (ret == HASHWALK_ABORT)
|
|
|
|
return;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2015-05-20 03:03:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
void hash_clean(struct hash *hash, void (*free_func)(void *))
|
|
|
|
{
|
2004-10-05 23:01:23 +02:00
|
|
|
unsigned int i;
|
2019-02-19 16:46:52 +01:00
|
|
|
struct hash_bucket *hb;
|
|
|
|
struct hash_bucket *next;
|
2002-12-13 21:15:29 +01:00
|
|
|
|
|
|
|
for (i = 0; i < hash->size; i++) {
|
|
|
|
for (hb = hash->index[i]; hb; hb = next) {
|
|
|
|
next = hb->next;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
if (free_func)
|
|
|
|
(*free_func)(hb->data);
|
|
|
|
|
|
|
|
XFREE(MTYPE_HASH_BACKET, hb);
|
|
|
|
hash->count--;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2002-12-13 21:15:29 +01:00
|
|
|
hash->index[i] = NULL;
|
|
|
|
}
|
2017-06-19 16:22:26 +02:00
|
|
|
|
|
|
|
hash->stats.ssq = 0;
|
|
|
|
hash->stats.empty = hash->size;
|
2002-12-13 21:15:29 +01:00
|
|
|
}
|
|
|
|
|
2019-02-19 16:46:52 +01:00
|
|
|
static void hash_to_list_iter(struct hash_bucket *hb, void *arg)
|
2018-05-24 17:44:54 +02:00
|
|
|
{
|
|
|
|
struct list *list = arg;
|
|
|
|
|
|
|
|
listnode_add(list, hb->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct list *hash_to_list(struct hash *hash)
|
|
|
|
{
|
|
|
|
struct list *list = list_new();
|
|
|
|
|
|
|
|
hash_iterate(hash, hash_to_list_iter, list);
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2002-12-13 21:15:29 +01:00
|
|
|
void hash_free(struct hash *hash)
|
|
|
|
{
|
2019-06-21 10:58:02 +02:00
|
|
|
frr_with_mutex(&_hashes_mtx) {
|
2017-06-19 16:22:26 +02:00
|
|
|
if (_hashes) {
|
|
|
|
listnode_delete(_hashes, hash);
|
|
|
|
if (_hashes->count == 0) {
|
2018-10-02 11:39:51 +02:00
|
|
|
list_delete(&_hashes);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-06-19 16:22:26 +02:00
|
|
|
}
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-02-25 21:18:13 +01:00
|
|
|
XFREE(MTYPE_HASH, hash->name);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
XFREE(MTYPE_HASH_INDEX, hash->index);
|
|
|
|
XFREE(MTYPE_HASH, hash);
|
2017-05-30 02:16:52 +02:00
|
|
|
}
|
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
|
|
|
|
/* CLI commands ------------------------------------------------------------ */
|
2017-05-30 02:16:52 +02:00
|
|
|
|
2017-08-21 14:34:31 +02:00
|
|
|
DEFUN_NOSH(show_hash_stats,
|
|
|
|
show_hash_stats_cmd,
|
|
|
|
"show debugging hashtable [statistics]",
|
|
|
|
SHOW_STR
|
|
|
|
DEBUG_STR
|
|
|
|
"Statistics about hash tables\n"
|
|
|
|
"Statistics about hash tables\n")
|
2017-05-30 02:16:52 +02:00
|
|
|
{
|
|
|
|
struct hash *h;
|
|
|
|
struct listnode *ln;
|
|
|
|
struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-23 07:14:54 +02:00
|
|
|
ttable_add_row(tt, "Hash table|Buckets|Entries|Empty|LF|SD|FLF|SD");
|
2017-06-19 16:22:26 +02:00
|
|
|
tt->style.cell.lpad = 2;
|
|
|
|
tt->style.cell.rpad = 1;
|
|
|
|
tt->style.corner = '+';
|
2017-05-30 02:16:52 +02:00
|
|
|
ttable_restyle(tt);
|
|
|
|
ttable_rowseps(tt, 0, BOTTOM, true, '-');
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
/* Summary statistics calculated are:
|
|
|
|
*
|
2017-08-03 14:30:01 +02:00
|
|
|
* - 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.
|
2017-06-19 16:22:26 +02:00
|
|
|
*
|
|
|
|
* - Full load factor: this is the number of elements in the table
|
2017-08-03 14:30:01 +02:00
|
|
|
* divided by the number of buckets that have some elements in them.
|
2017-06-19 16:22:26 +02:00
|
|
|
*
|
2017-06-23 07:14:54 +02:00
|
|
|
* - Std. Dev.: This is the standard deviation calculated from the
|
2017-08-03 14:30:01 +02:00
|
|
|
* 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.
|
2017-06-19 16:22:26 +02:00
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-23 07:14:54 +02:00
|
|
|
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
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
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
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
pthread_mutex_lock(&_hashes_mtx);
|
2017-07-11 14:52:06 +02:00
|
|
|
if (!_hashes) {
|
|
|
|
pthread_mutex_unlock(&_hashes_mtx);
|
2017-10-23 01:14:21 +02:00
|
|
|
ttable_del(tt);
|
2017-07-14 13:09:47 +02:00
|
|
|
vty_out(vty, "No hash tables in use.\n");
|
2017-07-11 14:52:06 +02:00
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-05-30 02:16:52 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(_hashes, ln, h)) {
|
2017-06-19 16:22:26 +02:00
|
|
|
if (!h->name)
|
2017-05-30 02:16:52 +02:00
|
|
|
continue;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
ssq = (long double)h->stats.ssq;
|
2017-08-04 16:58:00 +02:00
|
|
|
x2 = h->count * h->count;
|
2017-06-19 16:22:26 +02:00
|
|
|
ldc = (long double)h->count;
|
|
|
|
full = h->size - h->stats.empty;
|
|
|
|
lf = h->count / (double)h->size;
|
|
|
|
flf = full ? h->count / (double)(full) : 0;
|
2017-06-23 07:14:54 +02:00
|
|
|
var = ldc ? (1.0 / ldc) * (ssq - x2 / ldc) : 0;
|
|
|
|
fvar = full ? (1.0 / full) * (ssq - x2 / full) : 0;
|
2017-06-19 16:22:26 +02:00
|
|
|
var = (var < .0001) ? 0 : var;
|
|
|
|
fvar = (fvar < .0001) ? 0 : fvar;
|
2017-06-23 07:14:54 +02:00
|
|
|
stdv = sqrt(var);
|
|
|
|
fstdv = sqrt(fvar);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-23 07:14:54 +02:00
|
|
|
ttable_add_row(tt, "%s|%d|%ld|%.0f%%|%.2lf|%.2lf|%.2lf|%.2lf",
|
2017-06-19 16:22:26 +02:00
|
|
|
h->name, h->size, h->count,
|
2017-06-23 07:14:54 +02:00
|
|
|
(h->stats.empty / (double)h->size) * 100, lf,
|
|
|
|
stdv, flf, fstdv);
|
2017-06-19 16:22:26 +02:00
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&_hashes_mtx);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
/* 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';
|
2017-07-13 17:49:13 +02:00
|
|
|
vty_out(vty, "%s%s\n", header, frr_protonameinst);
|
|
|
|
vty_out(vty, "%s\n", underln);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-13 17:49:13 +02:00
|
|
|
vty_out(vty, "# allocated: %d\n", _hashes->count);
|
2017-07-13 19:42:42 +02:00
|
|
|
vty_out(vty, "# named: %d\n\n", tt->nrows - 1);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-06-19 16:22:26 +02:00
|
|
|
if (tt->nrows > 1) {
|
|
|
|
ttable_colseps(tt, 0, RIGHT, true, '|');
|
2017-07-13 20:17:06 +02:00
|
|
|
char *table = ttable_dump(tt, "\n");
|
2017-07-13 19:42:42 +02:00
|
|
|
vty_out(vty, "%s\n", table);
|
2017-06-19 16:22:26 +02:00
|
|
|
XFREE(MTYPE_TMP, table);
|
|
|
|
} else
|
2017-07-13 17:49:13 +02:00
|
|
|
vty_out(vty, "No named hash tables to display.\n");
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-05-30 02:16:52 +02:00
|
|
|
ttable_del(tt);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-05-30 02:16:52 +02:00
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2019-01-24 10:12:36 +01:00
|
|
|
void hash_cmd_init(void)
|
2017-05-30 02:16:52 +02:00
|
|
|
{
|
|
|
|
install_element(ENABLE_NODE, &show_hash_stats_cmd);
|
|
|
|
}
|