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

disc.c (127604B)


      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:        disc.c
      8  * Purpose:     Broadcom Discovery Algorithm
      9  * Requires:
     10  *
     11  * The discovery procedure is run using next hop transmit only.
     12  * It builds a local database with static variables, so it is
     13  * not re-entrant.
     14  *
     15  * This version of discovery runs in the callers thread of
     16  * execution.  No async option is provided.
     17  *
     18  * We assume that a full probe packet can be sent in a packet
     19  * that is returned by RX.  Each probe entry takes less than 20 bytes,
     20  * so a 1K buffer can hold 50 entries.
     21  */
     22 
     23 #include <shared/bsl.h>
     24 
     25 #include <sdk_config.h>
     26 #include <assert.h>
     27 
     28 #include <shared/alloc.h>
     29 #include <sal/core/libc.h>
     30 #include <sal/core/thread.h>
     31 #include <sal/core/sync.h>
     32 #include <sal/core/time.h>
     33 
     34 #include <bcm/types.h>
     35 #include <bcm/rx.h>
     36 #include <bcm/error.h>
     37 
     38 #include <appl/discover/disc.h>
     39 #include <appl/cputrans/next_hop.h>
     40 #include <appl/cputrans/cputrans.h>
     41 #include <appl/cputrans/atp.h>
     42 
     43 #include <appl/cpudb/cpudb.h>
     44 
     45 #include "disc_int.h"
     46 
     47 #ifndef DISC_COS_DEFAULT
     48 #define DISC_COS_DEFAULT 0
     49 #endif
     50 
     51 #ifndef DISC_VLAN_DEFAULT
     52 #define DISC_VLAN_DEFAULT 1
     53 #endif
     54 
     55 #ifndef DISC_THREAD_STACK
     56 #define DISC_THREAD_STACK        SAL_THREAD_STKSZ
     57 #endif
     58 
     59 #ifndef DISC_THREAD_PRIORITY
     60 #define DISC_THREAD_PRIORITY     100
     61 #endif
     62 
     63 #define DISC_TASK (_disc_tid != SAL_THREAD_ERROR)
     64 
     65 static volatile sal_thread_t     _disc_tid = SAL_THREAD_ERROR;
     66 
     67 typedef struct disc_callback_handler_s {
     68     disc_cb_t callback;
     69     void *user_data;
     70 } disc_callback_handler_t;
     71 
     72 typedef enum disc_task_cmd_e {
     73     DISC_TASK_CMD_UNKNOWN,
     74     DISC_TASK_CMD_RUN,
     75     DISC_TASK_CMD_STOP
     76 } disc_task_cmd_t;
     77 
     78 typedef enum disc_status_e {
     79     DISC_STATUS_INACTIVE,
     80     DISC_STATUS_SYNC_RUNNING,
     81     DISC_STATUS_TASK_RUNNING,
     82     DISC_STATUS_TASK_IDLE
     83 } disc_status_t;
     84 
     85 #define DISC_TASK_SLEEP sal_sem_take(disc_task_sem, sal_sem_FOREVER)
     86 #define DISC_TASK_WAKE sal_sem_give(disc_task_sem)
     87 
     88 
     89 static int disc_cos = DISC_COS_DEFAULT;
     90 static int disc_vlan = DISC_VLAN_DEFAULT;
     91 
     92 /* Locks and synchronization,
     93  *     disc_lock               Mutual exclusion for queues
     94  *     disc_sem                Signal to main processing loop
     95  * Macros:
     96  *     DISC_LOCK               Take mutex
     97  *     DISC_UNLOCK             Give mutex
     98  *     INIT_DONE               See if discovery module init is complete
     99  *     DISC_INIT_CHECK         if not initialized, attempt to init system.
    100  */
    101 
    102 /* Thread and synchronization */
    103 static sal_mutex_t              disc_lock;
    104 static sal_sem_t                disc_sem;
    105 
    106 static int disc_version  = DISCOVERY_VERSION_DEFAULT;
    107 static int disc_fallback = TRUE;  /* Indicates version auto-fallback */
    108 
    109 #define DISC_LOCK       sal_mutex_take(disc_lock, sal_mutex_FOREVER)
    110 #define DISC_UNLOCK     sal_mutex_give(disc_lock)
    111 #define DISC_SLEEP(_t)  sal_sem_take(disc_sem, _t)
    112 #define DISC_WAKE       sal_sem_give(disc_sem)
    113 
    114 #define INIT_DONE       (disc_lock != NULL)
    115 #define DISC_INIT_CHECK if (!INIT_DONE) BCM_IF_ERROR_RETURN(_disc_init())
    116 
    117 /****************************************************************
    118  *
    119  * Local discovery configuration and active information
    120  *      disc_trans_ptr        Low level transport driver
    121  *      disc_m_elect          Master election function
    122  *      disc_timeout_us       Overall timeout
    123  *      disc_cfg_to_us        Set ports that have not received
    124  *                            probe packets as inactive
    125  *      disc_retrx_us         Retransmit timeout
    126  *      disc_retrx_min_us     Min time between pkts out
    127  *
    128  * Macros
    129  *      LOCAL_KEY(db_ref)     Local CPU entry's DB key
    130  *      LOCAL_MAC(db_ref)     Local CPU entry's MAC address
    131  *      LOCAL_FLAGS(db_ref)   Local CPU entry's flags
    132  *      STKP_UNIT(db_ref, i)  Local CPU entry's unit for stack port [i]
    133  *      STKP_PORT(db_ref, i)  Local CPU entry's port for stack port [i]
    134  *      STK_PORT(db_ref, i)   Local CPU entry's stack port [i] (flags, etc)
    135  *      STK_FLAGS(db_ref, i)  Flags for local stack port i
    136  */
    137 
    138 STATIC int disc_m_elect_default(cpudb_ref_t db_ref, void *user_data);
    139 
    140 /* The database reference to use */
    141 static bcm_trans_ptr_t *disc_trans_ptr = TRANS_PTR_DEFAULT;
    142 
    143 static volatile int disc_timeout_us    = DISC_TIMEOUT_DEFAULT;
    144 static volatile int disc_cfg_to_us     = DISC_CFG_TO_DEFAULT;
    145 static volatile int disc_retrx_us      = DISC_RETRX_DEFAULT;
    146 int disc_retrx_min_us                  = DISC_RETRX_MIN_DEFAULT;
    147 static disc_election_cb_t disc_m_elect = disc_m_elect_default;
    148 static disc_start_election_cb_t disc_start_elect = NULL;
    149 static void *disc_m_user_data;
    150 
    151 #define LOCAL_KEY(db_ref)       (db_ref->local_entry->base.key)
    152 #define LOCAL_MAC(db_ref)       (db_ref->local_entry->base.mac)
    153 #define LOCAL_FLAGS(db_ref)     (db_ref->local_entry->flags)
    154 
    155 #define STKP_UNIT(db_ref, i)    (db_ref->local_entry->base.stk_ports[i].unit)
    156 #define STKP_PORT(db_ref, i)    (db_ref->local_entry->base.stk_ports[i].port)
    157 
    158 /* Stack port info other than unit/port */
    159 #define STK_PORT(db_ref, i)     (db_ref->local_entry->sp_info[i])
    160 #define STK_FLAGS(db_ref, i)    STK_PORT(db_ref, i).flags
    161 
    162 #define DATA_SALLOC(size)       atp_tx_data_alloc(size)
    163 #define DATA_SFREE(ptr)         atp_tx_data_free(ptr)
    164 
    165 #define DISC_TTL_FULL_DUPLEX 2  /* full duplex TTL */
    166 
    167 /*
    168  * If disc_ttl is enabled (> 0), then TTL is sent and examined in probe
    169  * packets.
    170  *
    171  * The TTL increased each time "send init info" is called up to
    172  * disc_ttl_max.
    173  */
    174 
    175 uint8 disc_ttl_min = DISC_TTL_MIN_DEFAULT;
    176 uint8 disc_ttl_max = DISC_TTL_MAX_DEFAULT;
    177 uint8 disc_ttl;
    178 
    179 /* Discovery packet rate limiting
    180 
    181   Large numbers of stack ports can easily overwhelm the Next Hop
    182   protocol, which has a limited capacity to detect stale packets. Rate
    183   limiting the transmission of probe and route packets helps keep the
    184   Next Hop protocol working correctly.
    185 
    186   Define DISC_PPS <= 0 to disable discovery packet rate limiting.
    187 
    188 */
    189 
    190 #ifndef DISC_PPS
    191 #define DISC_PPS 100
    192 #endif
    193 
    194 #if DISC_PPS > 0
    195 static int disc_tx_init;
    196 static sal_usecs_t disc_tx_prev;
    197 #endif
    198 
    199 /****************************************************************
    200  *
    201  * Discovery packet coordination
    202  *
    203  * Packet structure to maintain info about packet until serviced
    204  * in discovery thread.
    205  *
    206  * Probe and routing packet queues.
    207  */
    208 
    209 /*
    210  * This is used for queuing probe and routing packets
    211  * The queues are very simple.  We either add one entry, or remove
    212  * all entries in the queue at once; so it can be an array with
    213  * a length.
    214  */
    215 
    216 typedef struct disc_pkt_s disc_pkt_t;
    217 struct disc_pkt_s {
    218     cpudb_key_t src_key;
    219     uint8* pkt_buf;
    220     uint8* data;
    221     int len;
    222     int rx_unit, rx_port;
    223 };
    224 
    225 #ifndef DISC_PKTS_MAX
    226 #define DISC_PKTS_MAX 32 /* Max packets allowed queued at a time */
    227 #endif
    228 
    229 
    230 static disc_pkt_t          probe_pkt_queue[DISC_PKTS_MAX];
    231 static volatile int        probe_pkt_count;
    232 
    233 static disc_pkt_t          routing_pkt_queue[DISC_PKTS_MAX];
    234 static volatile int        routing_pkt_count;
    235 
    236 /* Output packets */
    237 static uint8 *route_pkt_buf[ROUTE_PKTS_MAX];
    238 static int route_pkt_len[ROUTE_PKTS_MAX];
    239 
    240 /*
    241  * To handle ports that have link, but on which there is no
    242  * activity, a check is made of whether a probe packet has been
    243  * seen on the port since the beginning of discovery.  If none
    244  * has been seen for a given amount of time, the port will be
    245  * marked inactive and ignored for the rest of the processing.
    246  * If discovery then completes successfully, those ports will be
    247  * marked as "no-link".
    248  *
    249  * probe_pkt_seen is a bit-map indexed by local stack port index.
    250  */
    251 static uint32 probe_pkt_seen[_SHR_BITDCLSIZE(CPUDB_STK_PORTS_MAX)];
    252 
    253 #define PP_SEEN_GET(n)      SHR_BITGET(probe_pkt_seen, n)
    254 #define PP_SEEN_SET(n)      SHR_BITSET(probe_pkt_seen, n)
    255 #define PP_SEEN_CLR(n)      SHR_BITCLR(probe_pkt_seen, n)
    256 #define PP_SEEN_ZERO        sal_memset(probe_pkt_seen, 0, \
    257                                         SHR_BITALLOCSIZE(CPUDB_STK_PORTS_MAX))
    258 
    259 /****************************************************************
    260  *
    261  * State
    262  *     disc_flags
    263  *         Divided into transient and non-transient flags.
    264  *         Transient flags are reset when starting.
    265  *             SETUP_DONE          disc_setup has been called.
    266  *             DISC_RUNNING        Discovery thread is running.
    267  *             DISC_ABORT          Terminate discovery
    268  *             ABORT_ACK           Respond that ACK seen
    269  *             DISC_ERROR          An error occurred, terminating discovery
    270  *             DISC_TIMEOUT        Discovery timed out (implies error)
    271  *             DB_UPDATED          Local DB has been changed.
    272  *             INFO_IN             Enough info is in to send routing pkts
    273  *                                 can be sent
    274  *             PROBE_SEND          Force probe packets to be sent the
    275  *                                 next cycle; used by restart.
    276  *             RESTART             Request controlling application
    277  *                                 restart discovery with same sequence
    278  *                                 number
    279  *             RESTART_DSEQ_NUM    Request controlling application
    280  *                                 restart discovery with a new sequence
    281  *                                 number
    282  *             TX_KNOWN            Some TX is known; this indicates
    283  *                                 we can send out routing pkts
    284  *
    285  *     disc_start_time         When did discovery start
    286  *     disc_tx_pkt_err         Counter:  TX errors
    287  *     disc_rx_pkt_err         Counter:  RX processing errors
    288  *     disc_alloc_fail         Counter:  Memory (external) resource error
    289  *     disc_resource_err       Counter:  Internal resource error
    290  *     disc_external_err       Counter:  External resource error
    291  *     disc_internal_err       Counter:  Internal error
    292  */
    293 
    294 volatile uint32          disc_flags;
    295 #define DF_SETUP_DONE           0x1
    296 #define DF_DISC_RUNNING         0x2
    297 #define DF_DISC_ABORT           0x4
    298 #define DF_ABORT_ACK            0x8   /* ACK that abort occurred */
    299 #define DF_DISC_ERROR           0x10
    300 #define DF_DISC_TIMEOUT         0x20
    301 #define DF_DB_UPDATED           0x40
    302 #define DF_INFO_IN              0x80
    303 #define DF_DISC_SUCCESS         0x100
    304 #define DF_TX_KNOWN             0x200
    305 #define DF_IGNORE_PACKETS       0x400
    306 #define DF_SEND_CFG_PKT         0x800
    307 #define DF_CFG_PKT_SENT         0x1000
    308 
    309 /* On abort, return value to use is saved here */
    310 static volatile int             disc_abort_rv;
    311 
    312 #define DISC_EXIT_FLAGS \
    313     (DF_DISC_ERROR | DF_DISC_ABORT | DF_DISC_TIMEOUT | DF_DISC_SUCCESS)
    314 
    315 #define DISC_EXIT (disc_flags & DISC_EXIT_FLAGS)
    316 
    317 /* When did discovery start; will restart if timeout_set is called */
    318 static volatile sal_usecs_t disc_start_time;
    319 static volatile sal_usecs_t disc_last_probe_time;
    320 static volatile sal_usecs_t disc_last_route_time;
    321 
    322 #define CUR_DSEQ_NUM(db_ref) (db_ref->local_entry->base.dseq_num)
    323 
    324 /* Some counters */
    325 int                        disc_tx_pkt_err    = 0;
    326 int                        disc_rx_pkt_err    = 0;
    327 int                        disc_alloc_fail    = 0;
    328 int                        disc_resource_err  = 0;
    329 int                        disc_external_err  = 0;
    330 int                        disc_internal_err  = 0;
    331 int                        disc_tot_err       = 0;
    332 
    333 /* Will fail if this many total errors occurs. */
    334 
    335 #ifndef DISC_MAX_ERRORS
    336 #define DISC_MAX_ERRORS 20
    337 #endif
    338 
    339 static cpudb_ref_t disc_task_db = NULL;
    340 static disc_callback_handler_t _disc_callback;
    341 static disc_task_cmd_t disc_task_cmd;
    342 static sal_sem_t disc_task_sem;
    343 static disc_status_t disc_stat = DISC_STATUS_INACTIVE;
    344 static sal_mutex_t disc_status_lock;
    345 
    346 /****************************************************************
    347  *
    348  * Forward declarations
    349  */
    350 
    351 STATIC bcm_rx_t disc_rx_pkt(cpudb_key_t src_key, int port_num,
    352                             int unit, int port, uint8 *pkt_buf,
    353                             int len, void *cookie);
    354 STATIC int _disc_init(void);
    355 
    356 STATIC int disc_prep(cpudb_ref_t db_ref);
    357 STATIC void queues_clear(void);
    358 STATIC cpudb_entry_t *_disc_key_resolve(cpudb_ref_t db_ref,
    359                                         cpudb_key_t key,
    360                                         bcm_mac_t mac,
    361                                         int dseq_num);
    362 
    363 STATIC int disc_done_check(cpudb_ref_t db_ref, int *rv, int unreg);
    364 STATIC void disc_status_update(cpudb_ref_t db_ref);
    365 
    366 STATIC void routing_pkts_send(cpudb_ref_t db_ref);
    367 STATIC void routing_pkt_process(cpudb_ref_t db_ref, disc_pkt_t *routing_pkt);
    368 STATIC void probe_pkt_process(cpudb_ref_t db_ref, disc_pkt_t *probe_pkt);
    369 STATIC void probe_pkts_generate(cpudb_ref_t db_ref);
    370 STATIC int _disc_run(cpudb_ref_t db_ref);
    371 STATIC int disc_callout(cpudb_ref_t db_ref, int status);
    372 
    373 
    374 /****************************************************************
    375  *
    376  * Configuration functions for timeout, transport pointer and
    377  * done callback function
    378  */
    379 
    380 /*
    381  * Function:
    382  *      disc_timeout_set/get
    383  * Purpose:
    384  *      Set/get the overall timeout and retransmit times in us
    385  * Parameters:
    386  *      timeout_us     - Overall timeout in us (OUT for get)
    387  *      retrx_us       - Retransmit timeout in us (OUT for get)
    388  * Returns:
    389  *      BCM_E_XXX
    390  * Notes:
    391  *      If either parameter is < 0 on set, it is ignored.
    392  *      The timeout or cfg timeout may be changed while running;
    393  *      in this case, (if timeout_us > 0) the start time is
    394  *      recalculated to the present time.
    395  */
    396 
    397 int
    398 disc_timeout_set(int timeout_us, int cfg_to_us, int retrx_us)
    399 {
    400     DISC_INIT_CHECK;
    401 
    402     if (timeout_us >= 0) {
    403         disc_timeout_us = timeout_us;
    404         if (disc_flags & DF_DISC_RUNNING) {
    405             disc_start_time = sal_time_usecs();
    406         }
    407     }
    408 
    409     if (cfg_to_us >= 0) {
    410         disc_cfg_to_us = cfg_to_us;
    411         if (disc_flags & DF_DISC_RUNNING) {
    412             disc_start_time = sal_time_usecs();
    413         }
    414     }
    415 
    416     if (retrx_us >= 0) {
    417         disc_retrx_us = retrx_us;
    418     }
    419 
    420     return BCM_E_NONE;
    421 }
    422 
    423 int
    424 disc_timeout_get(int *timeout_us, int *cfg_to_us, int *retrx_us)
    425 {
    426     DISC_INIT_CHECK;
    427 
    428     *timeout_us = disc_timeout_us;
    429     *cfg_to_us = disc_cfg_to_us;
    430     *retrx_us = disc_retrx_us;
    431 
    432     return BCM_E_NONE;
    433 }
    434 
    435 /*
    436  * Function:
    437  *      disc_(cos,vlan)_(set,get)
    438  * Purpose:
    439  *      Set/get the COS and VLAN settings used by discovery
    440  * Notes:
    441  *      The RX subsystem and chip COS configurations must be
    442  *      set up consistently with the values used here; similarly
    443  *      for the VLAN settings.
    444  */
    445 
    446 int
    447 disc_cos_set(int cos)
    448 {
    449     disc_cos = cos;
    450 
    451     return BCM_E_NONE;
    452 }
    453 
    454 int
    455 disc_cos_get(int *cos)
    456 {
    457     *cos = disc_cos;
    458 
    459     return BCM_E_NONE;
    460 }
    461 
    462 int
    463 disc_vlan_set(int vlan)
    464 {
    465     disc_vlan = vlan;
    466 
    467     return BCM_E_NONE;
    468 }
    469 
    470 int
    471 disc_vlan_get(int *vlan)
    472 {
    473     *vlan = disc_vlan;
    474 
    475     return BCM_E_NONE;
    476 }
    477 
    478 /*
    479  * Sets/gets the current discovery version used when sending
    480  * discovery packets.
    481  */
    482 int
    483 disc_version_set(int version)
    484 {
    485     int rv = BCM_E_NONE;
    486 
    487     switch (version) {
    488     case DISCOVERY_VERSION_0:
    489     case DISCOVERY_VERSION_1:
    490     case DISCOVERY_VERSION_2:
    491         disc_version = version;
    492         break;
    493     default:
    494         rv = BCM_E_PARAM;
    495         break;
    496     }
    497 
    498     return rv;
    499 }
    500 
    501 int
    502 disc_version_get(int *version)
    503 {
    504     if (version != NULL) {
    505         *version = disc_version;
    506     }
    507 
    508     return BCM_E_NONE;
    509 }
    510 
    511 /*
    512  * Enables/disables discovery version auto fallback.
    513  *
    514  *   When set, the current discovery version is set back
    515  *   to the received discovery version (lower) in case of a discovery
    516  *   version mismatch.
    517  */
    518 int
    519 disc_fallback_set(int enable)
    520 {
    521     if (enable) {
    522         disc_fallback = TRUE;
    523     } else {
    524         disc_fallback = FALSE;
    525     }
    526     return BCM_E_NONE;
    527 }
    528 
    529 int
    530 disc_fallback_get(int *enable)
    531 {
    532     if (enable != NULL) {
    533         *enable = disc_fallback;
    534     }
    535 
    536     return BCM_E_NONE;
    537 }
    538 
    539 
    540 
    541 /*
    542  * Function:
    543  *      disc_trans_ptr_set/get
    544  * Purpose:
    545  *      Set/get the transport pointer used by discovery
    546  * Parameters:
    547  *      trans_ptr        - (OUT for "get") Pointer to table of drivers
    548  * Returns:
    549  *      BCM_E_XXX
    550  * Notes:
    551  *      trans_ptr may be NULL on get.
    552  */
    553 
    554 int
    555 disc_trans_ptr_set(bcm_trans_ptr_t *trans_ptr)
    556 {
    557     if (trans_ptr == NULL) {
    558         return BCM_E_PARAM;
    559     }
    560 
    561     disc_trans_ptr = trans_ptr;
    562     return BCM_E_NONE;
    563 }
    564 
    565 int
    566 disc_trans_ptr_get(bcm_trans_ptr_t **trans_ptr)
    567 {
    568     if (trans_ptr != NULL) {
    569         *trans_ptr = disc_trans_ptr;
    570     }
    571     return BCM_E_NONE;
    572 }
    573 
    574 
    575 /*
    576  * Function:
    577  *      disc_ttl_min/max_set/get
    578  * Purpose:
    579  *      Set/get the probe minimum and maximum TTL
    580  * Parameters:
    581  *      min_ttl        - Minimum TTL (OUT for get)
    582  *      max_ttl        - Maximum TTL (OUT for get)
    583  * Returns:
    584  *      BCM_E_XXX
    585  * Notes:
    586  *      If either parameter is less than DISC_TTL_MIN_DEFAULT,
    587  *      then the corresponding value will not be set, and return
    588  *      BCM_E_PARAM.
    589  */
    590 
    591 int
    592 disc_ttl_min_set(int ttl_min)
    593 {
    594     int rv = BCM_E_NONE;
    595 
    596     if (ttl_min >= DISC_TTL_MIN_DEFAULT) {
    597         disc_ttl_min = ttl_min;
    598     } else {
    599         rv = BCM_E_PARAM;
    600     }
    601 
    602     return rv;
    603 }
    604 
    605 int
    606 disc_ttl_min_get(int *ttl_min)
    607 {
    608     if (ttl_min) {
    609         *ttl_min = disc_ttl_min;
    610     }
    611 
    612     return BCM_E_NONE;
    613 }
    614 
    615 
    616 int
    617 disc_ttl_max_set(int ttl_max)
    618 {
    619     int rv = BCM_E_NONE;
    620 
    621     if (ttl_max >= DISC_TTL_MIN_DEFAULT) {
    622         disc_ttl_max = ttl_max;
    623     } else {
    624         rv = BCM_E_PARAM;
    625     }
    626 
    627     return rv;
    628 }
    629 
    630 int
    631 disc_ttl_max_get(int *ttl_max)
    632 {
    633     if (ttl_max) {
    634         *ttl_max = disc_ttl_max;
    635     }
    636 
    637     return BCM_E_NONE;
    638 }
    639 
    640 
    641 #if defined(BROADCOM_DEBUG)
    642 int _check_start_values(cpudb_ref_t db_ref)
    643 {
    644 
    645     if (!cpudb_valid(db_ref)) {
    646         LOG_ERROR(BSL_LS_TKS_DISCOVER,
    647                   (BSL_META("disc ERR:  Bad DB reference\n")));
    648         return BCM_E_PARAM;
    649     }
    650 
    651     if (db_ref->local_entry == NULL) {
    652         LOG_ERROR(BSL_LS_TKS_DISCOVER,
    653                   (BSL_META("disc ERR:  Can't find local DB entry\n")));
    654         return BCM_E_PARAM;
    655     }
    656 
    657     if (disc_retrx_min_us >= disc_retrx_us) {
    658         LOG_ERROR(BSL_LS_TKS_DISCOVER,
    659                   (BSL_META("disc ERR:  retrx mis-configuration. min %d max %d\n"),
    660                    disc_retrx_min_us, disc_retrx_us));
    661         return BCM_E_PARAM;
    662     }
    663 
    664     if (db_ref->local_entry->base.num_units < 0 ||
    665         db_ref->local_entry->base.dest_port < 0 ||
    666         db_ref->local_entry->base.dest_unit < 0) {
    667         LOG_WARN(BSL_LS_TKS_DISCOVER,
    668                  (BSL_META("disc WARN: Bad local DB info\n")));
    669     }
    670 
    671     if (disc_ttl_min == 1 && disc_ttl_max == 1) {
    672         LOG_WARN(BSL_LS_TKS_DISCOVER,
    673                  (BSL_META("DISC WARN: TTL min == max == 1.  Discovery will fail\n")));
    674     }
    675 
    676     return BCM_E_NONE;
    677 }
    678 #endif /* BROADCOM_DEBUG */
    679 
    680 
    681 /****************************************************************
    682  *
    683  * Setup, start and abort functions
    684  */
    685 
    686 /* Get internal Discovery subsystem status */
    687 STATIC int
    688 disc_status_get(disc_status_t *status)
    689 {
    690     int rv = BCM_E_PARAM;
    691 
    692     if (status) {
    693         sal_mutex_take(disc_status_lock, sal_mutex_FOREVER);
    694         *status = disc_stat;
    695         sal_mutex_give(disc_status_lock);
    696         rv = BCM_E_NONE;
    697     }
    698     
    699     return rv;
    700 }
    701 
    702 #define STEAL_QUEUE(_q, _cnt, _dest_q, _dest_cnt)                          \
    703     do {                                                                   \
    704         int _bytes;                                                        \
    705         DISC_LOCK;                                                         \
    706         _bytes = (_cnt) * sizeof(disc_pkt_t);                              \
    707         sal_memcpy(_dest_q, _q, _bytes);                                   \
    708         sal_memset(_q, 0, _bytes);                                         \
    709         _dest_cnt = _cnt;                                                  \
    710         _cnt = 0;                                                          \
    711         DISC_UNLOCK;                                                       \
    712     } while (0)
    713 
    714 /* Temporary holding pen for handling a discovery queue. */
    715 static disc_pkt_t               _proc_queue[DISC_PKTS_MAX];
    716 static volatile int             _proc_count;
    717 
    718 /* Handle incoming probe/routing pkts */
    719 
    720 static INLINE void
    721 disc_incoming_pkts_process(cpudb_ref_t db_ref)
    722 {
    723     int i;
    724 
    725     if (probe_pkt_count > 0) { /* Received probe pkts to forward */
    726         /* Steal then process the probe packets */
    727         STEAL_QUEUE(probe_pkt_queue, probe_pkt_count,
    728                     _proc_queue, _proc_count);
    729 
    730         /* clear fowarded flag */
    731         for (i = 0; i < db_ref->local_entry->base.num_stk_ports; i++) {
    732             STK_FLAGS(db_ref, i) &=  ~CPUDB_SPF_TX_FORWARDED;
    733         }
    734 
    735         for (i = 0; i < _proc_count; i++) {
    736             probe_pkt_process(db_ref, &_proc_queue[i]);
    737             if (DISC_EXIT) {
    738                 return;
    739             }
    740         }
    741     }
    742 
    743     if (routing_pkt_count > 0) { /* Received routing packet */
    744         /* Steal then process the probe packets */
    745         STEAL_QUEUE(routing_pkt_queue, routing_pkt_count,
    746                     _proc_queue, _proc_count);
    747 
    748         for (i = 0; i < _proc_count; i++) {
    749             routing_pkt_process(db_ref, &_proc_queue[i]);
    750             if (DISC_EXIT) {
    751                 return;
    752             }
    753         }
    754     }
    755 }
    756 
    757 #undef STEAL_QUEUE
    758 
    759 /*
    760  * Function:
    761  *      disc_pkts_process
    762  * Purpose:
    763  *      Handle any pending packets queued from RX; check for timeout.
    764  * Returns:
    765  *      Boolean, TRUE => done;  FALSE => continue
    766  * Notes:
    767  *      Results of discovery are stored in disc_flags
    768  */
    769 
    770 static INLINE void
    771 disc_pkts_process(cpudb_ref_t db_ref)
    772 {
    773     sal_usecs_t cur_time;
    774     int dt;
    775 
    776     cur_time = sal_time_usecs();
    777     /* See if probe packets should be resent. */
    778     dt = SAL_USECS_SUB(cur_time, disc_last_probe_time);
    779     if (dt < 0 || dt > disc_retrx_us) {
    780         if (!(LOCAL_FLAGS(db_ref) & CPUDB_F_LOCAL_COMPLETE)) {
    781             probe_pkts_generate(db_ref); /* send probes */
    782         }
    783         disc_last_probe_time = cur_time;
    784     }
    785 
    786     if (DISC_EXIT) {
    787         return;
    788     }
    789 
    790     /* Check for queued probe and routing pkts */
    791     disc_incoming_pkts_process(db_ref);
    792 
    793     if (DISC_EXIT) {
    794         return;
    795     }
    796 
    797     cur_time = sal_time_usecs();
    798     if (disc_flags & DF_INFO_IN) {   /* Have info for routing pkts too */
    799         /*
    800          * IF     (Never sent out rte pkt) OR
    801          *        (DB updated and min time elapsed) OR
    802          *        (retransmit timeout)
    803          * THEN send route pkts
    804          */
    805         dt = SAL_USECS_SUB(cur_time, disc_last_route_time);
    806         if (disc_last_route_time == 0 ||
    807             ((disc_flags & DF_DB_UPDATED) &&
    808              (dt > disc_retrx_min_us)) ||
    809             (dt > disc_retrx_us) ||
    810             (dt < 0)) {  /* Send out routing pkts */
    811 
    812             routing_pkts_send(db_ref);
    813             disc_last_route_time = cur_time;
    814         }
    815     }
    816 }
    817 
    818 
    819 /*
    820  * Function:
    821  *      disc_start
    822  * Purpose:
    823  *      Start the discovery process
    824  * Parameters:
    825  *      db_ref       - The database reference to use; see notes
    826  *      m_elect      - Master election callback (DEPRECATED)
    827  * Returns:
    828  *      BCM_E_XXX
    829  * Notes:
    830  *      If db_ref < 0, then a new DB is created and used.
    831  *
    832  *      This routine is not re-entrant.
    833  *
    834  *      The discovery sequence number will be extracted from the local
    835  *      CPU DB entry.
    836  *
    837  *      Calling disc_election_register() to register an election function
    838  *      is preferred over passing the election function to disc_start().
    839  */
    840 int
    841 disc_start(cpudb_ref_t db_ref, disc_start_election_cb_t m_elect)
    842 {
    843     disc_status_t status;
    844     int rv;
    845 
    846     BCM_IF_ERROR_RETURN(disc_status_get(&status));
    847 
    848     if (status != DISC_STATUS_INACTIVE) {
    849         return BCM_E_BUSY;
    850     }
    851 
    852     if (m_elect != NULL) {
    853         disc_start_elect = m_elect;
    854     }
    855     rv = _disc_run(db_ref);
    856 
    857     return rv;
    858 }
    859 
    860 /*
    861  * Low level init routine; set up sync objects
    862  * Should only be called once.
    863  */
    864 
    865 STATIC int
    866 _disc_init(void)
    867 {
    868     int delay, retries;
    869     int to;
    870 
    871     /* Set up status lock */
    872     if (disc_status_lock != NULL) {
    873         sal_mutex_destroy(disc_status_lock);
    874     }
    875 
    876     disc_status_lock = sal_mutex_create("discstatus");
    877     if (disc_status_lock == NULL) {
    878         return BCM_E_MEMORY;
    879     }
    880     
    881     
    882     /* Set up mutex */
    883     if (disc_lock != NULL) {
    884         sal_mutex_destroy(disc_lock);
    885         disc_lock = NULL;
    886     }
    887 
    888     disc_lock = sal_mutex_create("discovery");
    889     if (disc_lock == NULL) {
    890         sal_mutex_destroy(disc_status_lock);
    891         return BCM_E_MEMORY;
    892     }
    893 
    894     /* Set up disc_sem */
    895     if (disc_sem != NULL) {
    896         sal_sem_destroy(disc_sem);
    897     }
    898 
    899     disc_sem = sal_sem_create("disc_sem", sal_sem_BINARY, 0);
    900     if (disc_sem == NULL) {
    901         sal_mutex_destroy(disc_lock);
    902         sal_mutex_destroy(disc_status_lock);
    903         disc_lock = NULL;
    904         return BCM_E_MEMORY;
    905     }
    906 
    907     /* Initialize time out based on ATP settings */
    908     atp_timeout_get(&delay, &retries);
    909     to = delay * (retries + 1);
    910     if (to > DISC_TIMEOUT_DEFAULT) {
    911         disc_timeout_us = to;
    912     }
    913     
    914     return BCM_E_NONE;
    915 }
    916 
    917 
    918 
    919 /*
    920  * Function:
    921  *      disc_abort
    922  * Purpose:
    923  *      Request discovery exit with possible restart
    924  * Parameters:
    925  *      disc_rv     - Value discovery should return on exit
    926  *      timeout     - if > 0, us to wait for "running" flag to clear
    927  *                    before returning fail
    928  * Returns:
    929  *      BCM_E_NONE if "running" flag clears before timeout
    930  *      BCM_E_FAIL if "running" flag still set after timeout
    931  */
    932 
    933 int
    934 disc_abort(int disc_rv, int timeout_us)
    935 {
    936     int i;
    937     int retries;
    938     int rv = BCM_E_NONE;
    939 
    940     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
    941                 (BSL_META("DISC abort: RV %d, to %d\n"),
    942                  disc_rv,
    943                  timeout_us));
    944 
    945     DISC_LOCK;
    946     disc_abort_rv = disc_rv;
    947     disc_flags &= ~DF_ABORT_ACK;
    948     disc_flags |= DF_DISC_ABORT;
    949 
    950     if (!(disc_flags & DF_DISC_RUNNING)) {
    951         DISC_UNLOCK;
    952         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
    953                     (BSL_META("DISC abort: not running\n")));
    954         return BCM_E_NONE;
    955     }
    956     DISC_UNLOCK;
    957 
    958     DISC_WAKE;
    959 
    960     if (timeout_us > 0) {  /* Block until restart noted */
    961         retries = timeout_us/10000 + 1;  /* At least 1. */
    962 
    963         /* Wait for exit */
    964         for (i = 0; i < retries; i++) {
    965             if (disc_flags & DF_ABORT_ACK) {
    966                 break;
    967             }
    968             sal_usleep(10000);
    969         }
    970 
    971         if (!(disc_flags & DF_ABORT_ACK)) {
    972             LOG_WARN(BSL_LS_TKS_DISCOVER,
    973                      (BSL_META("DISC WARN: Discovery did not exit in "
    974                       "%d us\n"), timeout_us));
    975             rv = BCM_E_FAIL;
    976         }
    977     }
    978 
    979     return rv;
    980 }
    981 
    982 
    983 /* Prepare local variables and send out initial pkts for discovery.
    984    Assumes lock held.
    985 */
    986 
    987 STATIC int
    988 disc_prep(cpudb_ref_t db_ref)
    989 {
    990     int rv;
    991     int i;
    992 
    993     /* Make sure routing packets are allocated */
    994     for (i = 0; i < ROUTE_PKTS_MAX; i++) {
    995         if (route_pkt_buf[i] == NULL) {
    996             route_pkt_buf[i] = DATA_SALLOC(ROUTE_PKT_BYTES_MAX);
    997             if (route_pkt_buf[i] == NULL) {
    998                 LOG_WARN(BSL_LS_TKS_DISCOVER,
    999                          (BSL_META("disc WARN: Route pkt alloc %d\n"),
   1000                           i));
   1001                 return BCM_E_MEMORY;
   1002             }
   1003         }
   1004     }
   1005 
   1006     
   1007 
   1008 #if defined(BROADCOM_DEBUG)
   1009     if (CPUDB_ENTRY_COUNT_GET(db_ref) > 1) {
   1010         LOG_WARN(BSL_LS_TKS_DISCOVER,
   1011                  (BSL_META("disc WARN: Local DB has > 1 entry\n")));
   1012     }
   1013 #endif /* BROADCOM_DEBUG */
   1014 
   1015     /* Init error counters */
   1016     disc_tx_pkt_err = disc_rx_pkt_err = 0;
   1017     disc_resource_err = disc_internal_err = disc_tot_err = 0;
   1018 
   1019     /* Ignore packets (and let them drain) for the first sleep interval */
   1020     disc_flags |= DF_IGNORE_PACKETS;
   1021 
   1022     if (!next_hop_running()) {
   1023         nh_tx_local_mac_set(LOCAL_MAC(db_ref));
   1024         rv = next_hop_start(&db_ref->local_entry->base);
   1025         if (BCM_FAILURE(rv)) {
   1026             LOG_WARN(BSL_LS_TKS_DISCOVER,
   1027                      (BSL_META("disc WARN: Error starting next hop\n")));
   1028             disc_flags |= DF_DISC_ERROR;
   1029             return rv;
   1030         }
   1031     }
   1032 
   1033     /* Set up initial state */
   1034     PP_SEEN_ZERO;  /* Clear out stack port markings */
   1035     disc_ttl = disc_ttl_min;
   1036     disc_last_probe_time = disc_start_time = sal_time_usecs();
   1037     disc_last_route_time = 0; /* Signal not yet sent */
   1038 #if DISC_PPS > 0
   1039     disc_tx_init = 0;
   1040 #endif
   1041 
   1042     /* Initialize discovery flags, but preserve ABORT_ACK */
   1043     if (disc_flags & DF_ABORT_ACK) {
   1044         disc_flags = DF_DISC_RUNNING | DF_SETUP_DONE | DF_ABORT_ACK;
   1045     } else {
   1046         disc_flags = DF_DISC_RUNNING | DF_SETUP_DONE;
   1047     }
   1048 
   1049     return BCM_E_NONE;
   1050 }
   1051 
   1052 /* Clean up discovery on exit; does not deallocate or do final cleanup */
   1053 
   1054 STATIC void
   1055 queues_clear(void)
   1056 {
   1057     int i;
   1058     disc_pkt_t *pkt;
   1059 
   1060     /* Clean up any packets in receive queues */
   1061     for (i = 0; i < DISC_PKTS_MAX; i++) {
   1062         pkt = &probe_pkt_queue[i];
   1063         if (pkt->pkt_buf != NULL) {
   1064             bcm_rx_free(pkt->rx_unit, pkt->pkt_buf);
   1065             pkt->pkt_buf = NULL;
   1066         }
   1067     }
   1068     probe_pkt_count = 0;
   1069     sal_memset(probe_pkt_queue, 0, sizeof(disc_pkt_t) * DISC_PKTS_MAX);
   1070 
   1071     for (i = 0; i < DISC_PKTS_MAX; i++) {
   1072         pkt = &routing_pkt_queue[i];
   1073         if (pkt->pkt_buf != NULL) {
   1074             bcm_rx_free(pkt->rx_unit, pkt->pkt_buf);
   1075             pkt->pkt_buf = NULL;
   1076         }
   1077     }
   1078     routing_pkt_count = 0;
   1079     sal_memset(routing_pkt_queue, 0, sizeof(disc_pkt_t) * DISC_PKTS_MAX);
   1080 
   1081     for (i = 0; i < DISC_PKTS_MAX; i++) {
   1082         pkt = &_proc_queue[i];
   1083         if (pkt->pkt_buf != NULL) {
   1084             bcm_rx_free(pkt->rx_unit, pkt->pkt_buf);
   1085             pkt->pkt_buf = NULL;
   1086         }
   1087     }
   1088     _proc_count = 0;
   1089     sal_memset(_proc_queue, 0, sizeof(disc_pkt_t) * DISC_PKTS_MAX);
   1090 }
   1091 
   1092 /****************************************************************
   1093  *
   1094  * Discovery packet handling
   1095  */
   1096 STATIC int stk_entry_bytes_get(int version);
   1097 STATIC int route_entry_bytes_get(int num_sp, int num_units, int version);
   1098 
   1099 STATIC int probe_pkt_local_cpu_key(cpudb_ref_t db_ref,
   1100                                           uint8 *buf, int entry_count);
   1101 STATIC void probe_pkt_with_local_key(cpudb_ref_t db_ref,
   1102                                            disc_pkt_t *probe_pkt,
   1103                                            int local_idx,
   1104                                            int entry_count);
   1105 STATIC int stk_port_find(cpudb_ref_t db_ref, int unit, int port);
   1106 STATIC void probe_pkt_forward(cpudb_ref_t db_ref,
   1107                               disc_pkt_t *probe_pkt,
   1108                               int entry_count, int rx_sp_idx, int fdo);
   1109 STATIC int disc_config_send(cpudb_ref_t db_ref);
   1110 
   1111 STATIC bcm_rx_t disc_config_pkt_handler(cpudb_key_t src_key,
   1112                                         int client_id,
   1113                                         bcm_pkt_t *pkt,
   1114                                         uint8 *payload,
   1115                                         int payload_len,
   1116                                         void *cookie);
   1117 STATIC int route_entry_process(uint8 *buf, cpudb_entry_t *entry, int version);
   1118 
   1119 /*
   1120  * The following routines returns the buffer size for
   1121  * routing packet entry, as follows:
   1122  *
   1123  *   Route-entry = Route-base + Stk-entries
   1124  *   Stk-entries = Stk-entry * num-stk-ports
   1125  */
   1126 
   1127 /* Stack Entry */
   1128 STATIC int
   1129 stk_entry_bytes_get(int version)
   1130 {
   1131     int bytes = 0;
   1132 
   1133     switch (version) {
   1134     case DISCOVERY_VERSION_0:
   1135         bytes = STK_ENTRY_RX_IDX_OFS + sizeof(uint32);
   1136         break;
   1137     case DISCOVERY_VERSION_1:
   1138         bytes = STK_ENTRY_BFLAGS_OFS + sizeof(uint32);
   1139         break;
   1140     case DISCOVERY_VERSION_2:
   1141     default:
   1142         bytes = STK_ENTRY_PORT_OFS + sizeof(uint32);
   1143         break;
   1144     }
   1145 
   1146     return bytes;
   1147 }
   1148 
   1149 /* Route Entry */
   1150 STATIC int
   1151 route_entry_bytes_get(int num_sp, int num_units, int version)
   1152 {
   1153     return (ROUTE_BASE_BYTES(num_units) +
   1154             (num_sp * stk_entry_bytes_get(version)));
   1155 }
   1156 
   1157 STATIC void
   1158 routing_pkt_process(cpudb_ref_t db_ref, disc_pkt_t *routing_pkt)
   1159 {
   1160     int entry_count;
   1161     uint8 *buf;
   1162     uint8 *buf_upper_limit;
   1163     int i;
   1164     cpudb_entry_t *entry;
   1165     cpudb_key_t key;
   1166     int bytes;
   1167     int stk_count;
   1168     int num_units;
   1169     int src_dseq_num;
   1170     int dseq_num;
   1171     int version;
   1172 
   1173     if (routing_pkt->pkt_buf == NULL) {
   1174             ++disc_rx_pkt_err;
   1175             ++disc_tot_err;
   1176             return;
   1177     }
   1178     if (routing_pkt->len == 0) {
   1179             ++disc_rx_pkt_err;
   1180             ++disc_tot_err;
   1181             bcm_rx_free(routing_pkt->rx_unit, routing_pkt->pkt_buf);
   1182             routing_pkt->pkt_buf = NULL;
   1183             return;
   1184     }
   1185 
   1186     /* buf points to start of disc packet */
   1187     buf = routing_pkt->pkt_buf + CPUTRANS_HEADER_BYTES;
   1188     buf_upper_limit = routing_pkt->pkt_buf + routing_pkt->len;
   1189     entry_count = (int)(buf[ENTRY_COUNT_OFS]);
   1190     UNPACK_LONG(&buf[SRC_DSEQ_NUM_OFS], src_dseq_num);
   1191     version = buf[DISC_VER_OFS];
   1192     buf += DISC_HEADER_BYTES;
   1193 
   1194     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   1195               (BSL_META("disc: Processing rte pkt\n")));
   1196 
   1197     /* If src is not in DB or sequence numbers mismatch, discard pkt */
   1198     CPUDB_KEY_SEARCH(db_ref, routing_pkt->src_key, entry);
   1199     if (entry == NULL) {
   1200         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1201                     (BSL_META("disc: Rte src key not found " CPUDB_KEY_FMT_EOLN),
   1202                      CPUDB_KEY_DISP(routing_pkt->src_key)));
   1203         bcm_rx_free(routing_pkt->rx_unit, routing_pkt->pkt_buf);
   1204         routing_pkt->pkt_buf = NULL;
   1205         return;
   1206     }
   1207     if (entry->base.dseq_num != src_dseq_num) {
   1208         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1209                     (BSL_META("disc: Rte src dseq num mismatch; pkt %d, db %d.\n"),
   1210                      src_dseq_num, entry->base.dseq_num));
   1211         bcm_rx_free(routing_pkt->rx_unit, routing_pkt->pkt_buf);
   1212         routing_pkt->pkt_buf = NULL;
   1213         return;
   1214     }
   1215 
   1216     for (i = 0; i < entry_count; i++) {
   1217 
   1218         if (buf > buf_upper_limit) {
   1219             ++disc_rx_pkt_err;
   1220             ++disc_tot_err;
   1221             break;
   1222         }
   1223 
   1224         /*
   1225          * Get entry CPU key and discovery sequence number
   1226          *
   1227          * It is possible for a routing packet to arrive sooner
   1228          * than a probe packet for a given remote CPU. In this case,
   1229          * the entry for the CPU will be created.
   1230          *
   1231          * The CPU entry must contain the correct discovery
   1232          * sequence number to avoid sequence mismatch.
   1233          */
   1234         CPUDB_KEY_UNPACK(buf, key);
   1235         UNPACK_LONG(&buf[ROUTE_DSEQ_NUM_OFS], dseq_num);
   1236 
   1237         /* Add entry to DB if not already present. */
   1238         entry = _disc_key_resolve(db_ref, key, &buf[ROUTE_MAC_OFS], dseq_num);
   1239 
   1240         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   1241                   (BSL_META("disc: Rte entry %p key " CPUDB_KEY_FMT_EOLN),
   1242                    entry, CPUDB_KEY_DISP(key)));
   1243         if (entry == NULL || entry == db_ref->local_entry) {
   1244             UNPACK_LONG(&buf[ROUTE_NUM_UNITS_OFS], num_units);
   1245             UNPACK_LONG(&buf[ROUTE_STK_COUNT_OFS], stk_count);
   1246             buf += route_entry_bytes_get(stk_count, num_units, version);
   1247             continue;
   1248         }
   1249 
   1250         bytes = route_entry_process(buf, entry, version);
   1251         if (bytes < 0) {
   1252             ++disc_rx_pkt_err;
   1253             ++disc_tot_err;
   1254             break;
   1255         }
   1256         buf += bytes;
   1257     }
   1258 
   1259     bcm_rx_free(routing_pkt->rx_unit, routing_pkt->pkt_buf);
   1260     routing_pkt->pkt_buf = NULL;
   1261 }
   1262 
   1263 STATIC int
   1264 disc_done_check(cpudb_ref_t db_ref, int *rv, int unreg)
   1265 {
   1266     sal_usecs_t cur_time;
   1267     int done = TRUE;
   1268     int dt;
   1269 
   1270     cur_time = sal_time_usecs();
   1271 
   1272     dt = SAL_USECS_SUB(cur_time, disc_start_time);
   1273     DISC_LOCK;
   1274     if (disc_flags & DF_DISC_ERROR) { /* Error occurred */
   1275         *rv = BCM_E_FAIL;
   1276     } else if (disc_flags & DF_DISC_SUCCESS) { /* Completed successfully */
   1277         *rv = BCM_E_NONE;
   1278     } else if (disc_flags & DF_DISC_ABORT) { /* Exit forced */
   1279         *rv = disc_abort_rv;
   1280     } else if (dt < 0 || dt > disc_timeout_us) {
   1281         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   1282                   (BSL_META("disc ERR:  Timeout occurred\n")));
   1283         disc_flags |= DF_DISC_TIMEOUT | DF_DISC_ERROR;
   1284         *rv = BCM_E_TIMEOUT;
   1285     } else if (disc_tot_err >= DISC_MAX_ERRORS) {
   1286         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   1287                   (BSL_META("disc ERR:  Error limit exceeded\n")));
   1288         *rv = BCM_E_FAIL;
   1289     } else {
   1290         done = FALSE;
   1291     }
   1292 
   1293     if (done) {
   1294         /* Clear the running/abort flags for future calls */
   1295         disc_flags &= ~DF_DISC_RUNNING;
   1296         queues_clear();
   1297     }
   1298 
   1299     DISC_UNLOCK;
   1300 
   1301     if (unreg && done) {
   1302         /* Unregister callbacks */
   1303         next_hop_unregister(disc_rx_pkt, DISC_NH_PKT_TYPE);
   1304     }
   1305 
   1306     return done;
   1307 }
   1308 
   1309 
   1310 STATIC INLINE int
   1311 all_stk_entries_resolved(cpudb_ref_t db_ref)
   1312 {
   1313     cpudb_entry_t *key_entry, *entry;
   1314     int stk_idx;
   1315     uint32 flags;
   1316 
   1317     CPUDB_FOREACH_ENTRY(db_ref, entry) {
   1318         if (entry->base.num_stk_ports <= 0 && entry != db_ref->local_entry) {
   1319             /* Haven't gotten routing info on this non-local entry yet. */
   1320             return FALSE;
   1321         }
   1322         for (stk_idx = 0; stk_idx < entry->base.num_stk_ports;
   1323              stk_idx++) {
   1324             flags = entry->sp_info[stk_idx].flags;
   1325             if (flags & CPUDB_SPF_NO_LINK ||
   1326                 flags & CPUDB_SPF_INACTIVE ||
   1327                 flags & CPUDB_SPF_ETHERNET) {
   1328                 continue;  /* Ignore disabled ports */
   1329             }
   1330             if (!(flags & CPUDB_SPF_TX_RESOLVED) ||
   1331                 !(flags & CPUDB_SPF_RX_RESOLVED)) {
   1332                 /* Missing info on this stack port */
   1333                 return FALSE;
   1334             }
   1335 
   1336             /*
   1337              * CPU keys can only be added by probe packets.  It's
   1338              * possible they're referenced by a stack port, but
   1339              * have not been added to the local DB yet.
   1340              */
   1341             CPUDB_KEY_SEARCH(db_ref,
   1342                        entry->sp_info[stk_idx].tx_cpu_key, key_entry);
   1343             if (key_entry == NULL) {
   1344                 return FALSE;
   1345             }
   1346             CPUDB_KEY_SEARCH(db_ref,
   1347                        entry->sp_info[stk_idx].rx_cpu_key, key_entry);
   1348             if (key_entry == NULL) {
   1349                 return FALSE;
   1350             }
   1351         }
   1352     }
   1353 
   1354     return TRUE;
   1355 }
   1356 
   1357 /* Check for config timeout and if so, mark ports w/o PP_SEEN as inactive */
   1358 
   1359 static INLINE void
   1360 inactive_ports_check(cpudb_ref_t db_ref)
   1361 {
   1362     cpudb_entry_t *lentry;
   1363     sal_usecs_t cur_time;
   1364     int stk_idx;
   1365     int dt;
   1366 
   1367     if (LOCAL_FLAGS(db_ref) & CPUDB_F_INACTIVE_MARKED) {
   1368         return;
   1369     }
   1370 
   1371     cur_time = sal_time_usecs();
   1372 
   1373     dt = SAL_USECS_SUB(cur_time, disc_start_time);
   1374 
   1375     /*
   1376      * Run through local stack ports; if no probe packet seen on a
   1377      * port w/i timeout, mark as inactive.
   1378      */
   1379     if (dt < 0 || dt > disc_cfg_to_us) {
   1380         lentry = db_ref->local_entry;
   1381         for (stk_idx = 0; stk_idx < lentry->base.num_stk_ports;
   1382              stk_idx++) {
   1383             if (PP_SEEN_GET(stk_idx) == 0) {
   1384                 STK_FLAGS(db_ref, stk_idx) |= CPUDB_SPF_INACTIVE;
   1385                 LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1386                             (BSL_META("DISC: Marking SP %d inactive\n"),
   1387                              stk_idx));
   1388             }
   1389         }
   1390         LOCAL_FLAGS(db_ref) |= CPUDB_F_INACTIVE_MARKED;
   1391     }
   1392 }
   1393 
   1394 
   1395 /*
   1396  * Global flags for ATP config client; applications may make
   1397  * this "no-ACK".  This must be set before discovery (or stack
   1398  * task) is started.
   1399  */
   1400 uint32 disc_config_atp_flags = DISC_CONFIG_ATP_FLAGS_DEFAULT;
   1401 
   1402 /* Set up ATP once local complete is known */
   1403 static INLINE void
   1404 lc_atp_setup(cpudb_ref_t db_ref)
   1405 {
   1406     int rv;
   1407     cpudb_entry_t *entry;
   1408 
   1409     /* Register CONFIG client */
   1410     rv = atp_register(DISC_CONFIG_CLIENT_ID, disc_config_atp_flags,
   1411                       disc_config_pkt_handler, db_ref, disc_cos, disc_vlan);
   1412     if (BCM_FAILURE(rv)) {
   1413         LOG_WARN(BSL_LS_TKS_DISCOVER,
   1414                  (BSL_META("disc WARN: could not register cfg client\n")));
   1415     }
   1416 
   1417     /* Make sure ATP knows about all keys in DB */
   1418     CPUDB_FOREACH_ENTRY(db_ref, entry) {
   1419         if (entry != db_ref->local_entry) {
   1420             atp_key_add(entry->base.key,
   1421                 entry->flags & CPUDB_F_IS_LOCAL);
   1422         }
   1423     }
   1424 }
   1425 
   1426 /*
   1427  * The master entry in DB has been set (first time)
   1428  * Do what needs to be done
   1429  */
   1430 
   1431 static INLINE void
   1432 master_is_set(cpudb_ref_t db_ref)
   1433 {
   1434     int rv;
   1435     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1436                 (BSL_META("Disc: Master is set to " CPUDB_KEY_FMT " w/ dseq num %d\n"),
   1437                  CPUDB_KEY_DISP(db_ref->master_entry->base.key),
   1438                  db_ref->master_entry->base.dseq_num));
   1439     if (db_ref->master_entry == db_ref->local_entry) {
   1440         /* Send out config pkts */
   1441         rv = disc_config_send(db_ref);
   1442         if (BCM_FAILURE(rv)) {
   1443             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   1444                       (BSL_META("Disc ERR: cfg send returns %d: %s\n"),
   1445                        rv, bcm_errmsg(rv)));
   1446             DISC_LOCK;
   1447             disc_flags |= DF_DISC_ERROR;
   1448             DISC_UNLOCK;
   1449         } else {
   1450             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1451                         (BSL_META("Disc: master cfg pkts out sn %d\n"),
   1452                          db_ref->master_entry->base.dseq_num));
   1453        }
   1454     }
   1455 }
   1456 
   1457 /* Local configuration is complete; continue */
   1458 static INLINE void
   1459 local_complete_set(cpudb_ref_t db_ref)
   1460 {
   1461     /* If we get here, the local DB is complete; make callback */
   1462     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1463                 (BSL_META("Disc: Local DB complete\n")));
   1464     LOCAL_FLAGS(db_ref) |= CPUDB_F_LOCAL_COMPLETE;
   1465 
   1466     lc_atp_setup(db_ref);
   1467 }
   1468 
   1469 static INLINE void
   1470 global_complete_set(cpudb_ref_t db_ref)
   1471 {
   1472     int rv;
   1473 
   1474     LOCAL_FLAGS(db_ref) |= CPUDB_F_GLOBAL_COMPLETE;
   1475     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1476                 (BSL_META("Disc: All DB entries report complete\n")));
   1477 
   1478     if (db_ref->master_entry != NULL) {
   1479         /* Assume this means that we're not the master. */
   1480         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1481                     (BSL_META("DISC: Global done, but master known.\n")));
   1482     }
   1483 
   1484     /* Attempt to elect master. If the old election callback interface
   1485        is present, use the old one, otherwise use the new one. */
   1486     if (disc_start_elect) {
   1487         rv = disc_start_elect(db_ref);
   1488     } else {
   1489         rv = disc_m_elect(db_ref, disc_m_user_data);
   1490     }
   1491     if (rv != BCM_E_NONE) {
   1492         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1493                     (BSL_META("Disc: Master elect returns %d causing abort: %s\n"),
   1494                      rv, rv == DISC_RESTART_NEW_SEQ ? "Restarting" :
   1495                      bcm_errmsg(rv)));
   1496         DISC_LOCK;
   1497         disc_flags |= DF_DISC_ABORT;
   1498         disc_abort_rv = rv;
   1499         DISC_UNLOCK;
   1500     } else if (db_ref->master_entry == NULL) {
   1501         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1502                     (BSL_META("Disc WARN:  Global done; no master\n")));
   1503     } else {
   1504         master_is_set(db_ref);
   1505     }        
   1506 }       
   1507         
   1508 /*      
   1509  * Check for completed discovery and set flags appropriately
   1510  * Updates local CPU flags when all other CPUs indicate disc done
   1511  */     
   1512         
   1513 STATIC void
   1514 disc_status_update(cpudb_ref_t db_ref)
   1515 {       
   1516     cpudb_entry_t *entry;
   1517     int rv, flags = 0;
   1518         
   1519     /* First, see if cfg pkt should go out; if so, discovery done */
   1520     if ((disc_flags & DF_SEND_CFG_PKT) &&
   1521         !(disc_flags & DF_CFG_PKT_SENT)) {
   1522         rv = disc_config_send(db_ref);
   1523         if (BCM_FAILURE(rv)) {
   1524             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   1525                       (BSL_META("DISC:  Failed to respond with cfg pkt: %s\n"),
   1526                        bcm_errmsg(rv)));
   1527         } else {
   1528             flags |= DF_DISC_SUCCESS;
   1529         }
   1530 
   1531         flags |= DF_CFG_PKT_SENT;
   1532 
   1533         if (flags & DF_DISC_SUCCESS) {
   1534             DISC_LOCK;
   1535             disc_flags |= flags;
   1536             DISC_UNLOCK;
   1537             return;
   1538         }
   1539     }
   1540 
   1541     /* Look for configuration timeout; mark inactive ports */
   1542     inactive_ports_check(db_ref);
   1543 
   1544     if (!(LOCAL_FLAGS(db_ref) & CPUDB_F_LOCAL_COMPLETE)) {
   1545         /* Run through all stack ports in CPUDB and see if resolved */
   1546         if (!all_stk_entries_resolved(db_ref)) { 
   1547             return;  /* Nope, continue processing */
   1548         }
   1549         /* All info in DB is resolved.  Set local complete */
   1550         local_complete_set(db_ref);
   1551     }
   1552 
   1553     if (!(LOCAL_FLAGS(db_ref) & CPUDB_F_GLOBAL_COMPLETE)) {
   1554         /* Now check whether every CPU entry has local complete set */
   1555         CPUDB_FOREACH_ENTRY(db_ref, entry) {
   1556             if (!(entry->flags & CPUDB_F_LOCAL_COMPLETE)) {
   1557                 return;
   1558             }
   1559         }
   1560         global_complete_set(db_ref);
   1561     } else if (db_ref->local_entry == db_ref->master_entry) {
   1562         /* See if all (other) CPUs have responded with cfg pkt ACKs. */
   1563         CPUDB_FOREACH_ENTRY(db_ref, entry) {
   1564             if (entry == db_ref->master_entry) {
   1565                 continue;
   1566             }
   1567             if (!(entry->flags & CPUDB_F_CONFIG_IN)) {
   1568                 return;
   1569             }
   1570         }
   1571 
   1572         /* All CPU entries have indicated they accept the configuration */
   1573         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1574                     (BSL_META("DISC:  All DB entries accept config.\n")));
   1575         flags |= DF_DISC_SUCCESS;
   1576         DISC_LOCK;
   1577         disc_flags |= flags;
   1578         DISC_UNLOCK;
   1579     }
   1580 }
   1581 
   1582 /* Check if a local port is duplex; if so, tell next hop */
   1583 #define CHECK_IS_DUPLEX(db_ref, sp)                                   \
   1584     if ((STK_FLAGS(db_ref, sp) & CPUDB_SPF_TX_RESOLVED) &&            \
   1585             (STK_FLAGS(db_ref, sp) & CPUDB_SPF_RX_RESOLVED) &&        \
   1586             (CPUDB_KEY_COMPARE(STK_PORT(db_ref, sp).rx_cpu_key,       \
   1587                        STK_PORT(db_ref, sp).tx_cpu_key) == 0)) do {   \
   1588         STK_FLAGS(db_ref, sp) |= CPUDB_SPF_DUPLEX;                    \
   1589         next_hop_port_add(STKP_UNIT(db_ref, sp),                      \
   1590                           STKP_PORT(db_ref, sp), TRUE);               \
   1591     } while (0)
   1592 
   1593 
   1594 /*
   1595  * Set the RX/TX key if not already known; check for duplex
   1596  * Also checks if "local config" already done; if this
   1597  * occurred on a port that was assumed to be inactive,
   1598  * discovery restart is required.
   1599  */
   1600 
   1601 static INLINE int
   1602 stk_port_rx_set(cpudb_ref_t db_ref, int sp, cpudb_key_t cpu_key,
   1603                 int remote_sp_idx, uint32 flags)
   1604 {
   1605     if (!(STK_FLAGS(db_ref, sp) & CPUDB_SPF_RX_RESOLVED)) {
   1606         if (LOCAL_FLAGS(db_ref) & CPUDB_F_LOCAL_COMPLETE ||
   1607             STK_FLAGS(db_ref, sp) & CPUDB_SPF_INACTIVE) {
   1608             /* Signal restart request to controlling application */
   1609             DISC_LOCK;
   1610             disc_flags |= DF_DISC_ABORT;
   1611             disc_abort_rv = DISC_RESTART_NEW_SEQ;
   1612             DISC_UNLOCK;
   1613             return -1;
   1614         }
   1615         CPUDB_KEY_COPY(STK_PORT(db_ref, sp).rx_cpu_key, (cpu_key));
   1616         STK_PORT(db_ref, sp).rx_stk_idx = (int)(remote_sp_idx);
   1617         STK_FLAGS(db_ref, sp) |= CPUDB_SPF_RX_RESOLVED | (flags);
   1618         DISC_LOCK;
   1619         disc_flags |= DF_DB_UPDATED;
   1620         DISC_UNLOCK;
   1621         CHECK_IS_DUPLEX(db_ref, sp);
   1622         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1623                     (BSL_META("disc: Set RX for sp %d\n"),
   1624                      sp));
   1625     }
   1626 
   1627     return 0;
   1628 }
   1629 
   1630 /* Set the TX key if not already known; check for duplex */
   1631 static INLINE int
   1632 stk_port_tx_set(cpudb_ref_t db_ref, int sp, cpudb_key_t cpu_key,
   1633                 int remote_sp_idx, uint32 flags)
   1634 {
   1635     if (!(STK_FLAGS(db_ref, sp) & CPUDB_SPF_TX_RESOLVED)) {
   1636         if (LOCAL_FLAGS(db_ref) & CPUDB_F_LOCAL_COMPLETE ||
   1637             STK_FLAGS(db_ref, sp) & CPUDB_SPF_INACTIVE) {
   1638             /* Signal restart request to controlling application */
   1639             DISC_LOCK;
   1640             disc_flags |= DF_DISC_ABORT;
   1641             disc_abort_rv = DISC_RESTART_NEW_SEQ;
   1642             DISC_UNLOCK;
   1643             return -1;
   1644         }
   1645         CPUDB_KEY_COPY(STK_PORT(db_ref, sp).tx_cpu_key, (cpu_key));
   1646         STK_PORT(db_ref, sp).tx_stk_idx = (int)(remote_sp_idx);
   1647         STK_FLAGS(db_ref, sp) |= CPUDB_SPF_TX_RESOLVED | (flags);
   1648         DISC_LOCK;
   1649         disc_flags |= DF_DB_UPDATED | DF_TX_KNOWN;
   1650         DISC_UNLOCK;
   1651         CHECK_IS_DUPLEX(db_ref, sp);
   1652         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1653                     (BSL_META("disc: Set TX for sp %d\n"),
   1654                      sp));
   1655     }
   1656 
   1657     return 0;
   1658 }
   1659 
   1660 
   1661 /*
   1662  * Get the type out of a discovery packet.  Does not check if it's
   1663  * a discovery packet
   1664  */
   1665 
   1666 int
   1667 disc_pkt_type_get(uint8 *pkt_buf)
   1668 {
   1669     uint8 *data_start;
   1670 
   1671     data_start = pkt_buf + CPUTRANS_HEADER_BYTES;
   1672     return (int)data_start[LOCAL_PKT_TYPE_OFS];
   1673 }
   1674 
   1675 
   1676 /*
   1677  * Function:
   1678  *      disc_probe_pkt_new
   1679  * Purpose:
   1680  *      Determine if pkt_buf holds a discovery packet with new
   1681  *      probe information.
   1682  * Returns:
   1683  *      FALSE if not a new probe packet
   1684  *      TRUE if it is a new probe packet
   1685  * Notes:
   1686  *      Should not be called when discovery is running
   1687  *      This is used by stack task to see if new info has
   1688  *      appeared and discovery should be restarted.
   1689  */
   1690 
   1691 int
   1692 disc_probe_pkt_new(cpudb_ref_t db_ref, cpudb_key_t src_key, uint8 *pkt_buf)
   1693 {
   1694     int pkt_type;
   1695     uint8 *data_start, *entry_buf;
   1696     cpudb_entry_t *entry;
   1697     int i;
   1698     cpudb_key_t key;
   1699     int entry_count;
   1700     int dseq_num;
   1701 
   1702     /* First, is it a discovery probe packet */
   1703     /* pkt_buf points to start of packet */
   1704     data_start = pkt_buf + CPUTRANS_HEADER_BYTES;
   1705     pkt_type = (int)data_start[LOCAL_PKT_TYPE_OFS];
   1706 
   1707     if (pkt_type != DISC_PKT_TYPE_PROBE) {
   1708         return FALSE;  /* Not a probe packet */
   1709     }
   1710 
   1711     /* If no db, definitely a new packet */
   1712     if (db_ref == CPUDB_REF_NULL) {
   1713         return TRUE;
   1714     }
   1715 
   1716     /* If new source key, it's a new packet */
   1717     CPUDB_KEY_SEARCH(db_ref, src_key, entry);
   1718     if (entry == NULL) {
   1719         return TRUE;
   1720     }
   1721 
   1722     entry_count = (int)(data_start[ENTRY_COUNT_OFS]);
   1723     entry_buf = data_start + PROBE_ENTRY_OFS(0);
   1724 
   1725     /*
   1726      * If sequence number different, this is harder.  For now only return if
   1727      * greater than old seq num
   1728      */
   1729     for (i = 0; i < entry_count; i++) {
   1730         CPUDB_KEY_UNPACK(entry_buf, key);
   1731         CPUDB_KEY_SEARCH(db_ref, key, entry);
   1732         UNPACK_LONG(&entry_buf[PROBE_DSEQ_NUM_OFS], dseq_num);
   1733         if (entry == NULL) {
   1734             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1735                         (BSL_META("disc probe chk: New key "
   1736                                   CPUDB_KEY_FMT_EOLN), 
   1737                          CPUDB_KEY_DISP(src_key)));
   1738             return TRUE;   /* New CPU key */
   1739         } else {
   1740             if (dseq_num - entry->base.dseq_num > 0) {
   1741                 /* pkt SN newer than DB */
   1742                 LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1743                             (BSL_META("disc probe chk: New SN %d for key "
   1744                                       CPUDB_KEY_FMT_EOLN),
   1745                              dseq_num,
   1746                              CPUDB_KEY_DISP(entry->base.key)));
   1747                 return TRUE;
   1748             }
   1749         }
   1750         entry_buf += PROBE_ENTRY_BYTES;
   1751     }
   1752 
   1753     return FALSE;
   1754 }
   1755 
   1756 #define PROBE_PKT_OK          0
   1757 #define PROBE_PKT_DROP       -1
   1758 #define PROBE_PKT_DB_CLEAR   -2
   1759 #define PROBE_PKT_MEMORY     -3
   1760 
   1761 
   1762 /*
   1763  * If found, return index of entry in a probe pkt containing the local mac.
   1764  * buf points to beginning of list of entries.
   1765  */
   1766 STATIC INLINE int
   1767 probe_pkt_local_cpu_key(cpudb_ref_t db_ref, uint8 *buf, int entry_count)
   1768 {
   1769     int i;
   1770     cpudb_key_t key;
   1771 
   1772     for (i = 0; i < entry_count; i++) {
   1773         CPUDB_KEY_UNPACK(buf, key);
   1774         if (!CPUDB_KEY_COMPARE(key, LOCAL_KEY(db_ref))) {
   1775             LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   1776                       (BSL_META("disc: Local key in probe pkt, %d\n"),
   1777                        i));
   1778             return i;
   1779         }
   1780 
   1781         buf += PROBE_ENTRY_BYTES;
   1782     }
   1783 
   1784     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   1785               (BSL_META("disc: Local key not in probe pkt\n")));
   1786     return -1;
   1787 }
   1788 
   1789 STATIC int probe_pkt_dseq_num_check(cpudb_ref_t db_ref,
   1790                                           uint8 *pkt_start,
   1791                                           int entry_count);
   1792 #define _DROP_PKT do { \
   1793         bcm_rx_free(probe_pkt->rx_unit, probe_pkt->pkt_buf); \
   1794         probe_pkt->pkt_buf = NULL; \
   1795     } while (0)
   1796 
   1797 /* Process a probe packet */
   1798 
   1799 
   1800 
   1801 
   1802 STATIC void
   1803 probe_pkt_process(cpudb_ref_t db_ref, disc_pkt_t *probe_pkt)
   1804 {
   1805     int entry_count, last_entry;
   1806     uint8 *entry_buf;
   1807     int remote_tx_idx;
   1808     int local_idx;
   1809     cpudb_key_t key;
   1810     cpudb_entry_t *remote_ent;
   1811     int rx_sp_idx;
   1812     int dseq_num;
   1813     int drop_pkt;
   1814     int result;
   1815 
   1816 
   1817     /* See disc_int.h for packet format description */
   1818 
   1819     /* Unpack the number of entries */
   1820     entry_count = (int)(probe_pkt->data[ENTRY_COUNT_OFS]);
   1821     last_entry = entry_count - 1;
   1822 
   1823     /* First, check RX for stk port on which pkt received */
   1824     entry_buf = probe_pkt->data + PROBE_ENTRY_OFS(last_entry);
   1825     CPUDB_KEY_UNPACK(entry_buf, key);
   1826     remote_tx_idx = (int)(entry_buf[PROBE_TX_IDX_OFS]);
   1827     UNPACK_LONG(&entry_buf[PROBE_DSEQ_NUM_OFS], dseq_num);
   1828 
   1829     /* Record local connection for RX of this stack port */
   1830     rx_sp_idx = stk_port_find(db_ref, probe_pkt->rx_unit,
   1831                               probe_pkt->rx_port);
   1832     if (rx_sp_idx < 0) {
   1833         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   1834                   (BSL_META("disc ERR: Could not find stk port for "
   1835                    "probe from unit %d port %d\n"),
   1836                    probe_pkt->rx_unit, probe_pkt->rx_port));
   1837         ++disc_rx_pkt_err;
   1838         ++disc_tot_err;
   1839         _DROP_PKT;
   1840         return;
   1841     }
   1842 
   1843     if (!(STK_FLAGS(db_ref, rx_sp_idx) & CPUDB_SPF_RX_RESOLVED)) {
   1844         remote_ent = _disc_key_resolve(db_ref, key,
   1845                                        &entry_buf[PROBE_MAC_OFS], dseq_num);
   1846         if (remote_ent == NULL) {
   1847             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   1848                       (BSL_META("disc ERR: Error adding KEY to DB\n")));
   1849             ++disc_rx_pkt_err;
   1850             ++disc_tot_err;
   1851             _DROP_PKT;
   1852             return;
   1853         } else if (stk_port_rx_set(db_ref, rx_sp_idx, key,
   1854                                    remote_tx_idx, 0) < 0) {
   1855             _DROP_PKT;
   1856             return;
   1857         }
   1858     }
   1859 
   1860     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   1861               (BSL_META("disc: Prb pkt Rmt TX %d RX %d key "
   1862                CPUDB_KEY_FMT " cnt %d\n"), remote_tx_idx,
   1863                rx_sp_idx, CPUDB_KEY_DISP(key), entry_count));
   1864 
   1865     /*
   1866      * Update the key entries in the DB and check for changed
   1867      * sequence numbers
   1868      */
   1869     entry_buf = probe_pkt->data + PROBE_ENTRY_OFS(0);
   1870     drop_pkt = TRUE;
   1871 
   1872     if (entry_count > disc_ttl) {
   1873         /* A probe packet with a number of entries greater than the
   1874            TTL means that this is a probe forwarded because of a
   1875            it was found to have a sequence number greater than
   1876            what was found in the receivers database. */
   1877         result = PROBE_PKT_DB_CLEAR;
   1878         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1879                     (BSL_META("disc: probe entry count %d > %d - forcing forwarding\n"),
   1880                      entry_count, disc_ttl));
   1881     } else {
   1882         result = probe_pkt_dseq_num_check(db_ref, entry_buf, entry_count);
   1883     }
   1884 
   1885     switch (result) {
   1886     case PROBE_PKT_OK:
   1887         drop_pkt = FALSE;
   1888         break;
   1889     case PROBE_PKT_DROP:
   1890         break;
   1891     case PROBE_PKT_DB_CLEAR:        /* Clear DB and process packet */
   1892         DISC_LOCK;
   1893         disc_flags |= DF_DISC_ABORT;
   1894         disc_abort_rv = DISC_RESTART_REQUEST;
   1895         DISC_UNLOCK;
   1896         break;
   1897     case PROBE_PKT_MEMORY:
   1898         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   1899                     (BSL_META("disc: Could not add probe entry\n")));
   1900         break;
   1901     default:
   1902         assert(0 && "Bad rv dseq_num_check");
   1903         break;
   1904     }
   1905 
   1906     /* Look for local CPU key in packet; */               
   1907     local_idx = probe_pkt_local_cpu_key(db_ref,
   1908                                         &probe_pkt->data[ENTRY_START_OFS],
   1909                                         entry_count);
   1910 
   1911     if (!DISC_EXIT && !drop_pkt) {  /* Default behavior */
   1912         if (local_idx < 0) {
   1913             probe_pkt_forward(db_ref, probe_pkt, entry_count,
   1914                               rx_sp_idx, TRUE);
   1915         } else {
   1916             /* Local CPU key is in packet; analyze it */
   1917             probe_pkt_with_local_key(db_ref, probe_pkt,
   1918                                      local_idx, entry_count);
   1919         }
   1920 
   1921     } else if ((result == PROBE_PKT_DB_CLEAR) && (local_idx < 0)) {
   1922         /*
   1923          * When discovery is restarted due to a probe packet with
   1924          * a new sequence number, always forward the packet to
   1925          * trigger discovery to restart in the rest of the systems.
   1926          *
   1927          * Increasing the TTL by 1 if the ttl is at one will ensure
   1928          * such forwarding will occur for packets that are about to be
   1929          * dropped.
   1930          */
   1931         uint8 *ttl_ptr = &probe_pkt->data[DISC_PROBE_TTL_OFS];
   1932         
   1933         if (*ttl_ptr == 1) {
   1934             (*ttl_ptr)++;
   1935         }
   1936         probe_pkt_forward(db_ref, probe_pkt, entry_count,
   1937                           rx_sp_idx, FALSE);
   1938         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   1939                   (BSL_META("Restart discovery, forward probe from "
   1940                             CPUDB_KEY_FMT_EOLN),
   1941                    CPUDB_KEY_DISP(key)));
   1942     }
   1943 
   1944     /* Free the buffer */
   1945     bcm_rx_free(probe_pkt->rx_unit, probe_pkt->pkt_buf);
   1946     probe_pkt->pkt_buf = NULL;
   1947 
   1948     return;
   1949 }
   1950 
   1951 #undef _DROP_PKT
   1952 
   1953 /* Check stack port data from the packet */
   1954 
   1955 static INLINE void
   1956 stk_port_analyze(cpudb_entry_t *entry, int sp_idx, uint8 *buf, int new_entry,
   1957                  int version)
   1958 {
   1959     int new_flags, flags;
   1960     cpudb_stk_port_t *sp_p;
   1961 
   1962     sp_p = &entry->sp_info[sp_idx];
   1963 
   1964     UNPACK_U32_INCR(buf, new_flags);
   1965     flags = sp_p->flags;
   1966 
   1967     /* If new entry or new flags are set, copy data from pkt */
   1968     if (new_entry || ((flags | new_flags) != flags)) { /* Unpack */
   1969         sp_p->flags = new_flags;
   1970         CPUDB_KEY_UNPACK(buf, sp_p->tx_cpu_key);
   1971         buf += CPUDB_KEY_BYTES;
   1972         UNPACK_U32_INCR(buf, sp_p->tx_stk_idx);
   1973         CPUDB_KEY_UNPACK(buf, sp_p->rx_cpu_key);
   1974         buf += CPUDB_KEY_BYTES;
   1975         UNPACK_U32_INCR(buf, sp_p->rx_stk_idx);
   1976         if (version >= DISCOVERY_VERSION_1) {
   1977             UNPACK_U32_INCR(buf, entry->base.stk_ports[sp_idx].weight);
   1978             UNPACK_U32_INCR(buf, entry->base.stk_ports[sp_idx].bflags);
   1979         }
   1980         if (version >= DISCOVERY_VERSION_2) {
   1981             UNPACK_U32_INCR(buf, entry->base.stk_ports[sp_idx].unit);
   1982             UNPACK_U32_INCR(buf, entry->base.stk_ports[sp_idx].port);
   1983         } else {
   1984             entry->base.stk_ports[sp_idx].unit = -1;
   1985             entry->base.stk_ports[sp_idx].port = -1;
   1986         }
   1987         DISC_LOCK;
   1988         disc_flags |= DF_DB_UPDATED;
   1989         DISC_UNLOCK;
   1990         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   1991                   (BSL_META("disc:  Stk update flags %x\n"),
   1992                    new_flags));
   1993     }
   1994 }
   1995 
   1996 #if defined(BROADCOM_DEBUG)
   1997 STATIC int
   1998 _check_route_pkt(uint8 *buf, cpudb_entry_t *entry)
   1999 {
   2000     int stk_port_count;
   2001 
   2002     /* Could check all of entry, but just check num stk ports for now */
   2003     if (entry->base.num_stk_ports > 0) {  /* Have seen before */
   2004         UNPACK_LONG(&buf[ROUTE_STK_COUNT_OFS], stk_port_count);
   2005         if (entry->base.num_stk_ports != stk_port_count) {
   2006             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2007                       (BSL_META("disc ERR: stk port count mismatch for %x:%x\n"),
   2008                        entry->base.mac[4], entry->base.mac[5]));
   2009             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2010                       (BSL_META("    was %d. new %d\n"),
   2011                        entry->base.num_stk_ports, stk_port_count));
   2012             return BCM_E_FAIL;   /* Do not analyze further */
   2013         }
   2014     }
   2015 
   2016     return BCM_E_NONE;
   2017 }
   2018 #endif   /* BROADCOM_DEBUG */
   2019 
   2020 /*
   2021  * Process a DB entry pointed to by buf; buf points to the beginning
   2022  * of the routing entry (the key).
   2023  * Returns number of bytes processed on success.  < 0 if error
   2024  */
   2025 
   2026 STATIC int
   2027 route_entry_process(uint8 *buf, cpudb_entry_t *entry, int version)
   2028 {
   2029     uint32 combined_flags;
   2030     int new_entry = FALSE;
   2031     int sp_idx;
   2032     int i;
   2033     int num_units;
   2034     int num_stk_ports;
   2035     int dseq_num;
   2036     bcm_mac_t mac;
   2037     uint32 flags;
   2038     int slot_id;
   2039     int master_pri;
   2040     uint8 app_data[CPUDB_APP_DATA_BYTES];
   2041     uint32 base_flags;
   2042     CPUDB_BOARD_ID board_id;
   2043     int dest_unit;
   2044     int dest_port;
   2045 
   2046     /* Extract the Seq Number from the packet entry */
   2047     UNPACK_LONG(&buf[ROUTE_DSEQ_NUM_OFS], dseq_num);
   2048 
   2049     /* Do not update CPUDB with stale info from route, treat it as NOOP */
   2050     if (dseq_num < entry->base.dseq_num) {
   2051         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2052                     (BSL_META("disc: stale info for "
   2053                               CPUDB_KEY_FMT " seq %d->%d\n"),
   2054                      CPUDB_KEY_DISP(entry->base.key),
   2055                      entry->base.dseq_num, dseq_num));
   2056         UNPACK_LONG(&buf[ROUTE_NUM_UNITS_OFS], num_units);
   2057         UNPACK_LONG(&buf[ROUTE_STK_COUNT_OFS], num_stk_ports);
   2058         if (num_units > CPUDB_UNITS_MAX) {
   2059             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2060                       (BSL_META("disc ERR: Rte unit cnt %d > DB limit %d\n"),
   2061                        num_units, CPUDB_UNITS_MAX));
   2062             return BCM_E_FAIL;
   2063         }
   2064         if (num_stk_ports > CPUDB_STK_PORTS_MAX) {
   2065             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2066                       (BSL_META("disc ERR: Rte stk port cnt %d > DB limit %d\n"),
   2067                        num_stk_ports, CPUDB_STK_PORTS_MAX));
   2068             return BCM_E_FAIL;
   2069         }
   2070         return route_entry_bytes_get(num_stk_ports, num_units, version);
   2071     }
   2072 
   2073     /* Extract flags from packet entry data */
   2074     UNPACK_LONG(&buf[ROUTE_FLAGS_OFS], flags);
   2075 
   2076     /* Treat the higher seq number as a new entry. Higher than expected seq
   2077      * number would mean that the existng information is stale and should
   2078      * be all overwritten by the information in the incoming route entry.
   2079      */
   2080     if (!(entry->flags & CPUDB_F_BASE_INIT_DONE) ||
   2081         (dseq_num > entry->base.dseq_num)) {
   2082         if (!(entry->flags & CPUDB_F_BASE_INIT_DONE)) {
   2083             LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2084                   (BSL_META("disc: Base init key " CPUDB_KEY_FMT_EOLN),
   2085                    CPUDB_KEY_DISP(entry->base.key)));
   2086             flags |= CPUDB_F_BASE_INIT_DONE;
   2087         } else {
   2088             /* If the entry was already base init'd then append
   2089              * existing flags to the flags from incoming packet
   2090              */
   2091             flags |= entry->flags;
   2092         }
   2093 
   2094         /* Init base info */
   2095         if (dseq_num > entry->base.dseq_num) {
   2096             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2097                         (BSL_META("disc: new info for " CPUDB_KEY_FMT " seq %d->%d\n"),
   2098                          CPUDB_KEY_DISP(entry->base.key),
   2099                          entry->base.dseq_num, dseq_num));
   2100         }
   2101 
   2102         /* Extract the unretrieved data from the packet entry */
   2103         sal_memcpy(mac, &buf[ROUTE_MAC_OFS], sizeof(bcm_mac_t));
   2104         buf += ROUTE_SLOT_ID_OFS;
   2105         UNPACK_U32_INCR(buf, slot_id);
   2106         if (entry->db_ref->local_entry->base.flags & CPUDB_BASE_F_CHASSIS) {
   2107             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2108                         (BSL_META("Found slot %d\n"),
   2109                          slot_id));
   2110         }
   2111         UNPACK_U32_INCR(buf, master_pri);
   2112 
   2113         sal_memcpy(app_data, buf, CPUDB_APP_DATA_BYTES);
   2114         buf += CPUDB_APP_DATA_BYTES;
   2115         board_id = cpudb_board_id_unknown;
   2116         base_flags = 0;
   2117         /* Get application data: Board ID and Flag */
   2118         if (version >= DISCOVERY_VERSION_2) {
   2119 #if defined(DISCOVER_APP_DATA_BOARDID)
   2120             uint8 *app_buf = app_data;
   2121             UNPACK_U32_INCR(app_buf, board_id);
   2122             UNPACK_U32_INCR(app_buf, base_flags);
   2123 #endif /* DISCOVER_APP_DATA_BOARDID */
   2124         }
   2125 
   2126         /* Get unit count and check database limit */
   2127         UNPACK_U32_INCR(buf, num_units);
   2128         if (num_units > CPUDB_UNITS_MAX) {
   2129             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2130                       (BSL_META("disc ERR: Rte unit count %d exceeds DB limit %d\n"),
   2131                        num_units, CPUDB_UNITS_MAX));
   2132             return BCM_E_FAIL;
   2133         }
   2134         UNPACK_U32_INCR(buf, dest_unit);
   2135         UNPACK_U32_INCR(buf, dest_port);
   2136 
   2137         /* Get stack port count and check database limit */
   2138         UNPACK_U32_INCR(buf, num_stk_ports);
   2139         if (num_stk_ports > CPUDB_STK_PORTS_MAX) {
   2140             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2141                       (BSL_META("disc ERR: Rte stk port count %d exceeds DB limit %d\n"),
   2142                        num_stk_ports, CPUDB_STK_PORTS_MAX));
   2143             return BCM_E_FAIL;
   2144         }
   2145 
   2146         sal_memcpy(entry->base.mac, mac, sizeof(bcm_mac_t));
   2147         entry->base.dseq_num = dseq_num;
   2148         entry->flags = flags;
   2149         entry->base.slot_id = slot_id;
   2150         entry->base.master_pri = master_pri;
   2151         sal_memcpy(entry->base.app_data, app_data, CPUDB_APP_DATA_BYTES);
   2152         entry->base.board_id = board_id;
   2153         entry->base.flags = base_flags;
   2154         entry->base.num_units = num_units;
   2155         entry->base.dest_unit = dest_unit;
   2156         entry->base.dest_port = dest_port;
   2157         entry->base.num_stk_ports = num_stk_ports;
   2158 
   2159         /* Unpack per unit info */
   2160         for (i = 0; i < entry->base.num_units; i++) {
   2161             UNPACK_U32_INCR(buf, entry->base.mod_ids_req[i]);
   2162             UNPACK_U32_INCR(buf, entry->base.pref_mod_id[i]);
   2163             LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2164                       (BSL_META("disc:  Rte unit %d. mr %d. pref %d\n"),
   2165                        i, entry->base.mod_ids_req[i],
   2166                        entry->base.pref_mod_id[i]));
   2167         }
   2168 
   2169 
   2170         DISC_LOCK;
   2171         disc_flags |= DF_DB_UPDATED;
   2172         DISC_UNLOCK;
   2173         new_entry = TRUE;
   2174     } else {
   2175         /* Check for new flags */
   2176         combined_flags = (flags | entry->flags);
   2177         if (combined_flags != entry->flags) {
   2178             LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2179                       (BSL_META("disc:  Rte flag update %x->%x\n"),
   2180                        entry->flags, combined_flags));
   2181             entry->flags = combined_flags;
   2182             DISC_LOCK;
   2183             disc_flags |= DF_DB_UPDATED;
   2184             DISC_UNLOCK;
   2185         }
   2186 #if defined(BROADCOM_DEBUG)
   2187         BCM_IF_ERROR_RETURN(_check_route_pkt(buf, entry));
   2188 #endif  /* BROADCOM_DEBUG */
   2189         buf += ROUTE_BASE_BYTES(entry->base.num_units);
   2190     }
   2191 
   2192     /* buf now points to the beginning of the stack ports.  Analyze them */
   2193     for (sp_idx = 0; sp_idx < entry->base.num_stk_ports; sp_idx++) {
   2194         stk_port_analyze(entry, sp_idx, buf, new_entry, version);
   2195         buf += stk_entry_bytes_get(version);
   2196     }
   2197 
   2198     return route_entry_bytes_get(entry->base.num_stk_ports,
   2199                                  entry->base.num_units, version);
   2200 }
   2201 
   2202 /****************************************************************
   2203  * RX next hop packet handler; At this point, we know it's a
   2204  * discovery packet, but don't know what kind.
   2205  */
   2206 
   2207 /* Assumes lock */
   2208 
   2209 static INLINE int
   2210 enqueue_probe_pkt(cpudb_key_t src_key, int unit, int port,
   2211                   uint8 *pkt_buf, int len)
   2212 {
   2213     disc_pkt_t *pkt_info;
   2214 
   2215     if (probe_pkt_count + 1 >= DISC_PKTS_MAX) {
   2216         ++disc_resource_err;
   2217         return BCM_E_RESOURCE;
   2218     }
   2219 
   2220     pkt_info = &probe_pkt_queue[probe_pkt_count++];
   2221     CPUDB_KEY_COPY(pkt_info->src_key, src_key);
   2222     pkt_info->rx_unit = unit;
   2223     pkt_info->rx_port = port;
   2224     pkt_info->pkt_buf = pkt_buf;
   2225     pkt_info->len = len;
   2226     pkt_info->data = pkt_buf + CPUTRANS_HEADER_BYTES;
   2227 
   2228     return BCM_E_NONE;
   2229 }
   2230 
   2231 /* Assumes lock */
   2232 
   2233 static INLINE int
   2234 enqueue_routing_pkt(cpudb_key_t src_key, int unit, int port,
   2235                     uint8 *pkt_buf, int len)
   2236 {
   2237     disc_pkt_t *pkt_info;
   2238 
   2239     if (routing_pkt_count + 1 >= DISC_PKTS_MAX) {
   2240         ++disc_resource_err;
   2241         return BCM_E_RESOURCE;
   2242     }
   2243 
   2244     pkt_info = &routing_pkt_queue[routing_pkt_count++];
   2245     CPUDB_KEY_COPY(pkt_info->src_key, src_key);
   2246     pkt_info->rx_unit = unit;
   2247     pkt_info->rx_port = port;
   2248     pkt_info->pkt_buf = pkt_buf;
   2249     pkt_info->len = len;
   2250 
   2251     return BCM_E_NONE;
   2252 }
   2253 
   2254 /* Assumes lock held */
   2255 static INLINE int
   2256 stack_port_active_mark(cpudb_ref_t db_ref, int unit, int port)
   2257 {
   2258     int idx;
   2259 
   2260     idx = stk_port_find(db_ref, unit, port);
   2261     if (idx >= 0) {
   2262         if (STK_FLAGS(db_ref, idx) & CPUDB_SPF_INACTIVE) {
   2263             /*
   2264              * An inactive port has seen a discovery packet.  This
   2265              * counts as a stack event.  Request restart
   2266              */
   2267             disc_flags |= DF_DISC_ABORT;
   2268             disc_abort_rv = DISC_RESTART_NEW_SEQ;
   2269             return -1;
   2270         }
   2271         PP_SEEN_SET(idx);  /* Probe packet seen on port */
   2272     }
   2273 
   2274     return 0;
   2275 }
   2276 
   2277 /* Supports versions 0, 1, and 2 packets */
   2278 
   2279 STATIC bcm_rx_t
   2280 disc_rx_pkt_ver(cpudb_key_t src_key, 
   2281                 int unit, int port, uint8 *pkt_buf,
   2282                 int len, cpudb_ref_t db_ref)
   2283 {
   2284     int pkt_type;
   2285 
   2286     /* Called with DISC_LOCK held */
   2287     pkt_type = disc_pkt_type_get(pkt_buf);
   2288 
   2289     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2290               (BSL_META_U(unit,
   2291               "disc: %s (%d) pkt in\n"),
   2292                pkt_type == DISC_PKT_TYPE_PROBE ? "PROBE" : "ROUTING" ,
   2293                pkt_type));
   2294     switch (pkt_type) {
   2295     case DISC_PKT_TYPE_PROBE:          /* Probe packet */
   2296 
   2297         /* Mark the stack port active if found */
   2298         if (stack_port_active_mark(db_ref, unit, port) < 0) {
   2299             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2300                         (BSL_META_U(unit,
   2301                         "disc: Inactive Stack port %d %d now active.\n"),
   2302                          unit, port));
   2303             DISC_UNLOCK;
   2304             DISC_WAKE;
   2305             return BCM_RX_HANDLED;
   2306         }
   2307         if (enqueue_probe_pkt(src_key, unit, port, pkt_buf, len) < 0) {
   2308             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2309                         (BSL_META_U(unit,
   2310                         "disc: Discarding probe pkt\n")));
   2311             DISC_UNLOCK;
   2312             return BCM_RX_HANDLED;
   2313         }
   2314         break;
   2315     case DISC_PKT_TYPE_ROUTING:        /* Routing packet */
   2316         if (enqueue_routing_pkt(src_key, unit, port, pkt_buf, len) < 0) {
   2317             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2318                         (BSL_META_U(unit,
   2319                         "disc: Discarding routing pkt\n")));
   2320             DISC_UNLOCK;
   2321             return BCM_RX_HANDLED;
   2322         }
   2323         break;
   2324     default:
   2325         LOG_WARN(BSL_LS_TKS_DISCOVER,
   2326                  (BSL_META_U(unit,
   2327                  "disc WARN: Unsupported packet type %d"),
   2328                   pkt_type));
   2329         ++disc_tot_err;
   2330         DISC_UNLOCK;
   2331         return BCM_RX_NOT_HANDLED;
   2332 
   2333     }
   2334 
   2335 #if defined(BCM_RXP_DEBUG)
   2336     bcm_rx_pool_own(pkt_buf, "disc_pkt");
   2337 #endif
   2338 
   2339     /* Normal exit */
   2340     DISC_UNLOCK;
   2341     DISC_WAKE;
   2342 
   2343     return BCM_RX_HANDLED_OWNED;
   2344 }
   2345 
   2346 STATIC bcm_rx_t
   2347 disc_rx_pkt(cpudb_key_t src_key, int port_num,
   2348             int unit, int port, uint8 *pkt_buf,
   2349             int len, void *cookie)
   2350 {
   2351     int d_ver;
   2352     cpudb_ref_t db_ref;
   2353     static int version_warn = TRUE;
   2354 
   2355     COMPILER_REFERENCE(port_num);
   2356 
   2357     if (!INIT_DONE) {
   2358         return BCM_RX_NOT_HANDLED;
   2359     }
   2360 
   2361     DISC_LOCK;
   2362 
   2363     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2364               (BSL_META_U(unit,
   2365               "disc: disc_rx_pkt\n")));
   2366     if (!(disc_flags & DF_DISC_RUNNING)) {
   2367         DISC_UNLOCK;
   2368         /* If no task_db or discovery not running, then sink probe */
   2369         if (disc_pkt_type_get(pkt_buf) == DISC_PKT_TYPE_PROBE &&
   2370             disc_task_db) {
   2371             LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2372                       (BSL_META_U(unit,
   2373                       "disc: rx while idle.\n")));
   2374             (void)disc_callout(disc_task_db, DISC_PROBE_RECEIVED);
   2375         }
   2376         return BCM_RX_NOT_HANDLED;
   2377     }
   2378 
   2379     if (disc_flags & DF_IGNORE_PACKETS) {
   2380         DISC_UNLOCK;
   2381         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2382                   (BSL_META_U(unit,
   2383                   "disc: rx ignored.\n")));
   2384         return BCM_RX_NOT_HANDLED;
   2385     }
   2386 
   2387     db_ref = *(cpudb_ref_t *)cookie;
   2388     if (!cpudb_valid(db_ref)) {
   2389         DISC_UNLOCK;
   2390         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2391                     (BSL_META_U(unit,
   2392                     "disc: Config pkt in when db_ref is invalid.\n")));
   2393         return BCM_RX_NOT_HANDLED;
   2394     }
   2395 
   2396     d_ver = pkt_buf[CPUTRANS_HEADER_BYTES + DISC_VER_OFS];
   2397 
   2398     /* Fallback only if received discovery packet version is lower */
   2399     if (disc_fallback && (disc_version > d_ver)) {
   2400         LOG_WARN(BSL_LS_TKS_DISCOVER,
   2401                  (BSL_META_U(unit,
   2402                  "DISC WARN: Protocol version changed from %d to %d\n"),
   2403                   disc_version, d_ver));
   2404         disc_version = d_ver;
   2405         DISC_UNLOCK;
   2406         return BCM_RX_HANDLED;
   2407     } else if (d_ver != disc_version && version_warn) {
   2408         LOG_WARN(BSL_LS_TKS_DISCOVER,
   2409                  (BSL_META_U(unit,
   2410                  "DISC WARN: Received discovery version %d, sending %d\n"),
   2411                   d_ver, disc_version));
   2412         version_warn = FALSE;
   2413     }
   2414 
   2415     switch (d_ver) {
   2416     case DISCOVERY_VERSION_0:
   2417     case DISCOVERY_VERSION_1:
   2418     case DISCOVERY_VERSION_2:
   2419         return disc_rx_pkt_ver(src_key, unit, port, pkt_buf, len, db_ref);
   2420     }
   2421 
   2422     /* Unknown version - throw packet away */
   2423     DISC_UNLOCK;
   2424     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2425                 (BSL_META_U(unit,
   2426                 "disc: Unknown discovery version %d in probe/route\n"),
   2427                  d_ver));
   2428     return BCM_RX_NOT_HANDLED;
   2429 }
   2430 
   2431 
   2432 /*
   2433  * Set up the header of a discovery pkt.
   2434  *
   2435  * Note that "ttl" is put in the reserved field.  If DISC TTL is not
   2436  * enabled, then this value should be 0.
   2437  */
   2438 
   2439 STATIC INLINE void
   2440 disc_pkt_setup(uint8 *pkt_buf, uint8 pkt_type, uint8 entry_count,
   2441                uint8 ttl_rsvd, int dseq_num, int version)
   2442 {
   2443     pkt_buf[DISC_VER_OFS] = (uint8)version;
   2444     pkt_buf[DISC_RSVD_OFS] = (uint8)ttl_rsvd;
   2445     pkt_buf[LOCAL_PKT_TYPE_OFS] = pkt_type;
   2446     pkt_buf[ENTRY_COUNT_OFS] = entry_count;
   2447     PACK_LONG(&pkt_buf[SRC_DSEQ_NUM_OFS], dseq_num);
   2448 }
   2449 
   2450 
   2451 /*
   2452  * Function:
   2453  *      disc_header_setup
   2454  * Purpose:
   2455  *      Set up the header for a discovery packet
   2456  * Parameters:
   2457  *      pkt_buf        - Buffer to setup
   2458  *      pkt_type       - Type for discovery header
   2459  *      entry_count    - How many entries (if applicable)
   2460  * Returns:
   2461  *      BCM_E_XXX
   2462  */
   2463 
   2464 void
   2465 disc_header_setup(uint8 *pkt_buf, uint8 pkt_type, uint8 entry_count,
   2466                   uint8 ttl_rsvd, int dseq_num)
   2467 {
   2468     disc_pkt_setup(pkt_buf, pkt_type, entry_count, ttl_rsvd, dseq_num,
   2469                    disc_version);
   2470 }
   2471 
   2472 #if DISC_PPS > 0
   2473 
   2474 #define DISC_TX_DELAY (1000000/DISC_PPS) /* usec between transmissions */
   2475 
   2476 STATIC void
   2477 disc_rate_limit(void)
   2478 {
   2479     sal_usecs_t now = sal_time_usecs();
   2480     int delta = 0;
   2481 
   2482     if (disc_tx_init) {
   2483         delta = SAL_USECS_SUB(now, disc_tx_prev);
   2484         if (delta > 0 && delta < DISC_TX_DELAY) {
   2485             sal_usleep(DISC_TX_DELAY - delta);
   2486         }
   2487     } else {
   2488         disc_tx_init = !disc_tx_init;
   2489     }
   2490 
   2491     disc_tx_prev = now;
   2492 }
   2493 #else
   2494 /* rate limiting disabled */
   2495 #define disc_rate_limit()
   2496 #endif /*  DISC_PPS */
   2497 
   2498 /****************************************************************
   2499  *
   2500  * Probe packet handling
   2501  */
   2502 
   2503 /* Explicitly send probe pkt to each stk port so that idx can be updated */
   2504 
   2505 STATIC void
   2506 probe_pkt_tx(cpudb_ref_t db_ref, uint8 *buf, int len, uint8 *cur_entry,
   2507              disc_pkt_t *probe_pkt)
   2508 {
   2509     int i;
   2510     int rv = BCM_E_NONE;
   2511     int unit, port;
   2512 
   2513     /* if probe_pkt is not NULL, detect full duplex optimization */
   2514 
   2515     for (i = 0; i < db_ref->local_entry->base.num_stk_ports; i++) {
   2516         if (STK_FLAGS(db_ref, i) & CPUDB_SPF_NO_LINK ||
   2517             STK_FLAGS(db_ref, i) & CPUDB_SPF_ETHERNET) {
   2518             continue;
   2519         }
   2520         unit = STKP_UNIT(db_ref, i);
   2521         port = STKP_PORT(db_ref, i);
   2522 
   2523         if (probe_pkt &&
   2524             (unit != probe_pkt->rx_unit || port != probe_pkt->rx_port)) {
   2525             /* full duplex optimization: only forward packets to original
   2526                sender. */
   2527             continue;
   2528         }
   2529         if (STK_FLAGS(db_ref, i) & CPUDB_SPF_TX_RESOLVED &&
   2530             STK_FLAGS(db_ref, i) & CPUDB_SPF_RX_RESOLVED &&
   2531             STK_FLAGS(db_ref, i) & CPUDB_SPF_TX_FORWARDED) {
   2532             /* this port already resolved locally, and a probe
   2533                has been forwarded via this port. */
   2534            continue;
   2535         }
   2536         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2537                   (BSL_META_U(unit,
   2538                   "DISC:  Sending probe pkt cos %d to port %d (%d, %d)\n"),
   2539                    disc_cos, i, unit, port));
   2540 
   2541         next_hop_buffer_init(buf, DISC_NH_PKT_TYPE, cpudb_neighbor_key);
   2542         cur_entry[PROBE_TX_IDX_OFS] = (uint8)i;
   2543 
   2544         disc_rate_limit();
   2545 
   2546         rv = nh_tx(unit,
   2547                    port,
   2548                    buf,
   2549                    len,
   2550                    disc_cos,
   2551                    disc_vlan,
   2552                    NEXT_HOP_PKT_TYPE,
   2553                    CPUTRANS_NO_HEADER_ALLOC | CPUTRANS_CRC_REGEN,
   2554                    NULL,
   2555                    NULL);
   2556         if (BCM_FAILURE(rv)) {
   2557             ++disc_tx_pkt_err;
   2558             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2559                       (BSL_META_U(unit,
   2560                       "disc ERR %d: probe pkt tx to "
   2561                        "stkport %d: %s\n"), rv, i, bcm_errmsg(rv)));
   2562             break;
   2563         } else {
   2564             if (probe_pkt) {
   2565                 STK_FLAGS(db_ref, i) |= CPUDB_SPF_TX_FORWARDED; 
   2566             }
   2567         }
   2568     }
   2569 }
   2570 
   2571 
   2572 /* Send out initial probe packets */
   2573 STATIC void
   2574 probe_pkts_generate(cpudb_ref_t db_ref)
   2575 {
   2576     uint8 *buf, *start_buf;
   2577     int len;
   2578 
   2579     /* Just one entry in initial packets sent out */
   2580     len = CPUTRANS_HEADER_BYTES + PROBE_ENTRY_OFS(1) + sizeof(uint32);
   2581     buf = start_buf = DATA_SALLOC(len);
   2582     if (buf == NULL) {
   2583         ++disc_alloc_fail;
   2584         ++disc_tot_err;
   2585         return;
   2586     }
   2587 
   2588     buf += CPUTRANS_HEADER_BYTES;
   2589     disc_pkt_setup(buf, DISC_PKT_TYPE_PROBE, 1, disc_ttl,
   2590                    db_ref->local_entry->base.dseq_num, disc_version);
   2591     buf += ENTRY_START_OFS;
   2592     CPUDB_KEY_PACK(buf, LOCAL_KEY(db_ref));
   2593     sal_memcpy(&buf[PROBE_MAC_OFS], LOCAL_MAC(db_ref), sizeof(bcm_mac_t));
   2594     /* Set sequence number */
   2595     PACK_LONG(&buf[PROBE_DSEQ_NUM_OFS], CUR_DSEQ_NUM(db_ref));
   2596     probe_pkt_tx(db_ref, start_buf, len, buf, NULL);
   2597 
   2598     /* Update TTL if enabled (disc_ttl > 0) */
   2599     if (disc_ttl > 0 && ++disc_ttl > disc_ttl_max) {
   2600         disc_ttl = disc_ttl_max;
   2601     } else {
   2602         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2603                   (BSL_META("DISC: TTL is %d\n"),
   2604                    disc_ttl));
   2605     }
   2606 
   2607     DATA_SFREE(start_buf);
   2608 }
   2609 
   2610 
   2611 /*
   2612  * Clear all mod ids
   2613  */
   2614 STATIC INLINE void
   2615 db_entry_clear_mod_ids(cpudb_entry_t *entry)
   2616 {
   2617     int  unit;
   2618     int  max_units;
   2619 
   2620     max_units = COUNTOF(entry->base.pref_mod_id);
   2621     for (unit = 0; unit < max_units; unit++) {
   2622         entry->base.pref_mod_id[unit] = -1;
   2623     }
   2624 
   2625     max_units = COUNTOF(entry->mod_ids);
   2626     for (unit = 0; unit < max_units; unit++) {
   2627         entry->mod_ids[unit] = -1;
   2628     }
   2629 
   2630     return;
   2631 }
   2632 
   2633 /*
   2634  * Run through the entries in a probe packet.  Add new entries to
   2635  * the DB.  For existing entries, check for changes in sequence number.
   2636  *
   2637  * In order of precedence, returns
   2638  *    PROBE_PKT_DROP if packet should be dropped due to old seq num.
   2639  *    PROBE_PKT_DB_CLEAR if newer sequence number found;
   2640  *    PROBE_PKT_MEMORY if unable to add an entry
   2641  *    PROBE_PKT_OK if all okay;
   2642  *
   2643  * pkt_buf points to the beginning of the first entry in the packet
   2644  */
   2645 
   2646 STATIC int
   2647 probe_pkt_dseq_num_check(cpudb_ref_t db_ref, uint8 *pkt_start,
   2648                         int entry_count)
   2649 {
   2650     cpudb_key_t key;
   2651     cpudb_entry_t *entry;
   2652     int i;
   2653     int dseq_num;
   2654     int new_entries = 0;
   2655     int diff;
   2656     uint8 *pkt_buf, *first_new;
   2657 
   2658     first_new = NULL;
   2659     pkt_buf = pkt_start;
   2660     /* First, run thru entries looking for different seq nums (drop pkt) */
   2661     for (i = 0; i < entry_count; i++) {
   2662         CPUDB_KEY_UNPACK(pkt_buf, key);
   2663         CPUDB_KEY_SEARCH(db_ref, key, entry);
   2664         UNPACK_LONG(&pkt_buf[PROBE_DSEQ_NUM_OFS], dseq_num);
   2665         if (entry == NULL) {
   2666             new_entries++;
   2667             if (first_new == NULL) {
   2668                 first_new = pkt_buf;
   2669             }
   2670         } else {
   2671             diff = dseq_num - entry->base.dseq_num;
   2672             if (diff > 0) {  /* pkt SN is newer than local DB */
   2673                 LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2674                             (BSL_META("disc: New seq num %d for key "
   2675                                       CPUDB_KEY_FMT_EOLN),
   2676                              dseq_num,
   2677                              CPUDB_KEY_DISP(entry->base.key)));
   2678                 return PROBE_PKT_DB_CLEAR;
   2679             } else if (diff < 0) { /* pkt SN is older than DB */
   2680                 LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   2681                             (BSL_META("disc: Old seq num. DB SN %d. Pkt SN %d. key "
   2682                                       CPUDB_KEY_FMT_EOLN),
   2683                              entry->base.dseq_num,
   2684                              dseq_num, CPUDB_KEY_DISP(entry->base.key)));
   2685                 return PROBE_PKT_DROP;
   2686             }
   2687         }
   2688         pkt_buf += PROBE_ENTRY_BYTES;
   2689     }
   2690 
   2691     /* Now update any missing entries */
   2692     pkt_buf = first_new;
   2693     while (new_entries > 0) {
   2694         CPUDB_KEY_UNPACK(pkt_buf, key);
   2695         CPUDB_KEY_SEARCH(db_ref, key, entry);
   2696         UNPACK_LONG(&pkt_buf[PROBE_DSEQ_NUM_OFS], dseq_num);
   2697         if (entry == NULL) {
   2698             new_entries--;
   2699             DISC_LOCK;
   2700             disc_flags |= DF_DB_UPDATED;
   2701             DISC_UNLOCK;
   2702             entry = cpudb_entry_create(db_ref, key, FALSE);
   2703             if (entry == NULL) {
   2704                 return PROBE_PKT_MEMORY;
   2705             }
   2706             db_entry_clear_mod_ids(entry);
   2707             entry->base.dseq_num = dseq_num;
   2708         }
   2709 
   2710         pkt_buf += PROBE_ENTRY_BYTES;
   2711     }
   2712     return PROBE_PKT_OK;
   2713 }
   2714 
   2715 /*
   2716  * Local CPU key is not in this probe pkt;
   2717  * Add local info to pkt and send off to all stack ports.
   2718  *
   2719  * ASSUMPTION:  The RX packet we're manipulating is long enough
   2720  * to hold a max length probe packet.
   2721  *
   2722  * Disable full duplex optimization if enable_fdo is false.
   2723  */
   2724 
   2725 STATIC void
   2726 probe_pkt_forward(cpudb_ref_t db_ref, disc_pkt_t *probe_pkt,
   2727                   int entry_count, int rx_sp_idx, int enable_fdo)
   2728 {
   2729     uint8 *local_entry;
   2730     int tx_len;
   2731     uint8 ttl_rsvd;
   2732     disc_pkt_t *fd_opt = NULL;
   2733 
   2734     ttl_rsvd = probe_pkt->data[DISC_PROBE_TTL_OFS];
   2735     switch (ttl_rsvd) {
   2736     case 0:  /* Ignore TTL */
   2737         break;
   2738     case 1:  /* TTL expired, do not forward */
   2739         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2740                   (BSL_META("DISC: Not forwarding probe pkt due to TTL == 1\n")));
   2741         return;
   2742     default: /* Decrement TTL and forward */
   2743         probe_pkt->data[DISC_PROBE_TTL_OFS] = ttl_rsvd - 1;
   2744 
   2745         if (disc_ttl_min == DISC_TTL_FULL_DUPLEX &&
   2746             disc_ttl_max == DISC_TTL_FULL_DUPLEX &&
   2747             enable_fdo) {
   2748             fd_opt = probe_pkt;
   2749         }
   2750 
   2751         break;
   2752     }
   2753 
   2754     local_entry = &probe_pkt->data[PROBE_ENTRY_OFS(entry_count++)];
   2755     CPUDB_KEY_PACK(local_entry, LOCAL_KEY(db_ref));
   2756     sal_memcpy(&local_entry[PROBE_MAC_OFS], LOCAL_MAC(db_ref),
   2757                sizeof(bcm_mac_t));
   2758     local_entry[PROBE_RX_IDX_OFS] = (uint8)rx_sp_idx;
   2759     PACK_LONG(&local_entry[PROBE_DSEQ_NUM_OFS], CUR_DSEQ_NUM(db_ref));
   2760 
   2761     /* Update the entry count in the packet */
   2762     probe_pkt->data[ENTRY_COUNT_OFS] = (uint8)entry_count;
   2763 
   2764     /* TX UNIT and TX PORT are filled in by receiving CPU */
   2765     tx_len = CPUTRANS_HEADER_BYTES + DISC_HEADER_BYTES +
   2766                entry_count * PROBE_ENTRY_BYTES + sizeof(uint32);
   2767     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2768               (BSL_META("disc: Prb pkt out, ent %d len %d\n"),
   2769                entry_count, tx_len));
   2770     probe_pkt_tx(db_ref, probe_pkt->pkt_buf, tx_len, local_entry, fd_opt);
   2771 }
   2772 
   2773 
   2774 /*
   2775  * Handle a probe packet with the local CPU key which has traveled
   2776  * off the board (it has some other CPU info as well.)
   2777  *
   2778  * The transmit portion of the original stack port is now resolved.
   2779  *
   2780  * Otherwise, we scan through the entries and add the CPU keys.
   2781  * We could discern a lot of stack ports, but that will come to us
   2782  * from routing packets.
   2783  */
   2784 
   2785 static INLINE void
   2786 off_board_pkt_handle(cpudb_ref_t db_ref,
   2787                      uint8 *buf,       /* Points to first new entry */
   2788                      int tx_sp_idx,    /* index of orig TX stk port */
   2789                      int new_entries)  /* How many new entries in pkt --
   2790                                           that is, >= local entry */
   2791 {
   2792     int i;
   2793     cpudb_entry_t *entry;
   2794     cpudb_key_t key;
   2795     int remote_rx_idx;
   2796     int dseq_num;
   2797 
   2798     /* From the first entry after the local key, we can determine the
   2799      * TX connection of the stack port.  Note:  new_entries >= 1.  */
   2800 
   2801     remote_rx_idx = (int)buf[PROBE_RX_IDX_OFS];
   2802     CPUDB_KEY_UNPACK(buf, key);
   2803     UNPACK_LONG(&buf[PROBE_DSEQ_NUM_OFS], dseq_num);
   2804     entry = _disc_key_resolve(db_ref, key, &buf[PROBE_MAC_OFS], dseq_num);
   2805     if (entry == NULL) {
   2806         ++disc_rx_pkt_err;
   2807         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2808                   (BSL_META("disc ERR: bad key, off board 0\n")));
   2809         return;
   2810     }
   2811 
   2812     if (stk_port_tx_set(db_ref, tx_sp_idx, key, remote_rx_idx, 0) < 0) {
   2813         return;
   2814     }
   2815 
   2816     /* Have seen an offboard packet now */
   2817     DISC_LOCK;
   2818     disc_flags |= DF_INFO_IN;
   2819     DISC_UNLOCK;
   2820 
   2821     /* Put other MACs in the local DB */
   2822     buf += PROBE_ENTRY_BYTES;  /* Next entry */
   2823     for (i = 1; i < new_entries; i++) {  /* One done above */
   2824         CPUDB_KEY_UNPACK(buf, key);
   2825         UNPACK_LONG(&buf[PROBE_DSEQ_NUM_OFS], dseq_num);
   2826         entry = _disc_key_resolve(db_ref, key, &buf[PROBE_MAC_OFS], dseq_num);
   2827         if (entry == NULL) {
   2828             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2829                       (BSL_META("disc ERR: bad key, off board %d\n"),
   2830                        i));
   2831             ++disc_rx_pkt_err;
   2832             return;
   2833         }
   2834         buf += PROBE_ENTRY_BYTES;  /* Next entry */
   2835     }
   2836 }
   2837 
   2838 
   2839 /* Handle a probe packet that has the local MAC address in an entry
   2840  */
   2841 
   2842 STATIC void
   2843 probe_pkt_with_local_key(cpudb_ref_t db_ref,
   2844                          disc_pkt_t *probe_pkt,  /* The pkt */
   2845                          int local_idx,   /* Where is local MAC */
   2846                          int entry_count) /* How many entries in pkt */
   2847 {
   2848     uint8 *buf;
   2849     int tx_sp_idx;
   2850     int rx_sp_idx;
   2851 
   2852     /* Local MAC found.  Update local DB.  Set buf to start of entries */
   2853     buf = &probe_pkt->data[PROBE_ENTRY_OFS(local_idx)];
   2854 
   2855     /* Grab the originating unit/port info for this pkt */
   2856     tx_sp_idx = (int)buf[PROBE_TX_IDX_OFS];
   2857     rx_sp_idx = stk_port_find(db_ref, probe_pkt->rx_unit, probe_pkt->rx_port);
   2858 
   2859     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2860               (BSL_META("disc: Prb pkt local. RX %d. TX %d, ent cnt %d\n"),
   2861                rx_sp_idx, tx_sp_idx, entry_count));
   2862 
   2863 #if defined(BROADCOM_DEBUG)
   2864     if (tx_sp_idx < 0 || tx_sp_idx > db_ref->local_entry->base.num_stk_ports) {
   2865         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2866                   (BSL_META("disc ERR: Did not find stk port in probe pkt")));
   2867         return;
   2868     }
   2869     if (rx_sp_idx < 0) {
   2870         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2871                   (BSL_META("disc ERR: Did not find rx stk port of probe pkt")));
   2872         return;
   2873     }
   2874 #endif /* BROADCOM_DEBUG */
   2875 
   2876     if (entry_count == 1) { /* It's an on-board connection */
   2877         /*
   2878          * (cpu, tx_sp_idx) --> (cpu, rx_sp_idx);
   2879          * so TX of tx_sp_idx goes to local CPU, rx_sp_idx;
   2880          * and pkts received on rx_sp_idx were sent by local cpu to tx_sp_idx.
   2881          * Got that?  The RX one was already set in probe_pkt_process.
   2882          */
   2883         stk_port_tx_set(db_ref, tx_sp_idx, LOCAL_KEY(db_ref),
   2884                         rx_sp_idx, CPUDB_SPF_ON_BOARD);
   2885     } else {
   2886         off_board_pkt_handle(db_ref, buf + PROBE_ENTRY_BYTES, tx_sp_idx,
   2887                              entry_count - local_idx - 1);
   2888     }
   2889 }
   2890 
   2891 
   2892 /****************************************************************
   2893  *
   2894  * Routing packet handling
   2895  */
   2896 STATIC INLINE int db_entry_pack(uint8 *buf, cpudb_entry_t *entry,
   2897                                 int max_bytes, int version);
   2898 
   2899 /*
   2900  * Send a route packet specifically to each stack port using
   2901  * next hop tx.
   2902  */
   2903 static INLINE void
   2904 route_pkt_tx(cpudb_ref_t db_ref, uint8 *buf, int len)
   2905 {
   2906     int i;
   2907     int rv = BCM_E_NONE;
   2908     cpudb_entry_t *dest_entry;
   2909 
   2910     /* Keep track of TX keys to which we've sent info; up to 4 keys */
   2911     cpudb_key_t sent_keys[CPUDB_STK_PORTS_MAX];
   2912     int sk_count = 0, sk_idx;
   2913     int cpu_done;
   2914 
   2915     for (i = 0; i < db_ref->local_entry->base.num_stk_ports; i++) {
   2916         if (!(STK_FLAGS(db_ref, i) & CPUDB_SPF_TX_RESOLVED) ||
   2917             (STK_FLAGS(db_ref, i) & CPUDB_SPF_NO_LINK) ||
   2918             (STK_FLAGS(db_ref, i) & CPUDB_SPF_ETHERNET) ) {
   2919             continue;
   2920         }
   2921 
   2922         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   2923                   (BSL_META("disc rte pkt out len %d to %d @ " CPUDB_KEY_FMT_EOLN),
   2924                    len, i, CPUDB_KEY_DISP(STK_PORT(db_ref, i).tx_cpu_key)));
   2925         CPUDB_KEY_SEARCH(db_ref, STK_PORT(db_ref, i).tx_cpu_key, dest_entry);
   2926         if (dest_entry != NULL) {
   2927             cpu_done = FALSE;     /* See if info sent to this CPU already */
   2928             for (sk_idx = 0; sk_idx < sk_count; sk_idx++) {
   2929                 if (CPUDB_KEY_EQUAL(sent_keys[sk_idx], dest_entry->base.key)) {
   2930                     cpu_done = TRUE;
   2931                     break;
   2932                 }
   2933             }
   2934             if (cpu_done) {
   2935                 continue;
   2936             }
   2937 
   2938             if (sk_count < CPUDB_STK_PORTS_MAX) {  /* Record destination key */
   2939                 CPUDB_KEY_COPY(sent_keys[sk_count], dest_entry->base.key);
   2940                 sk_count++;
   2941             }
   2942         }
   2943 
   2944         next_hop_buffer_init(buf, DISC_NH_PKT_TYPE, cpudb_neighbor_key);
   2945 
   2946         disc_rate_limit();
   2947 
   2948         rv = nh_tx(STKP_UNIT(db_ref, i),
   2949                    STKP_PORT(db_ref, i),
   2950                    buf,
   2951                    len,
   2952                    disc_cos,
   2953                    disc_vlan,
   2954                    NEXT_HOP_PKT_TYPE,
   2955                    CPUTRANS_NO_HEADER_ALLOC,
   2956                    NULL,
   2957                    NULL);
   2958         if (BCM_FAILURE(rv)) {
   2959             ++disc_tx_pkt_err;
   2960             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   2961                       (BSL_META("disc ERR: sending rte to stkport %d\n"),
   2962                        i));
   2963             break;
   2964         }
   2965         if (DISC_EXIT) {
   2966             break;
   2967         }
   2968     }
   2969 }
   2970 
   2971 
   2972 /* Pack the current database into routing packets */
   2973 static INLINE int
   2974 routing_pkts_form(cpudb_ref_t db_ref)
   2975 {
   2976     uint8 *pkt_buf = NULL;
   2977     cpudb_entry_t *entry;
   2978     int cur_offset = 0; /* Byte offset into current packet */
   2979     int tot_entries;    /* Total number of route entries to pack */
   2980     int tot_pkts;       /* Total number of route packets */
   2981     int entry_bytes;    /* Number of bytes in route entry */
   2982     int num_entries;    /* Number of entries already packed in the packet */
   2983     int pkt_bytes_left; /* Remaining bytes in current route packet */
   2984     int cur_pkt_idx;    /* Current route packet being packed */
   2985     int version = disc_version;
   2986 
   2987 
   2988     /*
   2989      * A routing packet has the following format
   2990      * 
   2991      *   +----------------+----------------+-------------------+
   2992      *   | <cputrans-hdr> | <disc-pkt-hdr> | <route-entry> * n |
   2993      *   +----------------+----------------+-------------------+
   2994      *
   2995      * Where, n is the number of route entries in the packet
   2996      */
   2997 
   2998     /* Remaining bytes available in packet buffer */
   2999     pkt_bytes_left = ROUTE_PKT_BYTES_MAX -
   3000                      (CPUTRANS_HEADER_BYTES + ENTRY_START_OFS);
   3001 
   3002     tot_pkts    = 0;
   3003     tot_entries = 0;
   3004     num_entries = 0;
   3005     cur_pkt_idx = -1;
   3006     
   3007     CPUDB_FOREACH_ENTRY(db_ref, entry) {
   3008         if (!(entry->flags & CPUDB_F_BASE_INIT_DONE)) {
   3009             continue;
   3010         }
   3011         
   3012         /* Get size required by route entry */
   3013         entry_bytes = route_entry_bytes_get(entry->base.num_stk_ports,
   3014                                             entry->base.num_units,
   3015                                             version);
   3016         /*
   3017          * Route packet should fit at least one entry
   3018          */
   3019         if ((entry_bytes > pkt_bytes_left) && (num_entries == 0)) {
   3020             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3021                       (BSL_META("disc ERR: route pkt entry too long, need %d, left %d\n"),
   3022                        entry_bytes, pkt_bytes_left));
   3023             ++disc_internal_err;
   3024             ++disc_tot_err;
   3025             return -1;
   3026         }
   3027 
   3028         /*
   3029          * Start a new packet if:
   3030          *   - Current number of entries is 0, or
   3031          *   - There is no enough space left in packet, or
   3032          *   - Number of entries per packet has reached the limit
   3033          */
   3034         if ((num_entries == 0) || (entry_bytes > pkt_bytes_left) ||
   3035             (num_entries >= ROUTE_ENTRIES_PER_PKT_MAX)) {
   3036 
   3037             /* Complete setup for current packet, if any */
   3038             if (cur_pkt_idx >= 0) {
   3039                 /* Set header and record length */
   3040                 disc_pkt_setup(pkt_buf + CPUTRANS_HEADER_BYTES,
   3041                                DISC_PKT_TYPE_ROUTING, num_entries, 0,
   3042                                db_ref->local_entry->base.dseq_num, version);
   3043                 route_pkt_len[cur_pkt_idx] = ROUTE_PKT_BYTES_MAX -
   3044                                              pkt_bytes_left;
   3045             }
   3046 
   3047             /* Check route packets limit */
   3048             if ((++cur_pkt_idx) >= ROUTE_PKTS_MAX) {
   3049                 LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3050                           (BSL_META("disc ERR: number of route pkts exceeds limit %d\n"),
   3051                            ROUTE_PKTS_MAX));
   3052                 ++disc_internal_err;
   3053                 ++disc_tot_err;
   3054                 return -1;
   3055             }
   3056 
   3057             num_entries    = 0;
   3058             pkt_buf        = route_pkt_buf[cur_pkt_idx];
   3059             pkt_bytes_left = ROUTE_PKT_BYTES_MAX -
   3060                              (CPUTRANS_HEADER_BYTES + ENTRY_START_OFS);
   3061         }
   3062 
   3063         /* Pack the entry */
   3064         cur_offset = ROUTE_PKT_BYTES_MAX - pkt_bytes_left;
   3065         entry_bytes = db_entry_pack(&pkt_buf[cur_offset], entry,
   3066                                     pkt_bytes_left, version);
   3067         if (entry_bytes < 0) {
   3068             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3069                       (BSL_META("disc ERR: cannot pack route entry %d in packet %d\n"),
   3070                        num_entries + 1, cur_pkt_idx));
   3071             ++disc_internal_err;
   3072             ++disc_tot_err;
   3073             return -1;
   3074         }
   3075 
   3076         pkt_bytes_left -= entry_bytes;
   3077         num_entries++;
   3078         tot_entries++;
   3079         
   3080         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   3081                   (BSL_META("disc: Packing route entry %d in packet %d, "
   3082                    "buffer left %d\n"),
   3083                    num_entries, cur_pkt_idx, pkt_bytes_left));
   3084     }
   3085 
   3086     /* Complete setup for last packet, if needed */
   3087     if (num_entries != 0) {
   3088         disc_pkt_setup(pkt_buf + CPUTRANS_HEADER_BYTES,
   3089                        DISC_PKT_TYPE_ROUTING, num_entries, 0,
   3090                        db_ref->local_entry->base.dseq_num, version);
   3091         route_pkt_len[cur_pkt_idx] = ROUTE_PKT_BYTES_MAX - pkt_bytes_left;
   3092     }
   3093 
   3094     tot_pkts = cur_pkt_idx + 1;
   3095     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   3096               (BSL_META("disc: Packed %d entries in %d pkts\n"),
   3097                tot_entries, tot_pkts));
   3098 
   3099     return tot_pkts;
   3100 }
   3101 
   3102 
   3103 /*
   3104  * Send out routing packets to each off board port; may need to send
   3105  * more than one packet to cover all entries in the DB.
   3106  */
   3107 STATIC void
   3108 routing_pkts_send(cpudb_ref_t db_ref)
   3109 {
   3110     int num_pkts;
   3111     int i;
   3112 
   3113     if (!(disc_flags & DF_TX_KNOWN)) {
   3114         LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   3115                   (BSL_META("disc: No route out; no TX known\n")));
   3116         return;
   3117     }
   3118 
   3119     num_pkts = routing_pkts_form(db_ref);
   3120     if (num_pkts <= 0) {
   3121         return;
   3122     }
   3123     
   3124     DISC_LOCK;
   3125     disc_flags &= ~DF_DB_UPDATED;  /* Clear updated flag */
   3126     DISC_UNLOCK;
   3127     
   3128     for (i = 0; i < num_pkts; i++) {
   3129         route_pkt_tx(db_ref, route_pkt_buf[i], route_pkt_len[i]);
   3130         if (DISC_EXIT) {
   3131             break;
   3132         }
   3133     }
   3134 }
   3135 
   3136 
   3137 STATIC INLINE void
   3138 db_stk_entry_pack(uint8 *buf, cpudb_entry_t *entry, int sp, int version)
   3139 {
   3140     cpudb_stk_port_t *sp_p;
   3141 
   3142     sp_p = &entry->sp_info[sp];
   3143     PACK_U32_INCR(buf, sp_p->flags);
   3144     CPUDB_KEY_PACK(buf, sp_p->tx_cpu_key);
   3145     buf += CPUDB_KEY_BYTES;
   3146     PACK_U32_INCR(buf, sp_p->tx_stk_idx);
   3147     CPUDB_KEY_PACK(buf, sp_p->rx_cpu_key);
   3148     buf += CPUDB_KEY_BYTES;
   3149     PACK_U32_INCR(buf, sp_p->rx_stk_idx);
   3150     if (version >= DISCOVERY_VERSION_1) {
   3151         PACK_U32_INCR(buf, entry->base.stk_ports[sp].weight);
   3152         PACK_U32_INCR(buf, entry->base.stk_ports[sp].bflags);
   3153     }
   3154     if (version >= DISCOVERY_VERSION_2) {
   3155         PACK_U32_INCR(buf, entry->base.stk_ports[sp].unit);
   3156         PACK_U32_INCR(buf, entry->base.stk_ports[sp].port);
   3157     }
   3158 }
   3159 
   3160 
   3161 STATIC INLINE int
   3162 db_entry_pack(uint8 *buf, cpudb_entry_t *entry, int max_bytes, int version)
   3163 {
   3164     int bytes = 0;
   3165     int sp;
   3166     int i;
   3167 
   3168     bytes = route_entry_bytes_get(entry->base.num_stk_ports,
   3169                                   entry->base.num_units, version);
   3170     if (bytes > max_bytes) {
   3171         return -1;
   3172     }
   3173 
   3174     CPUDB_KEY_PACK(buf, entry->base.key);
   3175     buf += CPUDB_KEY_BYTES;
   3176     sal_memcpy(buf, entry->base.mac, sizeof(bcm_mac_t));
   3177     buf += sizeof(bcm_mac_t);
   3178     PACK_U32_INCR(buf, entry->base.dseq_num);
   3179     /* coverity[result_independent_of_operands] */
   3180     PACK_U32_INCR(buf, entry->flags & CPUDB_F_FORWARD_MASK);
   3181     PACK_U32_INCR(buf, entry->base.slot_id);
   3182     PACK_U32_INCR(buf, entry->base.master_pri);
   3183 
   3184     /* Set application data with Board ID and Flag information */
   3185     if (version >= DISCOVERY_VERSION_2) {
   3186 #if defined(DISCOVER_APP_DATA_BOARDID)
   3187         uint8 *app_buf = entry->base.app_data;
   3188         PACK_U32_INCR(app_buf, entry->base.board_id);
   3189         PACK_U32_INCR(app_buf, entry->base.flags);
   3190 #endif /* DISCOVER_APP_DATA_BOARDID */
   3191     }
   3192     sal_memcpy(buf, entry->base.app_data, CPUDB_APP_DATA_BYTES);
   3193     buf += CPUDB_APP_DATA_BYTES;
   3194 
   3195     PACK_U32_INCR(buf, entry->base.num_units);
   3196     PACK_U32_INCR(buf, entry->base.dest_unit);
   3197     PACK_U32_INCR(buf, entry->base.dest_port);
   3198     PACK_U32_INCR(buf, entry->base.num_stk_ports);
   3199 
   3200     for (i = 0; i < entry->base.num_units; i++) {
   3201         PACK_U32_INCR(buf, entry->base.mod_ids_req[i]);
   3202         PACK_U32_INCR(buf, entry->base.pref_mod_id[i]);
   3203     }
   3204 
   3205     for (sp = 0; sp < entry->base.num_stk_ports; sp++) {
   3206         db_stk_entry_pack(buf, entry, sp, version);
   3207         buf += stk_entry_bytes_get(version);
   3208     }
   3209 
   3210     return bytes;
   3211 }
   3212 
   3213 
   3214 /****************************************************************
   3215  *
   3216  * Local DB access routines
   3217  */
   3218 
   3219 /*
   3220  * If key exists in CPUDB, return pointer to entry; otherwise add it
   3221  * and return the new entry.  If entry is added, update discovery
   3222  * updated timer.
   3223  */
   3224 STATIC cpudb_entry_t *
   3225 _disc_key_resolve(cpudb_ref_t db_ref, cpudb_key_t key, bcm_mac_t mac,
   3226                   int dseq_num)
   3227 {
   3228     cpudb_entry_t *entry;
   3229 
   3230 
   3231     CPUDB_KEY_SEARCH(db_ref, key, entry);
   3232 
   3233     if (entry != NULL) {
   3234         return entry;
   3235     }
   3236 
   3237     DISC_LOCK;
   3238     disc_flags |= DF_DB_UPDATED;
   3239     DISC_UNLOCK;
   3240     entry = cpudb_entry_create(db_ref, key, FALSE);
   3241     if (entry == NULL) {
   3242         disc_external_err++;
   3243         ++disc_tot_err;
   3244         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3245                   (BSL_META("disc ERR: Error adding key\n")));
   3246         return NULL;
   3247     }
   3248     sal_memcpy(entry->base.mac, mac, sizeof(bcm_mac_t));
   3249     db_entry_clear_mod_ids(entry);
   3250     entry->base.dseq_num = dseq_num;
   3251     LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   3252               (BSL_META("disc: Added key " CPUDB_KEY_FMT
   3253                "), mac %x:%x seq %d to DB\n"), CPUDB_KEY_DISP(key),
   3254                mac[4], mac[5], dseq_num));
   3255 
   3256     return entry;
   3257 }
   3258 
   3259 /* Return index local stack port arrays if found */
   3260 STATIC int
   3261 stk_port_find(cpudb_ref_t db_ref, int unit, int port)
   3262 {
   3263     int i;
   3264 
   3265     for (i = 0; i < db_ref->local_entry->base.num_stk_ports; i++) {
   3266         if (unit == STKP_UNIT(db_ref, i) && port == STKP_PORT(db_ref, i)) {
   3267             return i;
   3268         }
   3269     }
   3270 
   3271     return -1;
   3272 }
   3273 
   3274 /* Handles versions 0, 1, and 2 config packets */
   3275 
   3276 STATIC bcm_rx_t
   3277 disc_config_pkt_handler_ver(cpudb_key_t src_key,
   3278                         int client_id,
   3279                         bcm_pkt_t *pkt,
   3280                         uint8 *payload,
   3281                         int payload_len,
   3282                         cpudb_ref_t db_ref)
   3283 {
   3284     cpudb_entry_t *entry;
   3285     uint32 src_dseq_num;
   3286     uint32 master_dseq_num;
   3287     int num_cpus;
   3288     cpudb_key_t master_key;
   3289 
   3290     /* Called with DISC_LOCK held */
   3291     /* See cfg_header_pack below */
   3292     num_cpus = payload[ENTRY_COUNT_OFS];
   3293     UNPACK_LONG(&payload[SRC_DSEQ_NUM_OFS], src_dseq_num);
   3294     CPUDB_KEY_UNPACK(&payload[CFG_MASTER_KEY_OFS], master_key);
   3295     UNPACK_LONG(&payload[CFG_MSEQ_NUM_OFS], master_dseq_num);
   3296 
   3297     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3298                 (BSL_META("disc: Config pkt in from " CPUDB_KEY_FMT " disc SN %d\n"),
   3299                  CPUDB_KEY_DISP(src_key), src_dseq_num));
   3300     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3301                 (BSL_META("disc: Cfg pkt master key " CPUDB_KEY_FMT " disc SN %d\n"),
   3302                  CPUDB_KEY_DISP(master_key), master_dseq_num));
   3303 
   3304     /*
   3305      * Search for the src key entry.
   3306      * Check the discovery sequence numbers agree.
   3307      */
   3308     CPUDB_KEY_SEARCH(db_ref, src_key, entry);
   3309     if (entry == NULL) {
   3310         ++disc_rx_pkt_err;
   3311         ++disc_tot_err;
   3312         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3313                   (BSL_META("disc ERR: Config pkt, bad source key "
   3314                             CPUDB_KEY_FMT_EOLN),
   3315                    CPUDB_KEY_DISP(src_key)));
   3316         DISC_UNLOCK;
   3317         return BCM_RX_HANDLED;
   3318     }
   3319     if (entry->base.dseq_num != src_dseq_num) {
   3320         ++disc_rx_pkt_err;
   3321         ++disc_tot_err;
   3322         LOG_WARN(BSL_LS_TKS_DISCOVER,
   3323                  (BSL_META("disc warn: Config pkt from " CPUDB_KEY_FMT
   3324                   "), seq num mismatch: pkt %d, db %d\n"),
   3325                   CPUDB_KEY_DISP(src_key),
   3326                   src_dseq_num, entry->base.dseq_num));
   3327         DISC_UNLOCK;
   3328         return BCM_RX_HANDLED;
   3329     }
   3330     if (db_ref->num_cpus != num_cpus) {
   3331         ++disc_rx_pkt_err;
   3332         ++disc_tot_err;
   3333         LOG_WARN(BSL_LS_TKS_DISCOVER,
   3334                  (BSL_META("disc warn: Config pkt from " CPUDB_KEY_FMT
   3335                   "), num CPU mismatch: pkt %d db %d\n"),
   3336                   CPUDB_KEY_DISP(src_key),
   3337                   num_cpus, db_ref->num_cpus));
   3338         DISC_UNLOCK;
   3339         return BCM_RX_HANDLED;
   3340     }
   3341 
   3342     /* If local is master, this should be a confirmation packet */
   3343     if (db_ref->local_entry == db_ref->master_entry) {
   3344         if (!CPUDB_KEY_EQUAL(master_key, db_ref->local_entry->base.key)) {
   3345             DISC_UNLOCK;
   3346             LOG_WARN(BSL_LS_TKS_DISCOVER,
   3347                      (BSL_META("disc ERR: Config pkt from " CPUDB_KEY_FMT
   3348                       "), master mismatch: pkt " CPUDB_KEY_FMT
   3349                       "), (local master)\n"),
   3350                       CPUDB_KEY_DISP(src_key),
   3351                       CPUDB_KEY_DISP(master_key)));
   3352             return BCM_RX_HANDLED;
   3353         }
   3354         entry->flags |= CPUDB_F_CONFIG_IN;
   3355     } else { /* I'm not the master. */
   3356         if (db_ref->master_entry != NULL &&
   3357             db_ref->master_entry != entry) {
   3358             LOG_WARN(BSL_LS_TKS_DISCOVER,
   3359                      (BSL_META("disc WARN: Config pkt w/ different master; "
   3360                       "overwriting\n")));
   3361         }
   3362         db_ref->master_entry = entry;
   3363         entry->flags |= CPUDB_F_IS_MASTER;
   3364         disc_flags |= DF_SEND_CFG_PKT;
   3365         disc_flags &= ~DF_CFG_PKT_SENT;
   3366     }
   3367 
   3368     DISC_UNLOCK;
   3369     DISC_WAKE;
   3370 
   3371     return BCM_RX_HANDLED;
   3372 }
   3373 
   3374 STATIC bcm_rx_t
   3375 disc_config_pkt_handler(cpudb_key_t src_key,
   3376                         int client_id,
   3377                         bcm_pkt_t *pkt,
   3378                         uint8 *payload,
   3379                         int payload_len,
   3380                         void *cookie)
   3381 {
   3382     uint8 d_ver;
   3383     cpudb_ref_t db_ref;
   3384 
   3385     if (!INIT_DONE) {
   3386         return BCM_RX_NOT_HANDLED;
   3387     }
   3388 
   3389     DISC_LOCK;
   3390 
   3391     if (!(disc_flags & DF_DISC_RUNNING) ||
   3392             (disc_flags & DF_DISC_SUCCESS)) {
   3393         DISC_UNLOCK;
   3394         LOG_WARN(BSL_LS_TKS_DISCOVER,
   3395                  (BSL_META("disc: Config pkt in when disc_flags are %x.\n"),
   3396                   disc_flags));
   3397         return BCM_RX_NOT_HANDLED;
   3398     }
   3399 
   3400     db_ref = (cpudb_ref_t)cookie;
   3401     if (!cpudb_valid(db_ref) || (db_ref->local_entry == NULL)) {
   3402         DISC_UNLOCK;
   3403         LOG_WARN(BSL_LS_TKS_DISCOVER,
   3404                  (BSL_META("disc: Config pkt in when db_ref is invalid.\n")));
   3405         return BCM_RX_NOT_HANDLED;
   3406     }
   3407 
   3408     if (!(db_ref->local_entry->flags & CPUDB_F_LOCAL_COMPLETE)) {
   3409         DISC_UNLOCK;
   3410         LOG_WARN(BSL_LS_TKS_DISCOVER,
   3411                  (BSL_META("disc: Config pkt in when local DB not complete\n")));
   3412         return BCM_RX_NOT_HANDLED;
   3413     }
   3414 
   3415     d_ver = payload[DISC_VER_OFS];
   3416 
   3417     switch (d_ver) {
   3418     case DISCOVERY_VERSION_0:
   3419     case DISCOVERY_VERSION_1:
   3420     case DISCOVERY_VERSION_2:
   3421         return disc_config_pkt_handler_ver(src_key, client_id, pkt,
   3422                                            payload, payload_len, db_ref);
   3423 
   3424     }
   3425 
   3426     /* Unknown version - throw packet away */
   3427     DISC_UNLOCK;
   3428     LOG_WARN(BSL_LS_TKS_DISCOVER,
   3429              (BSL_META("disc: Unknown discovery version %d in config\n"),
   3430               d_ver));
   3431     return BCM_RX_HANDLED;
   3432 }
   3433 
   3434 STATIC void
   3435 cfg_header_pack(cpudb_ref_t db_ref, uint8 *buf)
   3436 {
   3437     buf += CPUTRANS_HEADER_BYTES;
   3438     disc_pkt_setup(buf, DISC_PKT_TYPE_CONFIG, db_ref->num_cpus,
   3439                    0, db_ref->local_entry->base.dseq_num, disc_version);
   3440     buf += DISC_HEADER_BYTES;
   3441     CPUDB_KEY_PACK(buf, db_ref->master_entry->base.key);
   3442     buf += CPUDB_KEY_BYTES;
   3443     PACK_LONG(buf, db_ref->master_entry->base.dseq_num);
   3444     buf += sizeof(uint32);
   3445 }
   3446 
   3447 /*
   3448  * Send out config pkts to each other CPUs in DB; if master, send to
   3449  * all other CPUs.  Otherwise, just direct to the master.
   3450  *
   3451  * Returns BCM_E_XXX
   3452  */
   3453 STATIC int
   3454 disc_config_send(cpudb_ref_t db_ref)
   3455 {
   3456     uint8 *buf, *start_buf;
   3457     int len;
   3458     int rv = BCM_E_NONE;
   3459     cpudb_entry_t *entry;
   3460 
   3461     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3462                 (BSL_META("DISC:  Generating config packet(s)\n")));
   3463 
   3464     if (!cpudb_valid(db_ref) || (db_ref->master_entry == NULL)) {
   3465         LOG_WARN(BSL_LS_TKS_DISCOVER,
   3466                  (BSL_META("disc WARN: cfg send; master is NULL\n")));
   3467         return BCM_E_PARAM;
   3468     }
   3469 
   3470     if (!(LOCAL_FLAGS(db_ref) & CPUDB_F_LOCAL_COMPLETE)) {
   3471         LOG_WARN(BSL_LS_TKS_DISCOVER,
   3472                  (BSL_META("disc WARN: cfg send, but not local complete\n")));
   3473     }
   3474 
   3475     len = CPUTRANS_HEADER_BYTES + CFG_HEADER_BYTES;
   3476     buf = start_buf = DATA_SALLOC(len);
   3477     if (buf == NULL) {
   3478         ++disc_alloc_fail;
   3479         ++disc_tot_err;
   3480         return BCM_E_MEMORY;
   3481     }
   3482     cfg_header_pack(db_ref, buf);
   3483 
   3484     if (db_ref->local_entry != db_ref->master_entry) {
   3485         /* Send only to master */
   3486         rv = atp_tx(db_ref->master_entry->base.key,
   3487                     DISC_CONFIG_CLIENT_ID,
   3488                     start_buf,
   3489                     len,
   3490                     CPUTRANS_NO_HEADER_ALLOC,
   3491                     NULL,
   3492                     NULL);
   3493         if (BCM_FAILURE(rv)) {
   3494             LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3495                       (BSL_META("disc ERR:  %s (%d) Failed to tx cfg pkt to "
   3496                                 CPUDB_KEY_FMT_EOLN),
   3497                        bcm_errmsg(rv), rv,
   3498                        CPUDB_KEY_DISP(db_ref->master_entry->base.key)));
   3499         }
   3500     } else {
   3501         CPUDB_FOREACH_ENTRY(db_ref, entry) {
   3502             if (entry == db_ref->local_entry) {
   3503                 continue;
   3504             }
   3505 
   3506             LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   3507                       (BSL_META("disc: Cfg pkt out to " CPUDB_KEY_FMT_EOLN),
   3508                        CPUDB_KEY_DISP(entry->base.key)));
   3509             rv = atp_tx(entry->base.key,
   3510                         DISC_CONFIG_CLIENT_ID,
   3511                         start_buf,
   3512                         len,
   3513                         CPUTRANS_NO_HEADER_ALLOC,
   3514                         NULL,
   3515                         NULL);
   3516             if (BCM_FAILURE(rv)) {
   3517                 LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3518                           (BSL_META("disc ERR:  %s (%d) Failed to tx pkt to "
   3519                                     CPUDB_KEY_FMT_EOLN),
   3520                            bcm_errmsg(rv), rv,
   3521                            CPUDB_KEY_DISP(entry->base.key)));
   3522                 break;
   3523             }
   3524         }
   3525     }
   3526 
   3527     DATA_SFREE(start_buf);
   3528     return BCM_E_NONE;
   3529 }
   3530 
   3531 
   3532 /*
   3533  * Function:
   3534  *      disc_m_elect_default
   3535  * Purpose:
   3536  *      Default master election function
   3537  * Parameters:
   3538  *      db_ref          - The DB to analyze
   3539  * Returns:
   3540  *      DISC_RESTART_NEW_SEQ is supported
   3541  *      BCM_E_XXX
   3542  * Notes:
   3543  *      Used if no other master elect is provided.
   3544  *      Only set the local master if "global complete" is set.  This ensures
   3545  *      info is spread to other CPUs.
   3546  *
   3547  *      If local is not master, set master_entry so that discovery will
   3548  *      register to receive configuration packets
   3549  *
   3550  *      Uses highest master priority/lowest key as master;
   3551  */
   3552 
   3553 STATIC int
   3554 disc_m_elect_default(cpudb_ref_t db_ref, void *user_data)
   3555 {
   3556     cpudb_entry_t *entry, *best_entry;
   3557     int high_priority;
   3558 
   3559     COMPILER_REFERENCE(user_data);
   3560 
   3561     if (db_ref == CPUDB_REF_NULL || db_ref->local_entry == NULL) {
   3562         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3563                   (BSL_META("disc m_elect ERR:  Bad DB or local entry\n")));
   3564         return BCM_E_FAIL;
   3565     }
   3566 
   3567     /*
   3568      * First, find the highest priority in the DB;
   3569      * Then find the lowest key of those with that priority
   3570      */
   3571     best_entry = db_ref->local_entry;
   3572     high_priority = db_ref->local_entry->base.master_pri;
   3573     CPUDB_FOREACH_ENTRY(db_ref, entry) {
   3574         if (entry == db_ref->local_entry) {
   3575             continue;
   3576         }
   3577         if (entry->base.master_pri > high_priority) {
   3578             best_entry = entry;
   3579             high_priority = entry->base.master_pri;
   3580         }
   3581     }
   3582 
   3583     CPUDB_FOREACH_ENTRY(db_ref, entry) {
   3584         if (entry == db_ref->local_entry || entry == best_entry) {
   3585             continue;
   3586         }
   3587         if (entry->base.master_pri == high_priority) {
   3588             if (CPUDB_KEY_COMPARE(entry->base.key,
   3589                                   best_entry->base.key) < 0) {
   3590                 best_entry = entry;
   3591             }
   3592         }
   3593     }
   3594 
   3595     if (best_entry == db_ref->local_entry) {    /* See notes */
   3596         if (!(LOCAL_FLAGS(db_ref) & CPUDB_F_GLOBAL_COMPLETE)) {
   3597             LOG_DEBUG(BSL_LS_TKS_DISCOVER,
   3598                       (BSL_META("disc m_elect:  "
   3599                        "Local is master; wait for global complete\n")));
   3600             return BCM_E_NONE;
   3601         }
   3602     }
   3603 
   3604     if (db_ref->master_entry != NULL && db_ref->master_entry != best_entry) {
   3605         LOG_WARN(BSL_LS_TKS_DISCOVER,
   3606                  (BSL_META("DISC m_elect:  Master set, but changing\n")));
   3607     }
   3608 
   3609     db_ref->master_entry = best_entry;
   3610     best_entry->flags |= CPUDB_F_IS_MASTER;
   3611     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3612                 (BSL_META("disc m_elect: Master is "
   3613                           CPUDB_KEY_FMT ", %slocal\n"),
   3614                  CPUDB_KEY_DISP(best_entry->base.key),
   3615                  best_entry == db_ref->local_entry ? "" : "not "));
   3616     return BCM_E_NONE;
   3617 }
   3618 
   3619 #if defined(BROADCOM_DEBUG)
   3620 void
   3621 disc_counter_dump(void)
   3622 {
   3623     LOG_INFO(BSL_LS_TKS_DISCOVER,
   3624              (BSL_META("disc_alloc_fail    = %d\n"),
   3625               disc_alloc_fail));
   3626     LOG_INFO(BSL_LS_TKS_DISCOVER,
   3627              (BSL_META("disc_resource_err  = %d\n"),
   3628               disc_resource_err));
   3629     LOG_INFO(BSL_LS_TKS_DISCOVER,
   3630              (BSL_META("disc_internal_err  = %d\n"),
   3631               disc_internal_err));
   3632     LOG_INFO(BSL_LS_TKS_DISCOVER,
   3633              (BSL_META("disc_tx_pkt_err    = %d\n"),
   3634               disc_tx_pkt_err));
   3635     LOG_INFO(BSL_LS_TKS_DISCOVER,
   3636              (BSL_META("disc_rx_pkt_err    = %d\n"),
   3637               disc_rx_pkt_err));
   3638     LOG_INFO(BSL_LS_TKS_DISCOVER,
   3639              (BSL_META("disc_tot_err       = %d\n"),
   3640               disc_tot_err));
   3641     LOG_INFO(BSL_LS_TKS_DISCOVER,
   3642              (BSL_META("disc_external_err  = %d\n"),
   3643               disc_external_err));
   3644 }
   3645 #endif /* BROADCOM_DEBUG */
   3646 
   3647 static void
   3648 disc_status_set(disc_status_t status)
   3649 {
   3650     sal_mutex_take(disc_status_lock, sal_mutex_FOREVER);
   3651     disc_stat = status;
   3652     sal_mutex_give(disc_status_lock);
   3653 }
   3654 
   3655 /*
   3656  * Function:
   3657  *     int disc_election_register(*disc_election_cb_t m_elect,
   3658  *                                void *user_data)
   3659  * Purpose:
   3660  *     Register a master election function
   3661  * Parameters:
   3662  *     m_elect - election function
   3663  *     user_data - currently ignored
   3664  * Returns:
   3665  *     BCM_E_NONE     - no errors
   3666  *     BCM_E_PARAM    - m_elect is NULL
   3667  * Notes:
   3668  *     Only one registration at a time, and each new registration
   3669  *     overwrites the previous one.
   3670  */
   3671 
   3672 int
   3673 disc_election_register(disc_election_cb_t m_elect, void *user_data)
   3674 {
   3675     int rv = BCM_E_PARAM;
   3676 
   3677     if (m_elect != NULL) {
   3678         disc_m_elect = m_elect;
   3679         disc_m_user_data = user_data;
   3680 
   3681         /* Don't use an old callback interface */
   3682         disc_start_elect = NULL;
   3683         rv = BCM_E_NONE;
   3684     }
   3685 
   3686     return rv;
   3687 }
   3688 
   3689 /*
   3690  * Function:
   3691  *     disc_election_unregister(*disc_election_cb_t m_elect,
   3692  *                              void *user_data)
   3693  * Purpose:
   3694  *     Deregister a master election function
   3695  * Parameters:
   3696  *     m_elect - election function
   3697  *     user_data - currently ignored
   3698  * Returns:
   3699  *     BCM_E_NONE     - no errors
   3700  *     BCM_E_PARAM    - m_elect and/or user_data was not initially registered
   3701  * Notes:
   3702  *     Election reverts to default election function
   3703  */
   3704 
   3705 int
   3706 disc_election_unregister(disc_election_cb_t m_elect, void *user_data)
   3707 {
   3708     int rv = BCM_E_PARAM;
   3709 
   3710     if (disc_m_elect == m_elect && disc_m_user_data == user_data) {
   3711         disc_m_elect = disc_m_elect_default;
   3712         disc_m_user_data = NULL;
   3713         rv = BCM_E_NONE;
   3714     }
   3715     return rv;
   3716 }
   3717 
   3718 /*
   3719  * Function:
   3720  *      disc_run
   3721  * Purpose:
   3722  *      Run the discovery process
   3723  * Parameters:
   3724  *      db_ref       - The database reference to use; see notes
   3725  * Returns:
   3726  *      BCM_E_XXX
   3727  * Notes:
   3728  *      This routine is not re-entrant.
   3729  *
   3730  *      The discovery sequence number will be extracted from the local
   3731  *      CPU DB entry.
   3732  */
   3733 STATIC int
   3734 _disc_run_prep(cpudb_ref_t db_ref)
   3735 {
   3736     int rv = BCM_E_NONE;
   3737 
   3738     DISC_LOCK;
   3739     if (disc_flags & DF_DISC_RUNNING) {
   3740         DISC_UNLOCK;
   3741         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3742                     (BSL_META("_disc_run_prep: terminating existing disc\n")));
   3743         if (disc_abort(BCM_E_FAIL, 500000) != BCM_E_NONE) {
   3744             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3745                         (BSL_META("%s: disc_abort failed.\n"),
   3746                          FUNCTION_NAME()));
   3747             return BCM_E_BUSY;
   3748         }
   3749         DISC_LOCK;
   3750     } else {
   3751         /* reset internal flags */
   3752         disc_flags = 0;
   3753     }
   3754 
   3755     if (disc_trans_ptr == NULL) {
   3756         DISC_UNLOCK;
   3757         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3758                   (BSL_META("_disc_run ERR: Need transport pointers\n")));
   3759         return BCM_E_FAIL;
   3760     }
   3761 
   3762     if (db_ref == NULL) {
   3763         DISC_UNLOCK;
   3764         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3765                   (BSL_META("%s ERR: Need DB ref pointers\n"),
   3766                    FUNCTION_NAME()));
   3767         return BCM_E_PARAM;
   3768     }
   3769 
   3770 #if defined(BROADCOM_DEBUG)
   3771     rv = _check_start_values(db_ref);
   3772     if (BCM_FAILURE(rv)) {
   3773         DISC_UNLOCK;
   3774         return rv;
   3775     }
   3776 #endif  /* BROADCOM_DEBUG */
   3777 
   3778     rv = disc_prep(db_ref);
   3779     DISC_UNLOCK;
   3780 
   3781     return rv;
   3782 }
   3783 
   3784 /*
   3785  * Function:
   3786  *
   3787  * Purpose:
   3788  *      Register discovery packet processing
   3789  * Parameters:
   3790  *      db_ref       - The database reference to use; see notes
   3791  * Returns:
   3792  *      BCM_E_XXX
   3793  * Notes:
   3794  *      This routine is not re-entrant.
   3795  *
   3796  *      The discovery sequence number will be extracted from the local
   3797  *      CPU DB entry.
   3798  */
   3799 STATIC int
   3800 _disc_run_reg(cpudb_ref_t *db_refp)
   3801 {
   3802     int rv = BCM_E_NONE;
   3803 
   3804     /* Register with next hop */
   3805     rv = next_hop_register(disc_rx_pkt, db_refp, DISC_NH_PKT_TYPE);
   3806     if (BCM_FAILURE(rv)) {
   3807         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   3808                   (BSL_META("disc ERR: Could not register RX callback")));
   3809         DISC_LOCK;
   3810         disc_flags |= DF_DISC_ERROR;
   3811         DISC_UNLOCK;
   3812     } else {
   3813         LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3814                     (BSL_META("disc: registered with nexthop\n")));
   3815     }
   3816     return rv;
   3817 }
   3818 
   3819 /*
   3820  * Function:
   3821  *      _disc_run_unreg
   3822  * Purpose:
   3823  *      Unregister discovery packet processing
   3824  * Parameters:
   3825  *      none
   3826  * Returns:
   3827  *      BCM_E_XXX
   3828  * Notes:
   3829  */
   3830 STATIC int
   3831 _disc_run_unreg(void)
   3832 {
   3833     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3834                 (BSL_META("disc: unregistered with nexthop\n")));
   3835     return next_hop_unregister(disc_rx_pkt, DISC_NH_PKT_TYPE);
   3836 }
   3837 
   3838 /*
   3839  * Function:
   3840  *      _disc_run_loop
   3841  * Purpose:
   3842  *      Procoess discovery packets until discovery termination
   3843  * Parameters:
   3844  *      db_ref       - The database reference to use; see notes
   3845  * Returns:
   3846  *      BCM_E_XXX
   3847  * Notes:
   3848  */
   3849 STATIC int
   3850 _disc_run_loop(cpudb_ref_t db_ref)
   3851 {
   3852     int rv = BCM_E_NONE;
   3853 
   3854     /* Main discovery loop */
   3855     while (TRUE) {
   3856         DISC_SLEEP(disc_retrx_us);
   3857         DISC_LOCK;
   3858         disc_flags &= ~DF_IGNORE_PACKETS;  /* Time to check pkts */
   3859         DISC_UNLOCK;
   3860 
   3861         disc_status_update(db_ref);
   3862         if (disc_done_check(db_ref, &rv, FALSE)) {
   3863             break;
   3864         }
   3865 
   3866         disc_pkts_process(db_ref);  /* incoming and outgoing pkts */
   3867         if (disc_done_check(db_ref, &rv, FALSE)) {
   3868             break;
   3869         }
   3870 
   3871         disc_status_update(db_ref);
   3872         if (disc_done_check(db_ref, &rv, FALSE)) {
   3873             break;
   3874         }
   3875     }
   3876 
   3877     return rv;
   3878 }
   3879 
   3880 /*
   3881  * Function:
   3882  *      _disc_run_complete
   3883  * Purpose:
   3884  *      Mark completion of the discovery process
   3885  * Parameters:
   3886  *      db_ref       - The database reference to use; see notes
   3887  * Returns:
   3888  *      BCM_E_XXX
   3889  * Notes:
   3890  */
   3891 STATIC int
   3892 _disc_run_complete(cpudb_ref_t db_ref)
   3893 {
   3894     int rv = BCM_E_NONE;
   3895 
   3896     DISC_LOCK;
   3897     disc_flags |= DF_ABORT_ACK;
   3898     DISC_UNLOCK;
   3899 
   3900     return rv;
   3901 }
   3902 
   3903 /*
   3904  * Function:
   3905  *      _disc_run
   3906  * Purpose:
   3907  *      Run the discovery process once through
   3908  * Parameters:
   3909  *      db_ref       - The database reference to use; see notes
   3910  * Returns:
   3911  *      BCM_E_XXX
   3912  * Notes:
   3913  *      This routine is not re-entrant.
   3914  *
   3915  *      The discovery sequence number will be extracted from the local
   3916  *      CPU DB entry.
   3917  */
   3918 STATIC int
   3919 _disc_run(cpudb_ref_t db_ref)
   3920 {
   3921     int rv = BCM_E_NONE;
   3922 
   3923     DISC_INIT_CHECK;
   3924 
   3925     BCM_IF_ERROR_RETURN(_disc_run_prep(db_ref));
   3926 
   3927     if (!DISC_TASK) {
   3928         BCM_IF_ERROR_RETURN(_disc_run_reg(&db_ref));
   3929     }
   3930 
   3931     rv = _disc_run_loop(db_ref);
   3932 
   3933     if (!DISC_TASK) {
   3934         BCM_IF_ERROR_RETURN(_disc_run_unreg());
   3935     }
   3936 
   3937     BCM_IF_ERROR_RETURN(_disc_run_complete(db_ref));
   3938 
   3939     return rv;
   3940 }
   3941 
   3942 static int _disc_sequence_number;
   3943 
   3944 
   3945 STATIC int
   3946 _disc_run_restart_loop(cpudb_ref_t db_ref)
   3947 {
   3948     int rv = BCM_E_NONE;
   3949     int co_rv = BCM_E_NONE;
   3950 
   3951     do {
   3952         /* clear any non-local entries, either from the cpudb supplied,
   3953            or a populated cpudb from a restart. */
   3954         cpudb_clear(db_ref, TRUE);
   3955 
   3956         /* set sequence number */
   3957         if (rv != DISC_RESTART_REQUEST) {
   3958             CUR_DSEQ_NUM(db_ref) = _disc_sequence_number++;
   3959         }
   3960 
   3961         /* run discovery */
   3962         rv = _disc_run(db_ref);
   3963         co_rv = disc_callout(db_ref, rv);
   3964 
   3965 
   3966         /* Keep looping while there is a restart return value from
   3967            _disc_run() and the callout has allowed the restart. */
   3968 
   3969     } while ( (rv == DISC_RESTART_REQUEST || rv == DISC_RESTART_NEW_SEQ) &&
   3970               BCM_SUCCESS(co_rv) );
   3971 
   3972     return rv;
   3973 }
   3974 
   3975 STATIC int
   3976 disc_callout(cpudb_ref_t db_ref, int status)
   3977 {
   3978     int rv = BCM_E_PARAM;
   3979 
   3980     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3981                 (BSL_META("disc: callout status:%d\n"),
   3982                  status));
   3983     if (_disc_callback.callback) {
   3984         rv = _disc_callback.callback(db_ref, status, _disc_callback.user_data);
   3985     }
   3986     LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   3987                 (BSL_META("disc: callout returns %d\n"),
   3988                  rv));
   3989 
   3990     return rv;
   3991 }
   3992 
   3993 /*
   3994  * Discovery thread for asynchronous callback
   3995  */
   3996 STATIC void
   3997 _disc_run_thread(void *user_data)
   3998 {
   3999     do {
   4000         disc_status_set(DISC_STATUS_TASK_IDLE);
   4001         DISC_TASK_SLEEP;
   4002 
   4003         if (disc_task_cmd == DISC_TASK_CMD_RUN) {
   4004             (void)_disc_run_restart_loop(disc_task_db);
   4005         }
   4006     } while (disc_task_cmd == DISC_TASK_CMD_RUN);
   4007     disc_status_set(DISC_STATUS_INACTIVE);
   4008 
   4009     _disc_tid = SAL_THREAD_ERROR;
   4010     sal_thread_exit(0);
   4011 }
   4012 
   4013 /*
   4014  * Start discovery thread.
   4015  */
   4016 STATIC int
   4017 disc_run_task(void)
   4018 {
   4019     int  rv = BCM_E_NONE;
   4020 
   4021     if ((_disc_tid = sal_thread_create("bcmDISC",
   4022                                        DISC_THREAD_STACK,
   4023                                        DISC_THREAD_PRIORITY,
   4024                                        _disc_run_thread,
   4025                                        NULL))
   4026         == SAL_THREAD_ERROR) {
   4027         LOG_ERROR(BSL_LS_TKS_DISCOVER,
   4028                   (BSL_META("disc ERR: Could not create discovery thread\n")));
   4029         rv = BCM_E_FAIL;
   4030     }
   4031     return rv;
   4032 }
   4033 
   4034 /*
   4035  * Function:
   4036  *     int disc_run(cpudb_ref_t db_ref)
   4037  * Purpose:
   4038  *     Initiate the discovery protocol
   4039  * Parameters:
   4040  *     db_ref - CPUDB containing local entry
   4041  * Returns:
   4042  *     BCM_E_NONE     - no errors
   4043  *     BCM_E_BUSY     - the discovery protocol is currently running.
   4044  *     BCM_E_INIT     - module uninitialized
   4045  * Notes:
   4046  *     Discovery completion status is returned via a discovery
   4047  *     completion callback registered by disc_register()
   4048  *
   4049  *     Only one instance of discovery, regardless of the mode,
   4050  *     can be running at one time.
   4051  *
   4052  *     CPUDB is cleared to just the local entry.
   4053  */
   4054 
   4055 int
   4056 disc_run(cpudb_ref_t db_ref)
   4057 {
   4058     int  rv = BCM_E_INTERNAL;
   4059     cpudb_ref_t disc_db;
   4060 
   4061     if (!_disc_callback.callback) {
   4062         return BCM_E_CONFIG;
   4063     }
   4064 
   4065     sal_mutex_take(disc_status_lock, sal_mutex_FOREVER);
   4066     {
   4067         if (disc_stat == DISC_STATUS_INACTIVE) {
   4068             rv = BCM_E_INIT;
   4069         } else if (disc_stat != DISC_STATUS_TASK_IDLE) {
   4070             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   4071                         (BSL_META("disc_run busy = %d @ %d\n"),
   4072                          disc_stat, sal_time_usecs()));
   4073             rv = BCM_E_BUSY;
   4074         } else {
   4075             rv = BCM_E_NONE;
   4076             disc_stat = DISC_STATUS_TASK_RUNNING;
   4077         }
   4078     }
   4079     sal_mutex_give(disc_status_lock);
   4080 
   4081     if (BCM_SUCCESS(rv)) {
   4082         disc_db = cpudb_copy(db_ref);
   4083         if (disc_db) {
   4084             disc_task_db = disc_db;
   4085             LOG_VERBOSE(BSL_LS_TKS_DISCOVER,
   4086                         (BSL_META("disc_run @ %d\n"),
   4087                          sal_time_usecs()));
   4088             DISC_TASK_WAKE;
   4089             sal_thread_yield();
   4090         } else {
   4091             rv = BCM_E_MEMORY;
   4092         }
   4093     }
   4094 
   4095     return rv;
   4096 }
   4097 
   4098 
   4099 /*
   4100  * Function:
   4101  *     disc_register(disc_cb_t callback, void *user_data)
   4102  * Purpose:
   4103  *     Register a discovery completion callback
   4104  * Parameters:
   4105  *     callback - function to call on completion
   4106  *     user_data - user data passed to callback
   4107  * Returns:
   4108  *     BCM_E_PARAM    - callback is NULL
   4109  *     BCM_E_NONE     - no errors
   4110  * Notes:
   4111  *     Only one registration allowed at a time. New registrations
   4112  *     overwrite previous registrations.
   4113  *
   4114  *     Callback status:
   4115  *
   4116  *     BCM_E_NONE
   4117  *
   4118  *       Discovery has completed successfully.
   4119  *
   4120  *     BCM_E_TIMEOUT
   4121  *
   4122  *       Discovery has timed out while trying to complete.
   4123  *
   4124  *     BCM_E_FAIL
   4125  *     
   4126  *       Discovery has failed for some other reason.
   4127  *
   4128  *     DISC_RESTART_REQUEST
   4129  *
   4130  *       Discovery requests restart with same sequence number.  Request
   4131  *       is accepted by returning BCM_E_NONE, denied by returning
   4132  *       BCM_E_FAIL from the status callback.
   4133  *
   4134  *     DISC_RESTART_NEW_SEQ
   4135  *
   4136  *       Discovery requests restart with new sequence number.
   4137  *       Request is accepted by returning BCM_E_NONE, denied by
   4138  *       returning BCM_E_FAIL from the status callback.
   4139  *
   4140  *     DISC_PROBE_RECEIVED
   4141  *
   4142  *       A probe packet has been received which the discovery
   4143  *       process is idle.
   4144  *
   4145  *       This will not automatically initiate discovery - the event
   4146  *       callback is responsible for calling or scheduling
   4147  *       disc_run() if discovery needs to be initiated. Called from
   4148  *       a packet receive callback context.
   4149  */
   4150 
   4151 int
   4152 disc_register(disc_cb_t callback, void *user_data)
   4153 {
   4154     int rv = BCM_E_PARAM;
   4155 
   4156     if (callback) {
   4157         _disc_callback.callback = callback;
   4158         _disc_callback.user_data = user_data;
   4159         rv = BCM_E_NONE;
   4160     }
   4161 
   4162     return rv;
   4163 }
   4164 
   4165 /*
   4166  * Function:
   4167  *     disc_unregister(disc_cb_t callback, void *user_data)
   4168  * Purpose:
   4169  *     Unregister a discovery completion callback
   4170  * Parameters:
   4171  *     callback - function to unregister
   4172  *     user_data - user data passed to callback
   4173  * Returns:
   4174  *     BCM_E_NONE     - no errors
   4175  *     BCM_E_PARAM    - callback and/or user_data was not initially registered
   4176  * Notes:
   4177  */
   4178 
   4179 int
   4180 disc_unregister(disc_cb_t callback, void *user_data)
   4181 {
   4182     int rv = BCM_E_PARAM;
   4183 
   4184     if (_disc_callback.callback  == callback &&
   4185         _disc_callback.user_data == user_data) {
   4186         _disc_callback.callback = NULL;
   4187         _disc_callback.user_data = NULL;
   4188         rv = BCM_E_NONE;
   4189     }
   4190 
   4191     return rv;
   4192 }
   4193 
   4194 /*
   4195  * Function:
   4196  *     disc_init(void)
   4197  * Purpose:
   4198  *     Initialize discovery subsystem.
   4199  * Parameters:
   4200  *     none
   4201  * Returns:
   4202  *     BCM_E_NONE     - no errors
   4203  *     BCM_E_FAIL     - Discovery thread failed to initialize
   4204  *     BCM_E_MEMORY   - not enough memory
   4205  * Notes:
   4206  *     Does not initiate discovery protocol.
   4207  *     Not required if application just uses disc_start().
   4208  */
   4209 
   4210 int
   4211 disc_init(void)
   4212 {
   4213     int rv = BCM_E_INTERNAL;
   4214 
   4215     if (disc_task_sem) {
   4216         disc_deinit();
   4217     }
   4218     
   4219     DISC_INIT_CHECK;
   4220     
   4221     if (disc_task_sem != NULL) {
   4222         sal_sem_destroy(disc_task_sem);
   4223     }
   4224 
   4225     disc_task_sem = sal_sem_create("disc_task_sem", sal_sem_BINARY, 0);
   4226 
   4227     if (disc_task_sem != NULL) {
   4228         disc_task_cmd = DISC_TASK_CMD_RUN;
   4229         rv = disc_run_task();
   4230         if (BCM_SUCCESS(rv)) {
   4231             disc_stat = DISC_STATUS_TASK_IDLE;
   4232             rv = _disc_run_reg(&disc_task_db);
   4233         }
   4234     } else {
   4235         rv = BCM_E_MEMORY;
   4236     }
   4237 
   4238     if (BCM_FAILURE(rv)) {
   4239         (void)disc_deinit();
   4240     }
   4241 
   4242     return rv;
   4243 }
   4244 
   4245 
   4246 /*
   4247  * Function:
   4248  *     int disc_deinit(void)
   4249  * Purpose:
   4250  *     Shutdown discovery and deallocate resources.
   4251  * Parameters:
   4252  *     none
   4253  * Returns:
   4254  *     BCM_E_NONE     - no errors
   4255  * Notes:
   4256  */
   4257 
   4258 #ifndef DISC_DEINIT_RETRY
   4259 #define DISC_DEINIT_RETRY 5
   4260 #endif
   4261 
   4262 #ifndef DISC_DEINIT_SLEEP
   4263 #define DISC_DEINIT_SLEEP 10000
   4264 #endif
   4265 
   4266 
   4267 int
   4268 disc_deinit(void)
   4269 {
   4270     disc_status_t stat;
   4271     int i;
   4272 
   4273     for ( i=0; i<DISC_DEINIT_RETRY; i++ ) {
   4274         BCM_IF_ERROR_RETURN(disc_status_get(&stat));
   4275 
   4276         /* Stop running disc protocol */
   4277         if (stat == DISC_STATUS_TASK_RUNNING ||
   4278             stat == DISC_STATUS_SYNC_RUNNING) {
   4279             disc_abort(BCM_E_FAIL, 0);
   4280             sal_thread_yield();
   4281             sal_usleep(DISC_DEINIT_SLEEP);
   4282         } else {
   4283             break;
   4284         }
   4285     }
   4286 
   4287     if (DISC_TASK) {
   4288         (void) _disc_run_unreg();
   4289         /* Signal thread to stop */
   4290         disc_task_cmd = DISC_TASK_CMD_STOP;
   4291         DISC_TASK_WAKE;
   4292         /* May need to take extra steps if thread doesn't exit */
   4293     }
   4294 
   4295     if (disc_task_sem) {
   4296          sal_sem_destroy(disc_task_sem);
   4297     }
   4298 
   4299     if (disc_sem) {
   4300          sal_sem_destroy(disc_sem);
   4301     }
   4302 
   4303     if (disc_lock) {
   4304          sal_mutex_destroy(disc_lock);
   4305     }
   4306 
   4307     if (disc_status_lock) {
   4308          sal_mutex_destroy(disc_status_lock);
   4309     }
   4310 
   4311     return BCM_E_NONE;
   4312 }
   4313 
   4314