openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

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 }