]> git.proxmox.com Git - mirror_frr.git/blame - lib/hash.c
bgpd: route processing tracepoints
[mirror_frr.git] / lib / hash.c
CommitLineData
718e3744 1/* Hash routine.
2 * Copyright (C) 1998 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2, or (at your
9 * option) any later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
896014f4
DL
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
718e3744 19 */
20
21#include <zebra.h>
4db0cff1 22#include <math.h>
718e3744 23
24#include "hash.h"
25#include "memory.h"
4db0cff1
QY
26#include "linklist.h"
27#include "termtable.h"
28#include "vty.h"
29#include "command.h"
6f6f0010 30#include "libfrr.h"
00dffa8c 31#include "frr_pthread.h"
abf96a87 32#include "trace.h"
718e3744 33
eaf58ba9
DL
34DEFINE_MTYPE_STATIC(LIB, HASH, "Hash")
35DEFINE_MTYPE_STATIC(LIB, HASH_BACKET, "Hash Bucket")
d62a17ae 36DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index")
4a1ab8e4 37
c17faa4b 38static pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER;
4db0cff1
QY
39static struct list *_hashes;
40
d62a17ae 41struct hash *hash_create_size(unsigned int size,
d8b87afe 42 unsigned int (*hash_key)(const void *),
74df8d6d 43 bool (*hash_cmp)(const void *, const void *),
d62a17ae 44 const char *name)
718e3744 45{
d62a17ae 46 struct hash *hash;
47
48 assert((size & (size - 1)) == 0);
49 hash = XCALLOC(MTYPE_HASH, sizeof(struct hash));
50 hash->index =
e3b78da8 51 XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_bucket *) * size);
d62a17ae 52 hash->size = size;
d62a17ae 53 hash->hash_key = hash_key;
54 hash->hash_cmp = hash_cmp;
55 hash->count = 0;
56 hash->name = name ? XSTRDUP(MTYPE_HASH, name) : NULL;
57 hash->stats.empty = hash->size;
58
00dffa8c 59 frr_with_mutex(&_hashes_mtx) {
d62a17ae 60 if (!_hashes)
61 _hashes = list_new();
62
63 listnode_add(_hashes, hash);
64 }
d62a17ae 65
66 return hash;
718e3744 67}
68
d8b87afe 69struct hash *hash_create(unsigned int (*hash_key)(const void *),
74df8d6d 70 bool (*hash_cmp)(const void *, const void *),
d62a17ae 71 const char *name)
718e3744 72{
d62a17ae 73 return hash_create_size(HASH_INITIAL_SIZE, hash_key, hash_cmp, name);
718e3744 74}
75
d62a17ae 76void *hash_alloc_intern(void *arg)
718e3744 77{
d62a17ae 78 return arg;
718e3744 79}
80
e6464fdc
QY
81/*
82 * ssq = ssq + (new^2 - old^2)
83 * = ssq + ((new + old) * (new - old))
84 */
d62a17ae 85#define hash_update_ssq(hz, old, new) \
e6464fdc
QY
86 do { \
87 int _adjust = (new + old) * (new - old); \
88 if (_adjust < 0) \
89 atomic_fetch_sub_explicit(&hz->stats.ssq, -_adjust, \
90 memory_order_relaxed); \
91 else \
92 atomic_fetch_add_explicit(&hz->stats.ssq, _adjust, \
93 memory_order_relaxed); \
94 } while (0)
6f6f0010 95
97c84db0 96/* Expand hash if the chain length exceeds the threshold. */
d62a17ae 97static void hash_expand(struct hash *hash)
97c84db0 98{
8b2a6222 99 unsigned int i, new_size;
e3b78da8 100 struct hash_bucket *hb, *hbnext, **new_index;
97c84db0 101
d62a17ae 102 new_size = hash->size * 2;
40520c36
DW
103
104 if (hash->max_size && new_size > hash->max_size)
105 return;
106
d62a17ae 107 new_index = XCALLOC(MTYPE_HASH_INDEX,
e3b78da8 108 sizeof(struct hash_bucket *) * new_size);
97c84db0 109
d62a17ae 110 hash->stats.empty = new_size;
6f6f0010 111
d62a17ae 112 for (i = 0; i < hash->size; i++)
113 for (hb = hash->index[i]; hb; hb = hbnext) {
114 unsigned int h = hb->key & (new_size - 1);
97c84db0 115
d62a17ae 116 hbnext = hb->next;
117 hb->next = new_index[h];
6f6f0010 118
d62a17ae 119 int oldlen = hb->next ? hb->next->len : 0;
120 int newlen = oldlen + 1;
6f6f0010 121
d62a17ae 122 if (newlen == 1)
123 hash->stats.empty--;
124 else
125 hb->next->len = 0;
6f6f0010 126
d62a17ae 127 hb->len = newlen;
6f6f0010 128
d62a17ae 129 hash_update_ssq(hash, oldlen, newlen);
6f6f0010 130
d62a17ae 131 new_index[h] = hb;
132 }
97c84db0 133
d62a17ae 134 /* Switch to new table */
135 XFREE(MTYPE_HASH_INDEX, hash->index);
136 hash->size = new_size;
137 hash->index = new_index;
97c84db0
SH
138}
139
d62a17ae 140void *hash_get(struct hash *hash, void *data, void *(*alloc_func)(void *))
718e3744 141{
abf96a87
QY
142 tracepoint(frr_libfrr, hash_get, hash, data);
143
d62a17ae 144 unsigned int key;
145 unsigned int index;
146 void *newdata;
e3b78da8 147 struct hash_bucket *bucket;
d62a17ae 148
efb149d9
DS
149 if (!alloc_func && !hash->count)
150 return NULL;
151
d62a17ae 152 key = (*hash->hash_key)(data);
153 index = key & (hash->size - 1);
d62a17ae 154
e3b78da8
TB
155 for (bucket = hash->index[index]; bucket != NULL;
156 bucket = bucket->next) {
157 if (bucket->key == key && (*hash->hash_cmp)(bucket->data, data))
158 return bucket->data;
97c84db0
SH
159 }
160
d62a17ae 161 if (alloc_func) {
162 newdata = (*alloc_func)(data);
163 if (newdata == NULL)
164 return NULL;
6f6f0010 165
bed7ad83 166 if (HASH_THRESHOLD(hash->count + 1, hash->size)) {
d62a17ae 167 hash_expand(hash);
168 index = key & (hash->size - 1);
169 }
6f6f0010 170
e3b78da8
TB
171 bucket = XCALLOC(MTYPE_HASH_BACKET, sizeof(struct hash_bucket));
172 bucket->data = newdata;
173 bucket->key = key;
174 bucket->next = hash->index[index];
175 hash->index[index] = bucket;
d62a17ae 176 hash->count++;
6f6f0010 177
e3b78da8 178 int oldlen = bucket->next ? bucket->next->len : 0;
d62a17ae 179 int newlen = oldlen + 1;
6f6f0010 180
d62a17ae 181 if (newlen == 1)
182 hash->stats.empty--;
183 else
e3b78da8 184 bucket->next->len = 0;
6f6f0010 185
e3b78da8 186 bucket->len = newlen;
d62a17ae 187
188 hash_update_ssq(hash, oldlen, newlen);
189
e3b78da8 190 return bucket->data;
d62a17ae 191 }
192 return NULL;
718e3744 193}
194
d62a17ae 195void *hash_lookup(struct hash *hash, void *data)
718e3744 196{
d62a17ae 197 return hash_get(hash, data, NULL);
718e3744 198}
199
d62a17ae 200unsigned int string_hash_make(const char *str)
6392aa83 201{
d62a17ae 202 unsigned int hash = 0;
6392aa83 203
d62a17ae 204 while (*str)
205 hash = (hash * 33) ^ (unsigned int)*str++;
6392aa83 206
d62a17ae 207 return hash;
6392aa83
SH
208}
209
d62a17ae 210void *hash_release(struct hash *hash, void *data)
718e3744 211{
abf96a87 212 void *ret = NULL;
d62a17ae 213 unsigned int key;
214 unsigned int index;
e3b78da8
TB
215 struct hash_bucket *bucket;
216 struct hash_bucket *pp;
d62a17ae 217
218 key = (*hash->hash_key)(data);
219 index = key & (hash->size - 1);
220
e3b78da8
TB
221 for (bucket = pp = hash->index[index]; bucket; bucket = bucket->next) {
222 if (bucket->key == key
223 && (*hash->hash_cmp)(bucket->data, data)) {
d62a17ae 224 int oldlen = hash->index[index]->len;
225 int newlen = oldlen - 1;
226
e3b78da8
TB
227 if (bucket == pp)
228 hash->index[index] = bucket->next;
d62a17ae 229 else
e3b78da8 230 pp->next = bucket->next;
d62a17ae 231
232 if (hash->index[index])
233 hash->index[index]->len = newlen;
234 else
235 hash->stats.empty++;
236
237 hash_update_ssq(hash, oldlen, newlen);
238
e3b78da8
TB
239 ret = bucket->data;
240 XFREE(MTYPE_HASH_BACKET, bucket);
d62a17ae 241 hash->count--;
abf96a87 242 break;
d62a17ae 243 }
e3b78da8 244 pp = bucket;
718e3744 245 }
abf96a87
QY
246
247 tracepoint(frr_libfrr, hash_release, hash, data, ret);
248
249 return ret;
718e3744 250}
251
e3b78da8 252void hash_iterate(struct hash *hash, void (*func)(struct hash_bucket *, void *),
d62a17ae 253 void *arg)
718e3744 254{
d62a17ae 255 unsigned int i;
e3b78da8
TB
256 struct hash_bucket *hb;
257 struct hash_bucket *hbnext;
d62a17ae 258
df66eb2e 259 for (i = 0; i < hash->size; i++)
d62a17ae 260 for (hb = hash->index[i]; hb; hb = hbnext) {
e3b78da8 261 /* get pointer to next hash bucket here, in case (*func)
d62a17ae 262 * decides to delete hb by calling hash_release
263 */
264 hbnext = hb->next;
265 (*func)(hb, arg);
266 }
718e3744 267}
268
e3b78da8 269void hash_walk(struct hash *hash, int (*func)(struct hash_bucket *, void *),
d62a17ae 270 void *arg)
3f9c7369 271{
d62a17ae 272 unsigned int i;
e3b78da8
TB
273 struct hash_bucket *hb;
274 struct hash_bucket *hbnext;
d62a17ae 275 int ret = HASHWALK_CONTINUE;
276
277 for (i = 0; i < hash->size; i++) {
278 for (hb = hash->index[i]; hb; hb = hbnext) {
e3b78da8 279 /* get pointer to next hash bucket here, in case (*func)
d62a17ae 280 * decides to delete hb by calling hash_release
281 */
282 hbnext = hb->next;
283 ret = (*func)(hb, arg);
284 if (ret == HASHWALK_ABORT)
285 return;
286 }
3f9c7369 287 }
3f9c7369
DS
288}
289
d62a17ae 290void hash_clean(struct hash *hash, void (*free_func)(void *))
718e3744 291{
d62a17ae 292 unsigned int i;
e3b78da8
TB
293 struct hash_bucket *hb;
294 struct hash_bucket *next;
718e3744 295
d62a17ae 296 for (i = 0; i < hash->size; i++) {
297 for (hb = hash->index[i]; hb; hb = next) {
298 next = hb->next;
299
300 if (free_func)
301 (*free_func)(hb->data);
718e3744 302
d62a17ae 303 XFREE(MTYPE_HASH_BACKET, hb);
304 hash->count--;
305 }
306 hash->index[i] = NULL;
718e3744 307 }
6f6f0010 308
d62a17ae 309 hash->stats.ssq = 0;
310 hash->stats.empty = hash->size;
718e3744 311}
312
e3b78da8 313static void hash_to_list_iter(struct hash_bucket *hb, void *arg)
91f10370
QY
314{
315 struct list *list = arg;
316
317 listnode_add(list, hb->data);
318}
319
320struct list *hash_to_list(struct hash *hash)
321{
322 struct list *list = list_new();
323
324 hash_iterate(hash, hash_to_list_iter, list);
325 return list;
326}
327
d62a17ae 328void hash_free(struct hash *hash)
718e3744 329{
00dffa8c 330 frr_with_mutex(&_hashes_mtx) {
d62a17ae 331 if (_hashes) {
332 listnode_delete(_hashes, hash);
333 if (_hashes->count == 0) {
6a154c88 334 list_delete(&_hashes);
d62a17ae 335 }
336 }
337 }
d62a17ae 338
0a22ddfb 339 XFREE(MTYPE_HASH, hash->name);
d62a17ae 340
341 XFREE(MTYPE_HASH_INDEX, hash->index);
342 XFREE(MTYPE_HASH, hash);
4db0cff1
QY
343}
344
6f6f0010
QY
345
346/* CLI commands ------------------------------------------------------------ */
4db0cff1 347
40818cec
DL
348DEFUN_NOSH(show_hash_stats,
349 show_hash_stats_cmd,
350 "show debugging hashtable [statistics]",
351 SHOW_STR
352 DEBUG_STR
353 "Statistics about hash tables\n"
354 "Statistics about hash tables\n")
4db0cff1 355{
d62a17ae 356 struct hash *h;
357 struct listnode *ln;
358 struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
359
360 ttable_add_row(tt, "Hash table|Buckets|Entries|Empty|LF|SD|FLF|SD");
361 tt->style.cell.lpad = 2;
362 tt->style.cell.rpad = 1;
363 tt->style.corner = '+';
364 ttable_restyle(tt);
365 ttable_rowseps(tt, 0, BOTTOM, true, '-');
366
367 /* Summary statistics calculated are:
368 *
c7d3895e
CF
369 * - Load factor: This is the number of elements in the table divided
370 * by the number of buckets. Since this hash table implementation
371 * uses chaining, this value can be greater than 1.
372 * This number provides information on how 'full' the table is, but
373 * does not provide information on how evenly distributed the
374 * elements are.
375 * Notably, a load factor >= 1 does not imply that every bucket has
376 * an element; with a pathological hash function, all elements could
377 * be in a single bucket.
d62a17ae 378 *
379 * - Full load factor: this is the number of elements in the table
c7d3895e 380 * divided by the number of buckets that have some elements in them.
d62a17ae 381 *
382 * - Std. Dev.: This is the standard deviation calculated from the
c7d3895e
CF
383 * relevant load factor. If the load factor is the mean of number of
384 * elements per bucket, the standard deviation measures how much any
385 * particular bucket is likely to deviate from the mean.
386 * As a rule of thumb this number should be less than 2, and ideally
387 * <= 1 for optimal performance. A number larger than 3 generally
388 * indicates a poor hash function.
d62a17ae 389 */
390
391 double lf; // load factor
392 double flf; // full load factor
393 double var; // overall variance
394 double fvar; // full variance
395 double stdv; // overall stddev
396 double fstdv; // full stddev
397
398 long double x2; // h->count ^ 2
399 long double ldc; // (long double) h->count
400 long double full; // h->size - h->stats.empty
401 long double ssq; // ssq casted to long double
402
403 pthread_mutex_lock(&_hashes_mtx);
404 if (!_hashes) {
405 pthread_mutex_unlock(&_hashes_mtx);
44f12f20 406 ttable_del(tt);
d62a17ae 407 vty_out(vty, "No hash tables in use.\n");
408 return CMD_SUCCESS;
409 }
410
411 for (ALL_LIST_ELEMENTS_RO(_hashes, ln, h)) {
412 if (!h->name)
413 continue;
414
415 ssq = (long double)h->stats.ssq;
61b9e9d6 416 x2 = h->count * h->count;
d62a17ae 417 ldc = (long double)h->count;
418 full = h->size - h->stats.empty;
419 lf = h->count / (double)h->size;
420 flf = full ? h->count / (double)(full) : 0;
421 var = ldc ? (1.0 / ldc) * (ssq - x2 / ldc) : 0;
422 fvar = full ? (1.0 / full) * (ssq - x2 / full) : 0;
423 var = (var < .0001) ? 0 : var;
424 fvar = (fvar < .0001) ? 0 : fvar;
425 stdv = sqrt(var);
426 fstdv = sqrt(fvar);
427
428 ttable_add_row(tt, "%s|%d|%ld|%.0f%%|%.2lf|%.2lf|%.2lf|%.2lf",
429 h->name, h->size, h->count,
430 (h->stats.empty / (double)h->size) * 100, lf,
431 stdv, flf, fstdv);
432 }
433 pthread_mutex_unlock(&_hashes_mtx);
434
435 /* display header */
436 char header[] = "Showing hash table statistics for ";
437 char underln[sizeof(header) + strlen(frr_protonameinst)];
438 memset(underln, '-', sizeof(underln));
439 underln[sizeof(underln) - 1] = '\0';
440 vty_out(vty, "%s%s\n", header, frr_protonameinst);
441 vty_out(vty, "%s\n", underln);
442
443 vty_out(vty, "# allocated: %d\n", _hashes->count);
444 vty_out(vty, "# named: %d\n\n", tt->nrows - 1);
445
446 if (tt->nrows > 1) {
447 ttable_colseps(tt, 0, RIGHT, true, '|');
448 char *table = ttable_dump(tt, "\n");
449 vty_out(vty, "%s\n", table);
450 XFREE(MTYPE_TMP, table);
451 } else
452 vty_out(vty, "No named hash tables to display.\n");
453
454 ttable_del(tt);
455
456 return CMD_SUCCESS;
4db0cff1
QY
457}
458
4d762f26 459void hash_cmd_init(void)
4db0cff1 460{
d62a17ae 461 install_element(ENABLE_NODE, &show_hash_stats_cmd);
4db0cff1 462}