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

cpudb.c (22169B)


      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:        cpudb.c
      8  * Purpose:     CPU database; 
      9  * Requires:
     10  *
     11  * See the document cpu_database.txt for more information.
     12  *
     13  * The sysid is an application specific number between 0 and number
     14  * of CPUs permitted.  It is independent of the hard index used
     15  * by cpudb.
     16  */
     17 
     18 #include <assert.h>
     19 
     20 #include <sal/core/sync.h>
     21 #include <sal/core/libc.h>
     22 #include <shared/alloc.h>
     23 
     24 #include <bcm/types.h>
     25 #include <bcm/error.h>
     26 
     27 #include <appl/cpudb/cpudb.h>
     28 
     29 #define CPUDB_CHECK(db_ref, rv) if (!DB_REF_VALID(db_ref)) return (rv)
     30 #define CPUDB_MAGIC 0xfeedface
     31 #define DB_REF_VALID(db_ref)    \
     32     (((db_ref) != NULL) && ((db_ref)->magic == CPUDB_MAGIC))
     33 #define CPUDB_SET_MAGIC(db_ref) (db_ref)->magic = CPUDB_MAGIC
     34 #define CPUDB_CLEAR_MAGIC(db_ref) (db_ref)->magic = 0
     35 
     36 /* Declare broadcast and neighbor keys */
     37 CPUDB_BCAST_KEY_DECLARATION;
     38 CPUDB_NEIGHBOR_KEY_DECLARATION;
     39 
     40 static sal_mutex_t cpudb_lock;
     41 #define _CPUDB_LOCK sal_mutex_take(cpudb_lock, sal_mutex_FOREVER)
     42 #define _CPUDB_UNLOCK sal_mutex_give(cpudb_lock)
     43 #define INIT_DONE (cpudb_lock != NULL)
     44 
     45 STATIC int
     46 _cpudb_init(void)
     47 {
     48     if (cpudb_lock == NULL) {
     49         cpudb_lock = sal_mutex_create("cpudb-int-lock");
     50         if (cpudb_lock == NULL) {
     51             return BCM_E_MEMORY;
     52         }
     53     }
     54 
     55     return BCM_E_NONE;
     56 }
     57 
     58 STATIC cpudb_entry_t *_cpudb_entry_create(cpudb_t *db_ref,
     59                                           const cpudb_key_t key);
     60 
     61 /****************************************************************
     62  *
     63  * DB selection functions
     64  *      cpudb_create            Create a DB
     65  *      cpudb_destroy           De-init a DB
     66  *      cpudb_valid             Check if DB reference is valid
     67  *      cpudb_clear             Remove all info from a DB
     68  */
     69 
     70 
     71 /*
     72  * Function:
     73  *      cpudb_create
     74  * Purpose:
     75  *      Create a database
     76  * Parameters:
     77  * Returns:
     78  *      Small integer reference >= 0 if successful; used in cpudb calls.
     79  *      BCM_E_XXX < 0 otherwise.
     80  * Notes:
     81  */
     82 
     83 cpudb_ref_t
     84 cpudb_create(void)
     85 {
     86     cpudb_t *db;
     87 
     88     if (!INIT_DONE) {
     89         if (_cpudb_init() < 0) {
     90             return NULL;
     91         }
     92     }
     93 
     94     db = sal_alloc(sizeof(cpudb_t), "cpudb_create");
     95     if (db == NULL) {
     96         return NULL;
     97     }
     98 
     99     sal_memset(db, 0, sizeof(cpudb_t));
    100     CPUDB_SET_MAGIC(db);
    101     db->old_db = CPUDB_REF_NULL;
    102 
    103     return db;
    104 }
    105 
    106 
    107 STATIC int
    108 _cpudb_clear(cpudb_ref_t db_ref, int keep_local)
    109 {
    110     int i;
    111     cpudb_entry_t *entry, *next_entry;
    112     cpudb_entry_t save_local_entry;
    113     int saved = FALSE;
    114 
    115     if (keep_local) {
    116         if (db_ref->local_entry != NULL) {
    117             sal_memcpy(&save_local_entry, db_ref->local_entry,
    118                        sizeof(cpudb_entry_t));
    119             saved = TRUE;
    120         }
    121     }
    122 
    123     for (entry = db_ref->entries; entry != NULL; entry = next_entry) {
    124         next_entry = entry->next;
    125         sal_free(entry);
    126     }
    127 
    128     db_ref->entries = NULL;
    129     db_ref->num_cpus = 0;
    130 
    131     /* Clear key hashes */
    132     for (i = 0; i < CPUDB_HASH_ENTRY_COUNT; i++) {
    133         db_ref->key_hash[i] = NULL;
    134     }
    135 
    136     db_ref->master_entry = NULL;
    137 
    138     if (saved) {
    139         entry = _cpudb_entry_create(db_ref, save_local_entry.base.key);
    140         if (entry == NULL) {
    141             return BCM_E_FAIL;
    142         }
    143         sal_memcpy(&entry->base, &save_local_entry.base,
    144                    sizeof(cpudb_base_t));
    145         entry->flags = CPUDB_F_IS_LOCAL | CPUDB_F_BASE_INIT_DONE;
    146         db_ref->local_entry = entry;
    147         /* Clear the stack info for the local entry */
    148         for (i = 0; i < entry->base.num_stk_ports; i++) {
    149             /* Clear all flags except no link and ETH flag */
    150             entry->sp_info[i].flags =
    151                 save_local_entry.sp_info[i].flags &
    152                 (CPUDB_SPF_NO_LINK|CPUDB_SPF_ETHERNET);
    153         }
    154     }
    155 
    156     return BCM_E_NONE;
    157 }
    158 
    159 
    160 /*
    161  * Function:
    162  *      cpudb_clear
    163  * Purpose:
    164  *      Clear (initialized) the given DB.
    165  * Parameters:
    166  *      db_ref     - which DB to clear
    167  *      keep_local - Boolean indicating if local entry should be kept
    168  * Returns:
    169  *      BCM_E_XXX
    170  * Notes:
    171  *      Only the base info and user_cookie of the local entry is kept.
    172  *      The flags are cleared except for "is_local"
    173  */
    174 
    175 int
    176 cpudb_clear(cpudb_ref_t db_ref, int keep_local)
    177 {
    178     int rv;
    179 
    180     CPUDB_CHECK(db_ref, BCM_E_PARAM);
    181     _CPUDB_LOCK;
    182     rv = _cpudb_clear(db_ref, keep_local);
    183     _CPUDB_UNLOCK;
    184 
    185     return rv;
    186 }
    187 
    188 
    189 /*
    190  * Function:
    191  *      cpudb_destroy
    192  * Purpose:
    193  *      De-init a database
    194  * Parameters:
    195  *      db_ref        - Reference to DB to destroy
    196  * Returns:
    197  *      BCM_E_XXX
    198  * Notes:
    199  *      There should be no calls pending on the db_lock of this DB.
    200  *      Will not destroy db_ref->old_db even if non-NULL.
    201  */
    202 
    203 int
    204 cpudb_destroy(cpudb_ref_t db_ref)
    205 {
    206     int rv;
    207 
    208     CPUDB_CHECK(db_ref, BCM_E_PARAM);
    209     _CPUDB_LOCK;
    210     CPUDB_CLEAR_MAGIC(db_ref);
    211 
    212     rv = _cpudb_clear(db_ref, FALSE);
    213     if (rv == BCM_E_NONE) {
    214         sal_free(db_ref);
    215     } else { /* Shouldn't happen with keep_local == FALSE */
    216         assert(!"CPUDB failed to clear DB");
    217     }
    218     _CPUDB_UNLOCK;
    219 
    220     return rv;
    221 }
    222 
    223 
    224 /*
    225  * Function:
    226  *      cpudb_valid
    227  * Purpose:
    228  *      Is a CPU DB reference currently valid?
    229  * Parameters:
    230  *      db_ref         - The database reference to check
    231  * Returns:
    232  *      Boolean:  True means currently exists
    233  */
    234 
    235 int
    236 cpudb_valid(cpudb_ref_t db_ref)
    237 {
    238     return DB_REF_VALID(db_ref);
    239 }
    240 
    241 
    242 /****************************************************************
    243  *
    244  * Create functions
    245  *      cpudb_entry_create      Add an entry
    246  *      cpudb_entry_count_get   How many CPU entries in DB
    247  *
    248  * Set functions
    249  *      cpudb_sysid_set         Set system id by key
    250  *
    251  * Lookup functions; see also macros in cpudb.h
    252  *      cpudb_mac_lookup        Get entry pointer by MAC
    253  *      cpudb_sysid_lookup      Get entry pointer by system id
    254  *
    255  * Remove functions
    256  *      cpudb_entry_remove      Remove entry with given key
    257  */
    258 
    259 STATIC void _cpudb_unlink_entry(cpudb_ref_t db_ref,
    260                                 cpudb_entry_t *entry);
    261 
    262 /*
    263  * Function:
    264  *      cpudb_entry_create
    265  * Purpose:
    266  *      Create an entry for the given key with locking
    267  * Parameters:
    268  *      db_ref         - The database reference
    269  *      key            - The key to identify new entry
    270  * Returns:
    271  *      Pointer to entry created
    272  * Notes:
    273  *      Returns pointer to the entry or NULL if fails
    274  */
    275 
    276 cpudb_entry_t *
    277 cpudb_entry_create(cpudb_ref_t db_ref, const cpudb_key_t key, int is_local)
    278 {
    279     cpudb_entry_t *entry;
    280 
    281     CPUDB_CHECK(db_ref, NULL);
    282 
    283     _CPUDB_LOCK;
    284     entry =  _cpudb_entry_create(db_ref, key);
    285     if ((entry != NULL) && (is_local)) {
    286         db_ref->local_entry = entry;
    287         entry->flags |= CPUDB_F_IS_LOCAL;
    288     }
    289     _CPUDB_UNLOCK;
    290 
    291     return entry;
    292 }
    293 
    294 
    295 /*
    296  * Function:
    297  *      cpudb_local_base_info_set
    298  * Purpose:
    299  *      Set up local entry in db_ref according to local_entry data
    300  * Parameters:
    301  *      db_ref          - DB to update
    302  *      local_entry     - Pointer to "local" entry data
    303  * Returns:
    304  *      BCM_E_XXX
    305  * Notes:
    306  *      Creates local entry if one doesn't already exist in the DB.
    307  *      Error if local entry exists in db_ref with a different key.
    308  *      Sets the "base init done" flag for the entry.
    309  */
    310 
    311 int
    312 cpudb_local_base_info_set(cpudb_ref_t db_ref, cpudb_base_t *local_base)
    313 {
    314     cpudb_entry_t *entry;
    315 
    316     CPUDB_CHECK(db_ref, BCM_E_PARAM);
    317     if (db_ref->local_entry == NULL) {
    318         entry = cpudb_entry_create(db_ref, local_base->key, TRUE);
    319         if (entry == NULL) {
    320             return BCM_E_MEMORY;
    321         }
    322     } else {
    323         if (CPUDB_KEY_COMPARE(local_base->key,
    324                               db_ref->local_entry->base.key) != 0) {
    325             return BCM_E_EXISTS;
    326         }
    327         entry = db_ref->local_entry;
    328     }
    329 
    330     sal_memcpy(&entry->base, local_base, sizeof(cpudb_base_t));
    331     entry->flags |= CPUDB_F_BASE_INIT_DONE;
    332 
    333     return BCM_E_NONE;
    334 }
    335 
    336 /*
    337  * Function:
    338  *      cpudb_master_set
    339  * Purpose:
    340  *      Set the entry to be the master
    341  * Parameters:
    342  *      db_ref       -- DB being updated
    343  *      key          -- Key of the master CPU
    344  * Returns:
    345  *      BCM_E_XXX
    346  * Notes:
    347  *      If key is not in the DB, returns NOT_FOUND
    348  */
    349 
    350 int
    351 cpudb_master_set(cpudb_ref_t db_ref, const cpudb_key_t key)
    352 {
    353     cpudb_entry_t *entry;
    354 
    355     CPUDB_CHECK(db_ref, BCM_E_PARAM);
    356     CPUDB_KEY_SEARCH(db_ref, key, entry);
    357     if (entry == NULL) {
    358         return BCM_E_NOT_FOUND;
    359     }
    360 
    361     if (db_ref->master_entry != NULL) {
    362         db_ref->master_entry->flags &= ~CPUDB_F_IS_MASTER;
    363     }
    364 
    365     db_ref->master_entry = entry;
    366     entry->flags |= CPUDB_F_IS_MASTER;
    367 
    368     return BCM_E_NONE;
    369 }
    370 
    371 
    372 /*
    373  * Function:
    374  *      cpudb_master_get
    375  * Purpose:
    376  *      Get pointer to the master entry, if set
    377  * Parameters:
    378  *      db_ref     -- The DB to examine
    379  * Returns:
    380  *      BCM_E_XXX
    381  * Notes:
    382  *      Returns NULL if the master is not set.
    383  */
    384 
    385 cpudb_entry_t *
    386 cpudb_master_get(cpudb_ref_t db_ref)
    387 {
    388     CPUDB_CHECK(db_ref, NULL);
    389     
    390     return db_ref->master_entry;
    391 }
    392 
    393 
    394 /*
    395  * Function:
    396  *      cpudb_entry_count_get
    397  * Purpose:
    398  *      Returns the number of entries in the DB referenced.
    399  * Parameters:
    400  *      db_ref             - DB to update
    401  * Returns:
    402  *      BCM_E_XXX < 0 if not valid; otherwise, number of entries in this DB
    403  * Notes:
    404  */
    405 
    406 int
    407 cpudb_entry_count_get(cpudb_ref_t db_ref)
    408 {
    409     CPUDB_CHECK(db_ref, BCM_E_PARAM);
    410 
    411     return db_ref->num_cpus;
    412 }
    413 
    414 
    415 /*
    416  * Function:
    417  *      cpudb_sysid_set
    418  * Purpose:
    419  *      Set the sysid of a CPUDB entry
    420  * Parameters:
    421  *      db_ref     - Reference to CPUDB
    422  *      key        - Key of entry to update
    423  *      sysid      - Value to set sysid to.
    424  *      overwrite  - If TRUE, will overwrite existing sysid; otherwise
    425  *                   returns exists.
    426  * Returns:
    427  *      BCM_E_XXX
    428  * Notes:
    429  *      Checks whether the sysid is already assigned to a different entry
    430  */
    431 
    432 int
    433 cpudb_sysid_set(cpudb_ref_t db_ref, cpudb_key_t key, void *sysid,
    434                 int overwrite)
    435 {
    436     cpudb_entry_t *entry;
    437     int rv = BCM_E_NONE;
    438 
    439     CPUDB_CHECK(db_ref, BCM_E_PARAM);
    440 
    441     entry = cpudb_sysid_lookup(db_ref, sysid);
    442     if (entry != NULL) {   /* Sys ID in DB already; see if key matches */
    443         if (CPUDB_KEY_COMPARE(entry->base.key, key)) {   /* memcmp semantics */
    444             return BCM_E_EXISTS;  /* Key mismatch */
    445         }
    446 
    447         return BCM_E_NONE;   /* Exists, matches */
    448     }
    449 
    450     _CPUDB_LOCK;
    451 
    452     CPUDB_KEY_SEARCH(db_ref, key, entry);
    453     if (entry == NULL) {
    454         _CPUDB_UNLOCK;
    455         return BCM_E_NOT_FOUND;
    456     }
    457 
    458     if (entry->flags & CPUDB_F_SYSID_KNOWN) {
    459         if (overwrite) {
    460             entry->sysid = sysid;
    461         } else {
    462             rv = BCM_E_EXISTS;
    463         }
    464         /* We know it doesn't match b/c the above search failed */
    465         _CPUDB_UNLOCK;
    466         return rv;
    467     }
    468 
    469     entry->sysid = sysid;
    470     entry->flags |= CPUDB_F_SYSID_KNOWN;
    471 
    472     _CPUDB_UNLOCK;
    473     return BCM_E_NONE;
    474 }
    475 
    476 
    477 /*
    478  * Function:
    479  *      cpudb_sysid_lookup
    480  * Purpose:
    481  *      Search DB for sysid (not efficient)
    482  * Parameters:
    483  *      db_ref    - DB to search
    484  *      sysid     - sysid to search for
    485  * Returns:
    486  *      Pointer to entry if found; otherwise NULL
    487  */
    488 
    489 cpudb_entry_t *
    490 cpudb_sysid_lookup(cpudb_ref_t db_ref, void *sysid)
    491 {
    492     cpudb_entry_t *cur;
    493 
    494     CPUDB_CHECK(db_ref, NULL);
    495     _CPUDB_LOCK;
    496     CPUDB_FOREACH_ENTRY(db_ref, cur) {
    497         if (cur->flags & CPUDB_F_SYSID_KNOWN && cur->sysid == sysid) {
    498             _CPUDB_UNLOCK;
    499             return cur;  /* Found it */
    500         }
    501     }
    502 
    503     _CPUDB_UNLOCK;
    504     return NULL;
    505 }
    506 
    507 /*
    508  * Function:
    509  *      cpudb_mac_lookup
    510  * Purpose:
    511  *      Search DB for mac (not efficient)
    512  * Parameters:
    513  *      db_ref    - DB to search
    514  *      mac       - mac to search for
    515  * Returns:
    516  *      Pointer to entry if found; otherwise NULL
    517  * Notes:
    518  *      In the future, for systems with many CPUs supported, we
    519  *      might want to add a hash lookup for the MAC address.
    520  */
    521 
    522 cpudb_entry_t *
    523 cpudb_mac_lookup(cpudb_ref_t db_ref, const bcm_mac_t mac)
    524 {
    525     cpudb_entry_t *cur;
    526 
    527     CPUDB_CHECK(db_ref, NULL);
    528     _CPUDB_LOCK;
    529     CPUDB_FOREACH_ENTRY(db_ref, cur) {
    530         if (!sal_memcmp(cur->base.mac, mac, sizeof(bcm_mac_t))) {
    531             _CPUDB_UNLOCK;
    532             return cur;  /* Found it */
    533         }
    534     }
    535 
    536     _CPUDB_UNLOCK;
    537     return NULL;
    538 }
    539 
    540 
    541 /*
    542  * Function:
    543  *      cpudb_entry_remove
    544  * Purpose:
    545  *      Remove an entry from the CPU database by key
    546  * Parameters:
    547  *      db_ref             - DB to update
    548  *      key                - The key to search for and remove
    549  * Returns:
    550  *      BCM_E_XXX
    551  */
    552 
    553 int
    554 cpudb_entry_remove(cpudb_ref_t db_ref, const cpudb_key_t key)
    555 {
    556     cpudb_entry_t *entry;
    557 
    558     CPUDB_CHECK(db_ref, BCM_E_PARAM);
    559     _CPUDB_LOCK;
    560 
    561     /* Check if removing entry marked local or master */
    562     if (db_ref->local_entry != NULL &&
    563         CPUDB_KEY_EQUAL(db_ref->local_entry->base.key, key)) {
    564         db_ref->local_entry = NULL;
    565     }
    566     if (db_ref->master_entry != NULL &&
    567         CPUDB_KEY_EQUAL(db_ref->master_entry->base.key, key)) {
    568         db_ref->master_entry = NULL;
    569     }
    570 
    571     CPUDB_KEY_SEARCH(db_ref, key, entry);
    572     if (entry != NULL) {
    573         _cpudb_unlink_entry(db_ref, entry);
    574         sal_free(entry);
    575     }
    576 
    577     _CPUDB_UNLOCK;
    578     return BCM_E_NONE;
    579 }
    580 
    581 
    582 static char	cpudb_hex[] = "0123456789abcdef";
    583 
    584 /*
    585  * Format a key into a string.
    586  * This version assumes that a key is a MAC address.
    587  */
    588 int
    589 cpudb_key_format(cpudb_key_t key, char *buf, int len)
    590 {
    591     int	i, n;
    592 
    593     /* 00:11:22:33:44:55:66 */
    594     if (len < CPUDB_KEY_STRING_LEN) {
    595 	return BCM_E_FAIL;
    596     }
    597     for (i = 0; i < 6; i++) {
    598 	n = key.key[i];
    599 	if (n > 0xf) {
    600 	    *buf++ = cpudb_hex[(n>>4) & 0xf];
    601 	}
    602 	*buf++ = cpudb_hex[n & 0xf];
    603 	*buf++ = ':';
    604     }
    605     *--buf = '\0';
    606     return BCM_E_NONE;
    607 }
    608 
    609 /*
    610  * Parse a string into a key.
    611  * This version assumes that a key is a MAC address.
    612  */
    613 int
    614 cpudb_key_parse(char *buf, cpudb_key_t *keyp)
    615 {
    616     int		i, c1, c2;
    617     char	*s;
    618 
    619     keyp->key[0] = keyp->key[1] = keyp->key[2] = 0;
    620     keyp->key[3] = keyp->key[4] = keyp->key[5] = 0;
    621 
    622     if (buf == NULL) {
    623 	return BCM_E_FAIL;
    624     }
    625 
    626     /* skip leading 0x if plain hex format */
    627     if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) {
    628 	buf += 2;
    629     }
    630 
    631     /* start at end of string and work backwards */
    632     for (s = buf; *s; s++) {
    633 	;
    634     }
    635 
    636     for (i = 5; i >= 0 && s >= buf; i--) {
    637 	c1 = c2 = 0;
    638 	if (--s >= buf) {
    639 	    if (*s >= '0' && *s <= '9') {
    640 		c2 = *s - '0';
    641 	    } else if (*s >= 'a' && *s <= 'f') {
    642 		c2 = *s - 'a' + 10;
    643 	    } else if (*s >= 'A' && *s <= 'F') {
    644 		c2 = *s - 'A' + 10;
    645 	    } else if (*s == ':') {
    646 		;
    647 	    } else {
    648 		return BCM_E_FAIL;
    649 	    }
    650 	}
    651 	if (*s != ':' && --s >= buf) {
    652 	    if (*s >= '0' && *s <= '9') {
    653 		c1 = *s - '0';
    654 	    } else if (*s >= 'a' && *s <= 'f') {
    655 		c1 = *s - 'a' + 10;
    656 	    } else if (*s >= 'A' && *s <= 'F') {
    657 		c1 = *s - 'A' + 10;
    658 	    } else if (*s == ':') {
    659 		;
    660 	    } else {
    661 		return BCM_E_FAIL;
    662 	    }
    663 	}
    664 	if (s > buf && s[-1] == ':') {
    665 	    --s;
    666 	}
    667 	keyp->key[i] = c1 << 4 | c2;
    668     }
    669     return BCM_E_NONE;
    670 }
    671 
    672 /****************************************************************
    673  *
    674  * Internal functions
    675  */
    676 
    677 /* Create an entry; assumes lock is held; */
    678 STATIC cpudb_entry_t *
    679 _cpudb_entry_create(cpudb_t *db_ref, const cpudb_key_t key)
    680 {
    681     cpudb_entry_t *entry, *next, *prev;
    682     int h_idx;
    683 
    684     CPUDB_KEY_SEARCH(db_ref, key, entry);
    685 
    686     if (entry != NULL) {
    687         return entry;
    688     }
    689 
    690     entry = sal_alloc(sizeof(cpudb_entry_t), "cpudb_entry");
    691     if (entry == NULL) {
    692         return NULL;
    693     }
    694 
    695     sal_memset(entry, 0, sizeof(cpudb_entry_t));
    696     CPUDB_KEY_COPY(entry->base.key, key);
    697 
    698     /* Link into normal (sorted) list */
    699     prev = NULL;
    700     CPUDB_FOREACH_ENTRY(db_ref, next) {
    701 	if (CPUDB_KEY_COMPARE(next->base.key, key) > 0) {
    702 	    break;
    703 	}
    704 	prev = next;
    705     }
    706     entry->prev = prev;
    707     entry->next = next;
    708     if (entry->prev != NULL) {
    709 	entry->prev->next = entry;
    710     } else {
    711 	db_ref->entries = entry;
    712     }
    713     if (entry->next != NULL) {
    714 	entry->next->prev = entry;
    715     }
    716 	
    717     /* Link into hash list for key */
    718     h_idx = CPUDB_KEY_HASH(key);
    719     entry->h_prev = NULL;
    720     entry->h_next = db_ref->key_hash[h_idx];
    721     if (db_ref->key_hash[h_idx] != NULL) {
    722         db_ref->key_hash[h_idx]->h_prev = entry;
    723     }
    724     db_ref->key_hash[h_idx] = entry;
    725 
    726     entry->db_ref = db_ref;
    727 
    728     db_ref->num_cpus++;
    729 
    730     return entry;
    731 }
    732 
    733 /*
    734  * Unlink an entry from (doubly) linked lists (including key hash list)
    735  */
    736 
    737 STATIC void
    738 _cpudb_unlink_entry(cpudb_ref_t db_ref, cpudb_entry_t *entry)
    739 {
    740     int h_idx;
    741 
    742     /* Unlink from hash entry list */
    743     h_idx = CPUDB_KEY_HASH(entry->base.key);
    744     if (entry->h_prev == NULL) {
    745         db_ref->key_hash[h_idx] = entry->h_next;
    746     } else {
    747         entry->h_prev->h_next = entry->h_next;
    748     }
    749     if (entry->h_next != NULL) {
    750         entry->h_next->h_prev = entry->h_prev;
    751     }
    752 
    753     /* Unlink entry from main list */
    754     if (entry->prev != NULL) {
    755         entry->prev->next = entry->next;
    756     } else {
    757         db_ref->entries = entry->next;
    758     }
    759     if (entry->next != NULL) {
    760         entry->next->prev = entry->prev;
    761     }
    762     db_ref->num_cpus--;
    763 }
    764 
    765 /* Create a duplicate of src in db_ref */
    766 STATIC cpudb_entry_t *
    767 _cpudb_entry_duplicate(cpudb_ref_t db_ref, const cpudb_entry_t *src,
    768                        int is_local, int is_master)
    769 {
    770     cpudb_entry_t *entry;
    771 
    772     entry = _cpudb_entry_create(db_ref, src->base.key);
    773     if (entry == NULL) {
    774         return NULL;
    775     }
    776 
    777     if (is_local) {
    778         db_ref->local_entry = entry;
    779     }
    780     if (is_master) {
    781         db_ref->master_entry = entry;
    782     }
    783 
    784     sal_memcpy(&entry->base, &src->base, sizeof(cpudb_base_t));
    785     sal_memcpy(&entry->sp_info, &src->sp_info, sizeof(cpudb_sp_list_t));
    786     sal_memcpy(&entry->mod_ids, &src->mod_ids, sizeof(cpudb_mod_list_t));
    787     entry->flags = src->flags;
    788     entry->sysid = src->sysid;
    789     entry->tx_unit = src->tx_unit;
    790     entry->tx_port = src->tx_port;
    791     entry->dest_mod = src->dest_mod;
    792     entry->dest_port = src->dest_port;
    793     entry->user_cookie = src->user_cookie;
    794     entry->trans_ptr = src->trans_ptr;
    795     entry->topo_idx = src->topo_idx;
    796 
    797     return entry;
    798 }
    799 
    800 /*
    801  * Function:
    802  *      cpudb_copy
    803  * Purpose:
    804  *      Create a copy of a CPUDB and return a reference to it.
    805  * Parameters:
    806  *      db_ref     - which DB to copy
    807  * Returns:
    808  *      NULL if fails;
    809  *      Pointer to new DB clone otherwise
    810  *      Note that the TOPO COOKIE is NOT duplicated.  It stays
    811  *      NULL in the new DB since CPUDB doesn't know enough to
    812  *      duplicate it.
    813  */
    814 
    815 cpudb_ref_t
    816 cpudb_copy(const cpudb_ref_t src_db)
    817 {
    818     cpudb_ref_t new_db;
    819     int error = FALSE;
    820     cpudb_entry_t *current, *entry;
    821 
    822     if (!DB_REF_VALID(src_db)) {
    823         return CPUDB_REF_NULL;
    824     }
    825 
    826     new_db = cpudb_create();
    827     if (new_db == CPUDB_REF_NULL) {
    828         return CPUDB_REF_NULL;
    829     }
    830 
    831     _CPUDB_LOCK;
    832     CPUDB_FOREACH_ENTRY(src_db, current) {
    833         entry = _cpudb_entry_duplicate(new_db, current,
    834                                        src_db->local_entry == current,
    835                                        src_db->master_entry == current);
    836         if (entry == NULL) {
    837             error = TRUE;
    838             break;
    839         }
    840     }
    841     _CPUDB_UNLOCK;
    842 
    843     if (error) {
    844         if (new_db != CPUDB_REF_NULL) {
    845             cpudb_destroy(new_db);
    846         }
    847         return CPUDB_REF_NULL;
    848     }
    849 
    850     return new_db;
    851 }
    852 
    853 
    854 /*
    855  * Function:
    856  *      cpudb_entry_copy
    857  * Purpose:
    858  *      Copy data from one CPUDB entry to another
    859  * Parameters:
    860  *      dest    -- Pointer to destination entry
    861  *      src     -- Pointer to source entry
    862  * Returns:
    863  *      BCM_E_XXX
    864  * Notes:
    865  *      Copies the user cookie directly, so be careful if this is a
    866  *      pointer to a resource that could go away due to the original
    867  *      being destroyed.
    868  */
    869 
    870 int
    871 cpudb_entry_copy(cpudb_entry_t *dest, const cpudb_entry_t *src)
    872 {
    873     if (dest == NULL || src == NULL) {
    874         return BCM_E_PARAM;
    875     }
    876 
    877     sal_memcpy(&dest->base, &src->base, sizeof(cpudb_base_t));
    878     sal_memcpy(&dest->sp_info, &src->sp_info, sizeof(cpudb_sp_list_t));
    879     sal_memcpy(&dest->mod_ids, &src->mod_ids, sizeof(cpudb_mod_list_t));
    880     dest->flags = src->flags;
    881     dest->sysid = src->sysid;
    882     dest->tx_unit = src->tx_unit;
    883     dest->tx_port = src->tx_port;
    884     dest->dest_mod = src->dest_mod;
    885     dest->dest_port = src->dest_port;
    886     dest->user_cookie = src->user_cookie;
    887     dest->trans_ptr = src->trans_ptr;
    888     dest->topo_idx = src->topo_idx;
    889 
    890     return BCM_E_NONE;
    891 }
    892 
    893 /*
    894  * Function:
    895  *      cpudb_sp_idx_to_slot
    896  * Purpose:
    897  *      Map a stack port index to the slot number and DB entry
    898  *      it connects to.
    899  * Parameters:
    900  *      db_ref  -- DB reference
    901  *      entry   -- source entry
    902  *      sp_idx  -- stack port index from source entry
    903  *      out_entry -- (OUT) Destination entry
    904  * Returns:
    905  *      The slot ID of the destination if found
    906  * Notes:
    907  *      Returns -1 if not found.
    908  *      Works for CFM and LM sources
    909  *      out_entry may be NULL
    910  *      Assumes sp_idx is valid for entry.
    911  */
    912 
    913 int
    914 cpudb_sp_idx_to_slot(const cpudb_ref_t db_ref, const cpudb_entry_t *entry,
    915                      int sp_idx, cpudb_entry_t **out_entry)
    916 {
    917     cpudb_entry_t *db_ent;
    918 
    919     /* LM Source:  Search DB for CFM to which the SP connects */
    920     CPUDB_KEY_SEARCH(db_ref, entry->sp_info[sp_idx].tx_cpu_key, db_ent);
    921     if (db_ent == NULL) {
    922         return -1;
    923     }
    924 
    925     if (out_entry != NULL) {
    926         *out_entry = db_ent;
    927     }
    928 
    929     return db_ent->base.slot_id;
    930 }
    931 
    932 /*
    933  * Function:
    934  *      cpudb_key_lookup
    935  * Purpose:
    936  *      Search DB for key
    937  * Parameters:
    938  *      db_ref    - DB to search
    939  *      key       - key to search for
    940  * Returns:
    941  *      Pointer to entry if found; otherwise NULL
    942  */
    943 
    944 cpudb_entry_t *
    945 cpudb_key_lookup(cpudb_ref_t db_ref, const cpudb_key_t key)
    946 {
    947     cpudb_entry_t *cur;
    948 
    949     CPUDB_CHECK(db_ref, NULL);
    950     _CPUDB_LOCK;
    951     CPUDB_KEY_SEARCH(db_ref, key, cur);
    952     _CPUDB_UNLOCK;
    953     return cur;
    954 }
    955 
    956