hash_tbl.c (15971B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: hash_tbl.c 8 * 9 * Overview: 10 * Provides a generic hash table interface with configurable and default hash 11 * and key compare functions. The interface allows for complex key types, but 12 * the caller must implement a _cast_ method to transform the complex type into 13 * a string of bytes for the hash function. Alternatively, the caller may 14 * replace the hash function itself, in which case, the default _cast_ method 15 * may be used to return the complex key and complex key size. 16 * 17 * Memory is allocated on-demand in blocks for hash entries, and freed in 18 * blocks when the free pool becomes large. 19 * 20 * Collisions are handled simply by creating a linked list per hash index and 21 * a linear search is performed within the list to find an entry. The list is 22 * not sorted. (future upgrade?) 23 * 24 * The caller defined KEY is *copied* and stored in the variable sized hash 25 * table entry for comparison. A *pointer* to the caller defined DATA is 26 * stored. The caller is reponsible for managing the memory where DATA points 27 * if any. Callbacks are provided upon hash destruction to free any allocated 28 * memory. The hash table module itself does not explitly free any DATA 29 * pointer at any time. 30 */ 31 32 #include <shared/bsl.h> 33 34 #include <shared/hash_tbl.h> 35 #include <shared/error.h> 36 37 #include <sal/core/libc.h> 38 #include <shared/alloc.h> 39 #include <shared/util.h> 40 #include <sal/core/sync.h> 41 42 #include <soc/cm.h> 43 44 /* allocate this many blocks of hash entries when the free pool is dry */ 45 #define HTB_DEFAULT_ALLOC_BLK_CNT (16) 46 47 #define HTBL_LOCK(p) \ 48 sal_mutex_take((p)->lock, sal_mutex_FOREVER) 49 50 #define HTBL_UNLOCK(p) \ 51 sal_mutex_give((p)->lock) 52 53 54 #if 0 55 void _dbg_dump_list_recr(_hash_entry_t *entry) 56 { 57 if (entry == NULL) { 58 LOG_CLI((BSL_META("(NULL)\n"))); 59 } 60 else { 61 LOG_CLI((BSL_META("(0x%08x)->"), entry)); 62 _dbg_dump_list_recr(entry->next); 63 } 64 } 65 66 void 67 _dbg_dump_list(shr_htb_hash_table_t ht, int hash_idx) 68 { 69 _hash_entry_t *entry = ht->table[hash_idx]; 70 71 LOG_CLI((BSL_META("h_tbl[%4d]="), hash_idx)); 72 _dbg_dump_list_recr(entry); 73 } 74 #endif 75 76 77 /* 78 * Function: 79 * htb_default_hash_f 80 * Purpose: 81 * Generic default hash function. 82 * Parameters: 83 * (in) k - key as a string of bytes 84 * (in) length - Length of key in bytes 85 * Returns: 86 * Hash index for the given key. 87 */ 88 uint32 89 htb_default_hash_f(uint8 *k, uint32 length) 90 { 91 return _shr_crc32(~0, k, length); 92 } 93 94 /* 95 * Function: 96 * htb_default_key_cmp_f 97 * Purpose: 98 * Generic default key compare function; bit for bit compare of 99 * two keys of size key_size set at table creation time. 100 * Parameters: 101 * (in) a - Key A. 102 * (in) b - Key B 103 * (in) size - Size of key for this hash table. 104 * Returns: 105 * 0 if a==b; 106 * non-zero if a!=b 107 * Notes: 108 */ 109 int 110 htb_default_key_cmp_f(shr_htb_key_t a, 111 shr_htb_key_t b, 112 uint32 size) 113 { 114 return sal_memcmp(a, b, size); 115 } 116 117 118 /* 119 * Function: 120 * htb_default_cast_key_f 121 * Purpose: 122 * Default routine for casting the caller defined key to a string of bytes 123 * Parameters: 124 * (in) key - key to cast 125 * (out) key_bytes - string of bytes representing key 126 * (out) key_size - length of key_bytes 127 * Returns: 128 * none 129 * Notes: 130 * The default cast is a no-op 131 */ 132 void 133 htb_default_cast_key_f(shr_htb_key_t key, uint8 **key_bytes, uint32 *key_size) 134 { 135 /* do nothing */ 136 } 137 138 /* 139 * Function: 140 * _htb_free_list_push 141 * Purpose: 142 * Internal routine; add an hash entry to the free list 143 * Parameters: 144 * (in) ht - hash table to operate 145 * (in) entry - entry to add to the list 146 * Returns: 147 * none 148 * Notes: 149 */ 150 STATIC void 151 _htb_free_list_push(shr_htb_hash_table_t ht, _hash_entry_t *entry) 152 { 153 ht->num_free++; 154 entry->next = ht->free_list; 155 ht->free_list = entry; 156 } 157 158 159 /* 160 * Function: 161 * _htb_free_list_pop 162 * Purpose: 163 * Internal support routine; grab an entry from the free list. 164 * Parameters: 165 * (in) ht - Hash table to operate 166 * Returns: 167 * allocated hash entry 168 * Notes: 169 */ 170 STATIC _hash_entry_t * 171 _htb_free_list_pop(shr_htb_hash_table_t ht) 172 { 173 _hash_entry_t *entry; 174 175 /* alloc really shd be checking this... */ 176 if (ht->num_free == 0) { 177 return NULL; 178 } 179 180 ht->num_free--; 181 entry = ht->free_list; 182 183 ht->free_list = ht->free_list->next; 184 185 return entry; 186 } 187 188 189 /* 190 * Function: 191 * _htb_entry_free 192 * Purpose: 193 * Internal support routine; free a hash table entry 194 * Parameters: 195 * (in) ht - hash table to operate 196 * (out) entry - entry to free 197 * Returns: 198 * none 199 * Notes: 200 * When the free list becomes excessively large (2 * alloc_blk_cnt), 201 * alloc_blk_cnt are removed from the free list and returned to the 202 * system. 203 */ 204 STATIC void 205 _htb_entry_free(shr_htb_hash_table_t ht, _hash_entry_t **entry) 206 { 207 _htb_free_list_push(ht, *entry); 208 *entry = NULL; 209 210 if (ht->num_free > (2 * ht->alloc_blk_cnt)) { 211 int i; 212 _hash_entry_t *free_entry; 213 214 for (i=0; i<ht->alloc_blk_cnt; i++) { 215 free_entry = _htb_free_list_pop(ht); 216 sal_free(free_entry); 217 } 218 } 219 } 220 221 222 /* 223 * Function: 224 * _htb_entry_alloc 225 * Purpose: 226 * Internal support routine; allocate a hash table entry 227 * Parameters: 228 * (in) ht - hash table to operate 229 * Returns: 230 * NULL if out of memory, else ptr to hash table entry 231 * Notes: 232 * If the free list runs dry, this function will allocate 233 * another block of entries to the pool 234 * Assumes mutex lock has been obtained before called 235 */ 236 STATIC _hash_entry_t* 237 _htb_entry_alloc(shr_htb_hash_table_t ht) 238 { 239 _hash_entry_t *entry = NULL; 240 int entry_size = (sizeof(_hash_entry_t) - 241 sizeof(shr_htb_key_t) + ht->key_size); 242 243 /* if the free list is empty, allocate a block of entries to the pool */ 244 if (ht->num_free == 0) { 245 246 int i; 247 for (i=0; i < ht->alloc_blk_cnt; i++) { 248 entry = sal_alloc(entry_size, "htb_entry"); 249 if (entry == NULL) { 250 return NULL; 251 } 252 253 _htb_free_list_push(ht, entry); 254 } 255 } 256 257 /* free list must be non-zero here. Pop a free entry & return */ 258 entry = _htb_free_list_pop(ht); 259 260 if (entry) { 261 sal_memset(entry, 0, entry_size); 262 } 263 264 return entry; 265 } 266 267 /* 268 * Function: 269 * _htb_find 270 * Purpose: 271 * Internal support routine; find an entry in the table 272 * Parameters: 273 * (in) ht - hash table to operate 274 * (in) key - key to find 275 * (out) hash_index - hash index of key found 276 * (out) entry - ptr to entry found 277 * (out) prev - ptr to entry before <entry> in chain 278 * Returns: 279 * BCM_E_NONE - if key is found in ht, else 280 * BCM_E_NOT_FOUND 281 * Notes: 282 * Assumes mutex lock has been taken before called. 283 */ 284 STATIC int 285 _htb_find(shr_htb_hash_table_t ht, shr_htb_key_t key, 286 uint32 *hash_idx, _hash_entry_t **entry, _hash_entry_t **prev) 287 { 288 /* compute the hash idx, ensure the index returned is in bounds */ 289 uint32 key_size = ht->key_size; 290 uint8 *key_bytes = key; 291 292 ht->cast_key_f(key, &key_bytes, &key_size); 293 294 *hash_idx = ht->hash_f(key_bytes, key_size); 295 *hash_idx &= ht->max_num_entries - 1; 296 297 *prev = *entry = ht->table[*hash_idx]; 298 299 /* linear search the bucket, compare the entire key, not the caller 300 * casted byte string version 301 */ 302 while (*entry) { 303 if (ht->key_cmp_f(key, &(*entry)->key, key_size) == 0) { 304 break; 305 } 306 *prev = *entry; 307 *entry = (*entry)->next; 308 } 309 310 if (*entry == NULL) { 311 return _SHR_E_NOT_FOUND; 312 } 313 return _SHR_E_NONE; 314 } 315 316 317 /* 318 * Function: 319 * shr_htb_find 320 * Purpose: 321 * Find an entry with a key in a table, remove if requested. 322 * Parameters: 323 * (in) ht - hash table to operate 324 * (in) key - key to find 325 * (out) data - data stored at found key location 326 * (in) remove - 0 - keeps entry, non-zero removes from table. 327 * Returns: 328 * BCM_E_NONE - if found 329 * BCM_E_* 330 * Notes: 331 */ 332 int 333 shr_htb_find(shr_htb_hash_table_t ht, shr_htb_key_t key, shr_htb_data_t *data, 334 int remove) 335 { 336 uint32 hash_idx; 337 _hash_entry_t *entry, *prev; 338 int rv; 339 340 HTBL_LOCK(ht); 341 342 rv = _htb_find(ht, key, &hash_idx, &entry, &prev); 343 344 if (rv) { 345 HTBL_UNLOCK(ht); 346 return rv; 347 } 348 349 *data = entry->data; 350 351 if (remove) { 352 /* entry at head of list */ 353 if (entry == ht->table[hash_idx]) { 354 ht->table[hash_idx] = entry->next; 355 356 } else { 357 prev->next = entry->next; 358 } 359 360 /* return the entry to the free list */ 361 _htb_entry_free(ht, &entry); 362 } 363 364 HTBL_UNLOCK(ht); 365 return _SHR_E_NONE; 366 } 367 368 /* 369 * Function: 370 * shr_htb_insert 371 * Purpose: 372 * Insert an entry into the hash table 373 * Parameters: 374 * (in) ht - hash table to operate 375 * (in) key - key used to insert 376 * (in) data - data stored at key's location 377 * Returns: 378 * BCM_E_NONE, upon success 379 * BCM_E_* on error 380 * Notes: 381 * The same key may not be inserted twice. 382 */ 383 int 384 shr_htb_insert(shr_htb_hash_table_t ht, shr_htb_key_t key, shr_htb_data_t data) 385 { 386 uint32 hash_idx; 387 _hash_entry_t *entry, *ignore; 388 int rv = _SHR_E_NONE; 389 390 HTBL_LOCK(ht); 391 392 /* Check to see if the entry already exists, 393 * We'll consider this an error 394 */ 395 rv = _htb_find(ht, key, &hash_idx, &entry, &ignore); 396 397 if (rv == _SHR_E_NONE) { 398 rv = _SHR_E_EXISTS; 399 goto exit; 400 } 401 rv = _SHR_E_NONE; 402 403 /* any collisions? Find the tail of the bucket */ 404 while (entry) { 405 entry = entry->next; 406 } 407 408 /* alloc the entry and save the key & data */ 409 entry = _htb_entry_alloc(ht); 410 if (entry == NULL) { 411 rv = _SHR_E_MEMORY; 412 goto exit; 413 } 414 415 entry->data = data; 416 sal_memcpy(&entry->key, key, ht->key_size); 417 418 /* Add the entry to the list, if there's a collision, just add it to the 419 * head of the list - it's not sorted 420 */ 421 if(ht->table[hash_idx]) { 422 /* collision */ 423 entry->next = ht->table[hash_idx]; 424 ht->table[hash_idx] = entry; 425 } else { 426 ht->table[hash_idx] = entry; 427 } 428 429 #if 0 430 _dbg_dump_list(ht, hash_idx); 431 #endif 432 433 exit: 434 HTBL_UNLOCK(ht); 435 436 return rv; 437 } 438 439 440 /* 441 * Function: 442 * shr_htb_create 443 * Purpose: 444 * Create and initialize a hash table. 445 * Parameters: 446 * (out) ht - hash table to operate 447 * (in) max_num_entries - maximum number of hash table entries, power of 2 448 * (in) key_size - size of keys used in hash function 449 * (in) tbl_name - name of table 450 * Returns: 451 * BCM_E_NONE upon success 452 * BCM_E_* on failure 453 * Notes: 454 */ 455 int 456 shr_htb_create(shr_htb_hash_table_t *ht, int max_num_entries, int key_size, 457 char* tbl_name) 458 { 459 int rv = _SHR_E_NONE; 460 int table_mem_size; 461 shr_htb_hash_table_t prv_ht; 462 463 /* table size must be a power of 2 */ 464 if((max_num_entries & (max_num_entries - 1)) != 0) { 465 return _SHR_E_PARAM; 466 } 467 468 prv_ht = sal_alloc(sizeof(struct hash_table_s), "_hash_tbl_"); 469 if (prv_ht == NULL) { 470 return _SHR_E_MEMORY; 471 } 472 sal_memset(prv_ht, 0, sizeof(struct hash_table_s)); 473 474 prv_ht->lock = sal_mutex_create(tbl_name); 475 if (prv_ht->lock == NULL) { 476 sal_free(prv_ht); 477 return _SHR_E_RESOURCE; 478 } 479 480 prv_ht->max_num_entries = max_num_entries; 481 prv_ht->key_size = key_size; 482 prv_ht->alloc_blk_cnt = HTB_DEFAULT_ALLOC_BLK_CNT; 483 prv_ht->hash_f = htb_default_hash_f; 484 prv_ht->key_cmp_f = htb_default_key_cmp_f; 485 prv_ht->cast_key_f = htb_default_cast_key_f; 486 487 /* hash table is an array of pointers */ 488 table_mem_size = max_num_entries * sizeof(_hash_entry_t*); 489 prv_ht->table = sal_alloc(table_mem_size, tbl_name); 490 if (prv_ht->table == NULL) { 491 sal_mutex_destroy(prv_ht->lock); 492 sal_free(prv_ht); 493 return _SHR_E_MEMORY; 494 } 495 496 sal_memset(prv_ht->table, 0, table_mem_size); 497 498 *ht = prv_ht; 499 return rv; 500 } 501 502 503 /* 504 * Function: 505 * shr_htb_destroy 506 * Purpose: 507 * destroy a hash table and all associated entries. 508 * Parameters: 509 * (in/out) ht - hash table to operate 510 * (in) cb - callback function for each valid hash entry freed 511 * may be NULL. 512 * Returns: 513 * BCM_E_NONE upon success 514 * BCM_E_* on failure 515 * Notes: 516 * The CALLER is responsible for freeing all memory associated with data by 517 * supplying a callback function. The callback is called for all entries 518 * found in the hash table. 519 */ 520 int 521 shr_htb_destroy(shr_htb_hash_table_t *ht, shr_htb_data_free_f cb) 522 { 523 int i; 524 int rv = _SHR_E_NONE; 525 _hash_entry_t *entry, *next; 526 shr_htb_hash_table_t prv_ht = *ht; 527 528 /* for each entry in the table, 529 * for each entry in the collision chain, 530 * call supplied callback 531 * return the entry to the free list 532 */ 533 HTBL_LOCK(prv_ht); 534 for (i=0; i < prv_ht->max_num_entries; i++) { 535 entry = prv_ht->table[i]; 536 while (entry) { 537 if (cb) { 538 cb(entry->data); 539 } 540 next = entry->next; 541 _htb_entry_free(prv_ht, &entry); 542 entry = next; 543 } 544 } 545 546 /* free the free list */ 547 while(prv_ht->num_free > 0) { 548 entry = _htb_free_list_pop(prv_ht); 549 550 /* shouldn't happen. free list accounting error if does */ 551 if (entry == NULL) { 552 rv = _SHR_E_INTERNAL; 553 } else { 554 sal_free(entry); 555 } 556 } 557 558 HTBL_UNLOCK(prv_ht); 559 560 sal_mutex_destroy(prv_ht->lock); 561 sal_free(prv_ht->table); 562 sal_free(prv_ht); 563 *ht = NULL; 564 565 return rv; 566 } 567 568 int 569 shr_htb_iterate(int unit, shr_htb_hash_table_t ht, shr_htb_cb_t restore_cb) 570 { 571 int rv = _SHR_E_NONE; 572 _hash_entry_t *entry, *next; 573 int i; 574 575 576 HTBL_LOCK(ht); 577 578 for (i = 0; i < ht->max_num_entries; i++) { 579 entry = ht->table[i]; 580 while (entry) { 581 if (restore_cb) { 582 restore_cb(unit, &(entry->key), entry->data); 583 } 584 next = entry->next; 585 entry = next; 586 } 587 } 588 589 HTBL_UNLOCK(ht); 590 591 return(rv); 592 } 593 594 /* 595 * Function: 596 * shr_htb_hash_func_set 597 * Purpose: 598 * Change the default hash function 599 * Parameters: 600 * (in) ht - hash table to operate 601 * (in) func - replacement hash function 602 * Returns: 603 * none 604 * Notes: 605 */ 606 void 607 shr_htb_hash_func_set(shr_htb_hash_table_t ht, shr_htb_hash_f func) 608 { 609 ht->hash_f = func; 610 } 611 612 /* 613 * Function: 614 * shr_htb_key_cmp_func_set 615 * Purpose: 616 * Change the default key compare function 617 * Parameters: 618 * (in) ht - hash table to operate 619 * (in) func - replacement key compare func 620 * Returns: 621 * none 622 * Notes: 623 */ 624 void 625 shr_htb_key_cmp_func_set(shr_htb_hash_table_t ht, shr_htb_key_cmp_f func) 626 { 627 ht->key_cmp_f = func; 628 } 629 630 631 /* 632 * Function: 633 * shr_htb_cast_key_func_set 634 * Purpose: 635 * Change the default cast key function 636 * Parameters: 637 * (in) ht - hash table to operate 638 * (in) func - replacement cast key func 639 * Returns: 640 * none 641 * Notes: 642 * The cast_key method is provided to allow for complex key data types. The 643 * cast implementation should return a pointer to a string of bytes that is 644 * the unique key for the hash entry, and the number of bytes in the string. 645 * The intent is to allow the most flexabilty for the caller to define a key 646 * structure, while keeping the hash function generic enough for most use 647 * cases. 648 */ 649 void 650 shr_htb_cast_key_func_set(shr_htb_hash_table_t ht, shr_htb_cast_key_f func) 651 { 652 ht->cast_key_f = func; 653 }