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

stktask.c (73900B)


      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  * Stack Task:
      8  *     Stacking coordination software
      9  *     Finite state machine based module to control discovery, topology
     10  *     analysis/programming, attaching remote devices and synchronizing
     11  *     inter-box state.
     12  *
     13  * Protected Resources:
     14  *     st_state             Current state
     15  *     bcm_st_disc_db       Discovery database
     16  *     bcm_st_cur_db        Current (active) database
     17  *     st_new_events
     18  *     st_pending_events
     19  *     st_blocked_events
     20  *                   Event bitmaps
     21  *     st_disc_tid   Discovery thread ID; used as running signal as well
     22  *     bcm_st_flags      Convey state (working to eliminate)
     23  */
     24 
     25 #include <shared/bsl.h>
     26 
     27 #include <assert.h>
     28 
     29 #include <sal/core/sync.h>
     30 #include <sal/core/time.h>
     31 #include <sal/core/alloc.h>
     32 
     33 #include <bcm/types.h>
     34 #include <bcm/error.h>
     35 #include <bcm/link.h>
     36 #include <bcm/port.h>
     37 #include <bcm/stack.h>
     38 
     39 #include <appl/cpudb/cpudb.h>
     40 #include <appl/cputrans/atp.h>
     41 #include <appl/cputrans/next_hop.h>
     42 #include <appl/discover/disc.h>
     43 
     44 #include <appl/stktask/stktask.h>
     45 #include <appl/stktask/topology.h>
     46 #include <appl/stktask/attach.h>
     47 #include <appl/stktask/topo_brd.h>    /* bcm_board_trunk */
     48 #include <appl/stktask/topo_pkt.h>
     49 
     50 /* Event Logging */
     51 #include <shared/evlog.h>
     52 SHARED_EVLOG_DECLARE(st_log)
     53 
     54 /**************** Stack Task State and Events *************/
     55 bcm_st_state_t st_state; /* Current state of stack task */
     56 
     57 /* These should be treated as static but are left visible for debugging */
     58 volatile uint32 st_blocked_events;
     59 volatile uint32 st_new_events;
     60 volatile uint32 st_pending_events;
     61 volatile int st_ev_count;
     62 
     63 /* Last time a state transition occurred */
     64 volatile sal_usecs_t st_transition_time;
     65 
     66 static volatile sal_thread_t st_disc_tid = SAL_THREAD_ERROR;
     67 int bcm_st_disc_priority = BCM_ST_DISC_PRIORITY_DEFAULT;
     68 int bcm_st_disc_stk_size = BCM_ST_DISC_STACK_SIZE_DEFAULT;
     69 
     70 /* Which events cause discovery to start from READY or restart from DISC */
     71 uint32 bcm_st_disc_startable_events = BCM_ST_DISC_STARTABLE_EVENTS_DEFAULT;
     72 
     73 /* Stack task configuration */
     74 volatile uint32 bcm_st_flags;
     75 char * bcm_st_flags_strings[] = BCM_ST_FLAGS_STRINGS;
     76 
     77 static sal_usecs_t state_timeouts[BCM_STS_MAX] = BCM_STATE_TIMEOUT_DEFAULTS;
     78 
     79 char *bcm_st_state_strings[] = BCM_ST_STATE_STRINGS;
     80 char *bcm_st_event_strings[] = BCM_ST_EVENT_STRINGS;
     81 
     82 /* Link Scan CB processing thread */
     83 static volatile sal_thread_t st_lscan_tid = SAL_THREAD_ERROR;
     84 
     85 
     86 /**********************************************************/
     87 
     88 /* The local CPUDB pointers */
     89 cpudb_ref_t volatile bcm_st_cur_db  = CPUDB_REF_NULL;
     90 cpudb_ref_t volatile bcm_st_disc_db = CPUDB_REF_NULL;
     91 
     92 /**************** Synchronization elements ****************/
     93 static sal_sem_t st_event_sem;
     94 static sal_sem_t st_disc_sem;
     95 static sal_sem_t st_lscan_sem;
     96 static sal_mutex_t st_lock;
     97 static sal_mutex_t st_lscan_lock;
     98 
     99 #define ST_SLEEP(_usecs) sal_sem_take(st_event_sem, (_usecs))
    100 #define ST_WAKE sal_sem_give(st_event_sem)
    101 
    102 #define ST_DISC_SLEEP(_usecs) sal_sem_take(st_disc_sem, (_usecs))
    103 #define ST_DISC_WAKE sal_sem_give(st_disc_sem)
    104 
    105 #define ST_LSCAN_SLEEP(_usecs) sal_sem_take(st_lscan_sem, (_usecs))
    106 #define ST_LSCAN_WAKE sal_sem_give(st_lscan_sem)
    107 
    108 #define ST_LOCK sal_mutex_take(st_lock, sal_mutex_FOREVER)
    109 #define ST_UNLOCK sal_mutex_give(st_lock)
    110 
    111 #define ST_LSCAN_LOCK sal_mutex_take(st_lscan_lock, sal_mutex_FOREVER)
    112 #define ST_LSCAN_UNLOCK sal_mutex_give(st_lscan_lock)
    113 
    114 #define ST_INIT_DONE (st_lock != NULL)
    115 /**********************************************************/
    116 
    117 /******** LINK SCAN CALLBACK PROCESSING ELEMENTS **********/
    118 typedef struct bcm_st_lscan_rsp_node_s {
    119     int                             unit;
    120     int                             link_status;
    121     bcm_port_t                      port;
    122     sal_usecs_t                     link_evt_time;
    123     struct bcm_st_lscan_rsp_node_s  *nxt;
    124 } bcm_st_lscan_rsp_node_t;
    125 
    126 typedef struct bcm_st_lscan_cb_list_s {
    127     uint8       ready;
    128     bcm_st_lscan_rsp_node_t *head;
    129     bcm_st_lscan_rsp_node_t *tail;
    130 } bcm_st_lscan_cb_list_t;
    131 
    132 static bcm_st_lscan_cb_list_t st_linkscan_list;
    133 
    134 /*
    135  * If the link scan caillback processing thread is asked to
    136  * quit then wait for max this many times in chunks of 10000 usecs.
    137  */
    138 #define BCM_ST_LSCAN_CB_QUIT_RETRIES_MAX 1000
    139 
    140 STATIC void _bcm_st_linkscan_list_enable(void);
    141 STATIC void _bcm_st_linkscan_list_disable(void);
    142 STATIC int _bcm_st_linkscan_list_push(int, bcm_port_t, int, sal_usecs_t);
    143 STATIC int _bcm_st_linkscan_list_pop(int *, bcm_port_t *, int *, sal_usecs_t *);
    144 /**********************************************************/
    145 
    146 /* The local config structure; contents copied on start */
    147 static bcm_st_config_t st_config;
    148 
    149 /*
    150  * bcm_st_cfg_flags: Configuration variable for various Stack Task
    151  * properties.  This may change dynamically.
    152  *
    153  * bcm_st_link_up_db_usec:  Number of microseconds that a stack port
    154  * must remain up before an actual link up event is given.  Link down
    155  * is always payed attention to immediately (if current state is up).
    156  */
    157 
    158 volatile uint32 bcm_st_cfg_flags = BCM_ST_CFG_FLAGS_DEFAULT;
    159 volatile int bcm_st_link_up_db_usec = BCM_ST_LINK_UP_DB_USEC_DEFAULT;
    160 
    161 /*
    162  * Stack Task Stack Port Configuration Info
    163  *
    164  * Stack ports may be enabled/disabled by calling bcm_st_stk_port_enable_set.
    165  * Indexed by stack port; link_up indicates link state per stack port;
    166  * last_up indicates last time a link up event was seen on that stack port
    167  *
    168  *   STK_PORT_LINK_UP(_sp)  Is the link currently up?
    169  *   STK_PORT_LINK_DOWN(_sp)  Is the link currently down?
    170  *   STK_PORT_LAST_LINK_UP(_sp)  Was last event "up"?
    171  *   STK_PORT_DISABLED(_sp) Has this stk port been administratively disabled?
    172  */
    173 
    174 uint32 st_stk_port_flags[CPUDB_CXN_MAX];
    175 
    176 int stk_port_last_link_event[CPUDB_CXN_MAX]; /* Up/down for last link event */
    177 int stk_port_link_state[CPUDB_CXN_MAX];  /* ST's vision of stk port's link */
    178 sal_usecs_t stk_port_last_event_us[CPUDB_CXN_MAX]; /* Time of last event */
    179 
    180 #define STK_PORT_DISABLED(_sp)  (st_stk_port_flags[_sp] & ST_SPF_DISABLED)
    181 #define STK_PORT_LINK_UP(_sp)   (st_stk_port_flags[_sp] & ST_SPF_LINK_UP)
    182 #define STK_PORT_LINK_DOWN(_sp) (!STK_PORT_LINK_UP(_sp))
    183 #define STK_PORT_LAST_LINK_UP(_sp)  \
    184     (st_stk_port_flags[_sp] & ST_SPF_LAST_EVENT_UP)
    185 
    186 #define STK_PORT_LAST_LINK_SET(_sp, _link) \
    187     if (_link == BCM_PORT_LINK_STATUS_UP)  \
    188              st_stk_port_flags[_sp] |= ST_SPF_LAST_EVENT_UP;    \
    189     else st_stk_port_flags[_sp] &= ~ST_SPF_LAST_EVENT_UP
    190 
    191 #define STK_PORT_CUR_LINK_SET(_sp, _link) \
    192     if (_link) st_stk_port_flags[_sp] |= ST_SPF_LINK_UP;    \
    193     else st_stk_port_flags[_sp] &= ~ST_SPF_LINK_UP
    194 
    195 #define FOREACH_STK_PORT(_sp) \
    196     for (_sp = 0; _sp < st_config.base.num_stk_ports; _sp++) \
    197         if (!STK_PORT_DISABLED(_sp))
    198 
    199 #define CPUDB_TOPO_DESTROY(db_ref) do { \
    200         topology_destroy(db_ref); \
    201         cpudb_destroy(db_ref); \
    202     } while (0)
    203 
    204 volatile uint32 bcm_st_atp_flags = BCM_ST_ATP_FLAGS_DEFAULT;
    205 
    206 /* Reserved modid feature enabled:
    207      reserves modid NH_TX_SRC_MOD_DEFAULT for NH.
    208      Actual modid via nh_tx_unknown_modid_get/set() */
    209 static int reserved_modid_enable = TRUE;
    210 
    211 /* Internal init function */
    212 
    213 STATIC int
    214 st_init(void)
    215 {
    216     if (st_lock == NULL) {
    217         st_lock = sal_mutex_create("bcm_stk_task");
    218         if (st_lock == NULL) {
    219             return BCM_E_MEMORY;
    220         }
    221     }
    222 
    223     if (st_event_sem == NULL) {
    224         st_event_sem = sal_sem_create("bcm_stk_task_sem", sal_sem_BINARY, 0);
    225         if (st_event_sem == NULL) {
    226             sal_mutex_destroy(st_lock);
    227             st_lock = NULL;
    228             return BCM_E_MEMORY;
    229         }
    230     }
    231 
    232     if (st_disc_sem == NULL) {
    233         st_disc_sem = sal_sem_create("bcm_stk_task_disc", sal_sem_BINARY, 0);
    234         if (st_disc_sem == NULL) {
    235             sal_mutex_destroy(st_lock);
    236             st_lock = NULL;
    237             return BCM_E_MEMORY;
    238         }
    239     }
    240 
    241     if (NULL == st_lscan_sem) {
    242 
    243         st_lscan_sem = sal_sem_create("bcm_stk_task_lscan_sem", sal_sem_BINARY, 0);
    244         if (NULL == st_lscan_sem) {
    245             sal_mutex_destroy(st_lock);
    246             st_lock = NULL;
    247             return BCM_E_MEMORY;
    248         }
    249     }
    250 
    251     if (NULL == st_lscan_lock) {
    252         st_lscan_lock = sal_mutex_create("bcm_stk_task_lscan_mtx");
    253         if (NULL == st_lscan_lock) {
    254             sal_mutex_destroy(st_lock);
    255             st_lock = NULL;
    256             return BCM_E_MEMORY;
    257         }
    258     }
    259 
    260     return BCM_E_NONE;
    261 }
    262 
    263 /****************************************************************
    264  *
    265  * Per state event processing routines
    266  */
    267 
    268 /*
    269  * Applications may want to turn off state warnings; for example,
    270  * if the application handles link down events specially.
    271  */
    272 
    273 int bcm_st_max_bad_event_warnings = BCM_ST_MAX_BAD_EVENT_WARNINGS_DEFAULT;
    274 
    275 STATIC void
    276 st_bad_event_warn(bcm_st_state_t state, bcm_st_event_t event)
    277 {
    278     static int warn_count = 0;
    279 
    280     if (warn_count < bcm_st_max_bad_event_warnings) {
    281         ++warn_count;
    282         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
    283                     (BSL_META("ST ERR: Unexpected event %s in state %s.\n"),
    284                      BCM_STE_VALID(event) ?
    285                      bcm_st_event_strings[event] : "(invalid)",
    286                      BCM_STS_VALID(state) ?
    287                      bcm_st_state_strings[state] : "(invalid)"));
    288     }
    289 }
    290 
    291 /*
    292  * BLOCKED state:
    293  *     Go to ready on timeout or unblock;
    294  *     Otherwise, record discovery restart events in blocked_events
    295  */
    296 
    297 STATIC bcm_st_state_t
    298 st_blocked_event_process(bcm_st_event_t event)
    299 {
    300     bcm_st_state_t new_state = st_state;
    301 
    302     if (event == BCM_STE_UNBLOCK || event == BCM_STE_TIMEOUT) {
    303         new_state = BCM_STS_READY;
    304         st_new_events |= st_blocked_events;
    305     } else if (BCM_STE_DISC_STARTABLE(event)) {
    306         /* Record events that might restart discovery */
    307         st_blocked_events |= BCM_STE_FLAG(event);
    308     }
    309 
    310     return new_state;
    311 }
    312 
    313 /*
    314  * READY state:
    315  *     Go to DISC on restart events.
    316  */
    317 
    318 STATIC bcm_st_state_t
    319 st_ready_event_process(bcm_st_event_t event)
    320 {
    321     bcm_st_state_t new_state = st_state;
    322 
    323     if (BCM_STE_DISC_STARTABLE(event)) { /* Start discovery */
    324         new_state = BCM_STS_DISC;
    325     } else if (event == BCM_STE_BLOCK || event == BCM_STE_TIMEOUT) {  /* block */
    326         new_state = BCM_STS_BLOCKED;
    327     } else {
    328         st_bad_event_warn(BCM_STS_READY, event);
    329     }
    330 
    331     return new_state;
    332 }
    333 
    334 /*
    335  * DISC state:
    336  *     Go to TOPO on success; restart DISC, block or ignore otherwise
    337  */
    338 
    339 STATIC bcm_st_state_t
    340 st_disc_event_process(bcm_st_event_t event)
    341 {
    342     bcm_st_state_t new_state = st_state;
    343 
    344     if (BCM_STE_DISC_STARTABLE(event)) { /* Re-start discovery */
    345         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
    346                     (BSL_META("ST: State DISC, event %s restarting discovery\n"),
    347                      bcm_st_event_strings[event]));
    348         /* Signal discovery to restart */
    349         st_config.st_disc_abort(DISC_RESTART_NEW_SEQ, 0);
    350         /* If currently sleeping, wake it */
    351         ST_LOCK;
    352         if (bcm_st_flags & BCM_STF_DISC_SLEEPING) {
    353             ST_DISC_WAKE;
    354         }
    355         ST_UNLOCK;
    356     } else if (event == BCM_STE_DISC_SUCCESS) {  /* Success */
    357         new_state = BCM_STS_TOPO;
    358     } else if (    (event == BCM_STE_BLOCK) ||
    359                    (event == BCM_STE_TIMEOUT) ||
    360                    (event == BCM_STE_DISC_FAILURE)) {  /* block */
    361         new_state = BCM_STS_BLOCKED;
    362     } else {
    363         st_bad_event_warn(BCM_STS_DISC, event);
    364     }
    365 
    366     return new_state;
    367 }
    368 
    369 /*
    370  * TOPO state:
    371  *     Go to ATTACH on success; restart DISC, block or ignore otherwise
    372  */
    373 
    374 STATIC bcm_st_state_t
    375 st_topo_event_process(bcm_st_event_t event)
    376 {
    377     bcm_st_state_t new_state = st_state;
    378 
    379     if (event == BCM_STE_DISC_RESTART) {  /* Special case for restart */
    380         new_state = BCM_STS_DISC;
    381     } else if (BCM_STE_DISC_STARTABLE(event)) { /* Record blocked restart */
    382         st_blocked_events |= BCM_STE_FLAG(event);
    383     } else if (event == BCM_STE_TOPO_SUCCESS) {  /* Success */
    384         new_state = BCM_STS_ATTACH;
    385     } else if (   (event == BCM_STE_BLOCK) ||
    386                   (event == BCM_STE_TIMEOUT) ||
    387                   (event == BCM_STE_TOPO_FAILURE)) {  /* block */
    388         new_state = BCM_STS_BLOCKED;
    389     } else {
    390         st_bad_event_warn(BCM_STS_TOPO, event);
    391     }
    392 
    393     return new_state;
    394 }
    395 
    396 /*
    397  * ATTACH state:
    398  *     Go to BLOCKED on success; restart DISC, block or ignore otherwise
    399  */
    400 
    401 STATIC bcm_st_state_t
    402 st_attach_event_process(bcm_st_event_t event)
    403 {
    404     bcm_st_state_t new_state = st_state;
    405 
    406     if (event == BCM_STE_DISC_RESTART) {  /* Special case for restart */
    407         new_state = BCM_STS_DISC;
    408     } else if (BCM_STE_DISC_STARTABLE(event)) { /* Record blocked restart */
    409         st_blocked_events |= BCM_STE_FLAG(event);
    410     } else if (  (event == BCM_STE_ATTACH_SUCCESS) ||
    411                  (event == BCM_STE_ATTACH_FAILURE) ||
    412                  (event == BCM_STE_BLOCK) ||
    413                  (event == BCM_STE_TIMEOUT)  ) {
    414         new_state = BCM_STS_BLOCKED;
    415     } else {
    416         st_bad_event_warn(BCM_STS_ATTACH, event);
    417     }
    418 
    419     return new_state;
    420 }
    421 
    422 /* LOCK HELD; start discovery. */
    423 
    424 STATIC void
    425 discovery_start(void)
    426 {
    427     /* Give warning if discovery thread not detected or not sleeping */
    428     if (st_disc_tid == SAL_THREAD_ERROR) {
    429         /* Throw error; abort and return to caller */
    430         bcm_st_flags |= BCM_STF_ABORT;
    431         LOG_ERROR(BSL_LS_TKS_STKTASK,
    432                   (BSL_META("ST: Discovery thread not running on start; aborting\n")));
    433         return;
    434     }
    435     if (!(bcm_st_flags & BCM_STF_DISC_SLEEPING)) {
    436         LOG_WARN(BSL_LS_TKS_STKTASK,
    437                  (BSL_META("ST: Discovery thread not sleeping on start\n")));
    438         
    439     }
    440 
    441     /* Clear all possible restart events */
    442     st_blocked_events &= ~bcm_st_disc_startable_events;
    443     st_pending_events &= ~bcm_st_disc_startable_events;
    444     st_new_events &= ~bcm_st_disc_startable_events;
    445 
    446     ST_DISC_WAKE;
    447 }
    448 
    449 /* LOCK NOT HELD; stop discovery. */
    450 
    451 STATIC int
    452 discovery_stop(int retry_max)
    453 {
    454     int rv = BCM_E_NONE;
    455     int retries = 0;
    456 
    457     rv = st_config.st_disc_abort(BCM_E_FAIL, 0);
    458     if (rv < 0) {
    459         LOG_WARN(BSL_LS_TKS_STKTASK,
    460                  (BSL_META("ST: Discovery abort (fail) returns %s\n"),
    461                   bcm_errmsg(rv)));
    462     }
    463 
    464     while (!(bcm_st_flags & BCM_STF_DISC_SLEEPING) &&
    465            (retries++ < retry_max)) {
    466         sal_thread_yield();
    467         sal_usleep(10000);
    468     }
    469     if (!(bcm_st_flags & BCM_STF_DISC_SLEEPING)) {
    470         LOG_WARN(BSL_LS_TKS_STKTASK,
    471                  (BSL_META("ST: Discovery thread won't sleep; aborting\n")));
    472         ST_LOCK;
    473         bcm_st_flags |= BCM_STF_ABORT;
    474         ST_UNLOCK;
    475         rv = BCM_E_FAIL;
    476     }
    477     return rv;
    478 }
    479 
    480 /* LOCK NOT HELD; quit discovery. */
    481 
    482 STATIC int
    483 discovery_quit(int retry_max)
    484 {
    485     int rv = BCM_E_NONE;
    486     int retries = 0;
    487     int flags;
    488 
    489     ST_LOCK;
    490     flags = (bcm_st_flags |= BCM_STF_ABORT);
    491     ST_UNLOCK;
    492     if (!(flags & BCM_STF_DISC_SLEEPING)) {
    493         rv = st_config.st_disc_abort(BCM_E_FAIL, 0);
    494         if (rv < 0) {
    495             LOG_WARN(BSL_LS_TKS_STKTASK,
    496                      (BSL_META("ST: Discovery abort (fail) returns %s\n"),
    497                       bcm_errmsg(rv)));
    498         }
    499     } else {
    500         ST_DISC_WAKE;
    501     }
    502 
    503     while (st_disc_tid != SAL_THREAD_ERROR && (retries++ < retry_max)) {
    504         sal_thread_yield();
    505         sal_usleep(10000);
    506     }
    507     if (st_disc_tid != SAL_THREAD_ERROR) {
    508         LOG_WARN(BSL_LS_TKS_STKTASK,
    509                  (BSL_META("ST: Discovery thread won't quit\n")));
    510         rv = BCM_E_FAIL;
    511     }
    512     return rv;
    513 }
    514 
    515 /* LOCK HELD; Transition discovery DB to current DB. */
    516 
    517 STATIC void
    518 promote_disc_database(void)
    519 {
    520     if (cpudb_valid(bcm_st_cur_db)) {
    521         if (cpudb_valid(bcm_st_cur_db->old_db)) {
    522             CPUDB_TOPO_DESTROY(bcm_st_cur_db->old_db);
    523         }
    524     }
    525     bcm_st_disc_db->old_db = bcm_st_cur_db;
    526     bcm_st_cur_db = bcm_st_disc_db;
    527     bcm_st_disc_db = CPUDB_REF_NULL;
    528 }
    529 
    530 /*
    531  * The state is changing.  Notify the appl before change occurs.  Then,
    532  * there are a few transitions that require some extra work.
    533  */
    534 
    535 STATIC void
    536 st_state_change(bcm_st_state_t new_state, bcm_st_event_t event)
    537 {
    538     int rv;
    539     sal_usecs_t ttime;
    540 
    541     ttime = sal_time_usecs();
    542     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
    543                 (BSL_META("ST: Trans from %s to %s on event %s [T=%u]\n"),
    544                  bcm_st_state_strings[st_state],
    545                  bcm_st_state_strings[new_state],
    546                  bcm_st_event_strings[event],
    547                  ttime));
    548 
    549     /* Tell application about transition */
    550     rv = st_config.st_transition(st_state, event, new_state,
    551                                  bcm_st_disc_db, bcm_st_cur_db);
    552     if (rv < 0) { /* Error from appl state transition */
    553         LOG_ERROR(BSL_LS_TKS_STKTASK,
    554                   (BSL_META("ST: Appl trans returns %s; State was %s.\n"),
    555                    bcm_errmsg(rv), bcm_st_state_strings[st_state]));
    556         new_state = BCM_STS_BLOCKED;
    557     }
    558 
    559     /* new state could change due to above; make sure still different */
    560     if (new_state == st_state) {
    561         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
    562                     (BSL_META("ST: Trans from %s cancelled\n"),
    563                      bcm_st_state_strings[st_state]));
    564         return;
    565     }
    566 
    567     ST_LOCK;
    568     st_state = new_state;
    569 
    570     /* Clear timeout flag as that would have applied to previous state */
    571     st_pending_events &= ~BCM_STE_FLAG(BCM_STE_TIMEOUT);
    572     st_new_events &= ~BCM_STE_FLAG(BCM_STE_TIMEOUT);
    573 
    574     /*
    575      * Some states require a bit of post processing
    576      *     Enter switch w/ lock; exit unlocked.
    577      */
    578     switch (new_state) {
    579     case BCM_STS_BLOCKED: /* Stop discovery if running */
    580         ST_UNLOCK;
    581         (void)discovery_stop(BCM_ST_DISC_STOP_RETRIES_MAX);
    582         break;
    583     case BCM_STS_DISC: /* Start discovery */
    584         discovery_start();
    585         ST_UNLOCK;
    586         break;
    587     case BCM_STS_TOPO: /* Start topology processing */
    588         ST_UNLOCK;
    589         if ((rv = st_config.st_topo(bcm_st_disc_db)) < 0) {
    590             LOG_WARN(BSL_LS_TKS_STKTASK,
    591                      (BSL_META("ST: st_topo returns %s\n"),
    592                       bcm_errmsg(rv)));
    593         }
    594         break;
    595     case BCM_STS_ATTACH: /* Switch database over; call stack attach update */
    596         promote_disc_database();
    597         ST_UNLOCK;
    598         if ((rv = st_config.st_attach(bcm_st_cur_db)) < 0) {
    599             LOG_WARN(BSL_LS_TKS_STKTASK,
    600                      (BSL_META("ST: st_attach returns %s\n"),
    601                       bcm_errmsg(rv)));
    602         }
    603         break;
    604     default:
    605         ST_UNLOCK;
    606         break;
    607     }
    608     st_transition_time = ttime;
    609 }
    610 
    611 /*
    612  * Process one event.  Just check the current state and call it's event
    613  * processing routine.
    614  */
    615 STATIC void
    616 st_event_process(bcm_st_event_t event)
    617 {
    618     bcm_st_state_t new_state = BCM_STS_INVALID;
    619 #if defined(INCLUDE_SHARED_EVLOG)
    620     bcm_st_state_t prev_state = BCM_STS_INVALID;
    621 #endif
    622         
    623     switch (st_state) {
    624     case BCM_STS_BLOCKED:
    625         new_state = st_blocked_event_process(event);
    626         break;
    627     case BCM_STS_READY:
    628         new_state = st_ready_event_process(event);
    629         break;
    630     case BCM_STS_DISC:
    631         new_state = st_disc_event_process(event);
    632         break;
    633     case BCM_STS_TOPO:
    634         new_state = st_topo_event_process(event);
    635         break;
    636     case BCM_STS_ATTACH:
    637         new_state = st_attach_event_process(event);
    638         break;
    639     default:  /* Shouldn't get here */
    640         assert(0 && "ST: INVALID STATE");
    641         break;
    642     }
    643 
    644     if (new_state != BCM_STS_INVALID && new_state != st_state) {
    645 #if defined(INCLUDE_SHARED_EVLOG)
    646         prev_state = st_state;        
    647         SHARED_EVENT_LOG(st_log, "START",
    648                          st_state, new_state, event, 0);
    649 #endif
    650         
    651         st_state_change(new_state, event);
    652 
    653 #if defined(INCLUDE_SHARED_EVLOG)
    654         SHARED_EVENT_LOG(st_log, "END",
    655                          prev_state, st_state, event, 0);
    656 #endif
    657     }
    658 }
    659 
    660 #define PENDING_FLAG(event)  (st_pending_events & BCM_STE_FLAG(event))
    661 #define CLEAR_PENDING_FLAG(event)  st_pending_events &= ~BCM_STE_FLAG(event)
    662 
    663 /*
    664  * Event precedence:
    665  *     BLOCK over UNBLOCK
    666  *     SUCCESS over FAILURE in general
    667  *
    668  * These are resolved by the order in which the events are processed,
    669  * which in turn is the order in which they're defined.  See stktask.h.
    670  */
    671 
    672 STATIC void
    673 st_pending_events_handle(void)
    674 {
    675     int i;
    676 
    677     for (i = 0; i < BCM_STE_MAX; i++) {
    678         if (PENDING_FLAG(i)) {
    679             st_event_process((bcm_st_event_t)i);
    680         }
    681         CLEAR_PENDING_FLAG(i);
    682     }
    683 
    684     if (st_pending_events != 0) {
    685         LOG_WARN(BSL_LS_TKS_STKTASK,
    686                  (BSL_META("ST: Clearing illegal pending flags: 0x%x\n"),
    687                   st_pending_events));
    688         st_pending_events = 0;
    689     }
    690 }
    691 
    692 #undef PENDING_FLAG
    693 #undef CLEAR_PENDING_FLAG
    694 
    695 /*
    696  * Returns number of usecs until next time out;
    697  *     0 if already timedout.
    698  *     sal_sem_FOREVER if no timeout set
    699  */
    700 
    701 STATIC sal_usecs_t
    702 st_timeout_get(void)
    703 {
    704     int diff;
    705     sal_usecs_t timeout = (sal_usecs_t)sal_sem_FOREVER;
    706 
    707     if (state_timeouts[st_state] != 0) {
    708         /* Timeout out active for this state */
    709         diff = SAL_USECS_SUB(sal_time_usecs(), st_transition_time);
    710         if ((diff < 0) || (diff > state_timeouts[st_state])) {
    711             timeout = 0;
    712         } else { /* diff < timeout */
    713             timeout = state_timeouts[st_state] - diff;
    714         }
    715     }
    716 
    717     return timeout;
    718 }
    719 
    720 /*
    721  * Is port's link state pending (needing attention)?
    722  *    That is, link is curently down and was last reported up.
    723  */
    724 #define STK_PORT_LINK_PENDING(_i) \
    725     (STK_PORT_LINK_DOWN(_i) && STK_PORT_LAST_LINK_UP(_i))
    726 
    727 /*
    728  * Checks for pending link change events; finds time to next
    729  * link change consideration if there are any; otherwise, returns time
    730  * until next timeout (if any)
    731  */
    732 
    733 STATIC sal_usecs_t
    734 st_sleep_time_get(void)
    735 {
    736     sal_usecs_t sleep_time;
    737     sal_usecs_t link_check = 0; /* Compiler warning */
    738     sal_usecs_t cur_time;
    739     int diff;
    740     int i, found = FALSE;
    741 
    742     sleep_time = st_timeout_get();
    743 
    744     /* Look for pending link change events */
    745     cur_time = sal_time_usecs();
    746     FOREACH_STK_PORT(i) {
    747         if (STK_PORT_LINK_PENDING(i)) {
    748 
    749             /* Get time since last event; then subtract from debounce time */
    750             diff = SAL_USECS_SUB(cur_time, stk_port_last_event_us[i]);
    751             diff = bcm_st_link_up_db_usec - diff;
    752             diff = diff < 0 ? 0 : diff;
    753             /* diff is now time until pending link event should debounce */
    754 
    755             if (found) {  /* Get minimum of those found */
    756                 if (diff < link_check) {
    757                     link_check = diff;
    758                 }
    759             } else {
    760                 link_check = diff;
    761                 found = TRUE;
    762             }
    763         }
    764     }
    765 
    766     if (found && link_check < sleep_time) {
    767         sleep_time = link_check;
    768     }
    769 
    770     return sleep_time;
    771 }
    772 
    773 
    774 STATIC void
    775 link_up_debounce_check(void)
    776 {
    777     sal_usecs_t cur_time;
    778     int diff;
    779     int i;
    780 
    781     cur_time = sal_time_usecs();
    782 
    783     FOREACH_STK_PORT(i) {
    784         if (STK_PORT_LINK_PENDING(i)) {
    785             diff = SAL_USECS_SUB(cur_time, stk_port_last_event_us[i]);
    786             if (diff >= bcm_st_link_up_db_usec || diff < 0) {
    787                 /* Set port link state to 1 and give link up event */
    788                 STK_PORT_CUR_LINK_SET(i, TRUE);
    789                 st_new_events |= BCM_STE_FLAG(BCM_STE_LINK_UP);
    790             } /* Debounce time has elapsed */
    791         } /* last event up and currently down */
    792     } /* Foreach connection */
    793 }
    794 
    795 /****************************************************************
    796  *
    797  * Stack event loop
    798  */
    799 
    800 STATIC void
    801 st_event_loop(void)
    802 {
    803     sal_usecs_t sleep_time;
    804 
    805     while (!(bcm_st_flags & BCM_STF_ABORT)) {
    806         if (st_new_events == 0 && st_pending_events == 0) {
    807             ST_LOCK;
    808             sleep_time = st_sleep_time_get();
    809             ST_UNLOCK;
    810             if (sleep_time > 0) {
    811                 ST_SLEEP(sleep_time);
    812             }
    813         }
    814 
    815         if (bcm_st_flags & BCM_STF_ABORT) {
    816             break;  /* Signalled to exit */
    817         }
    818         /* Check again to see if timeout has occurred */
    819         ST_LOCK;
    820         if (st_timeout_get() == 0) {
    821             st_new_events |= BCM_STE_FLAG(BCM_STE_TIMEOUT);
    822         }
    823 
    824         /* Check if link debounce needs attention */
    825         link_up_debounce_check();
    826 
    827         /* Transition new flags to pending flags */
    828         st_pending_events |= st_new_events;
    829         st_new_events = 0;
    830         ST_UNLOCK;
    831 
    832         st_pending_events_handle();
    833     }
    834 }
    835 
    836 
    837 /****************************************************************
    838  *
    839  * Utility functions, stack ports, etc.
    840  */
    841 
    842 STATIC bcm_rx_t st_nh_callback(cpudb_key_t src_key,
    843                                int mplx_num,
    844                                int rx_unit,
    845                                int rx_port,
    846                                uint8 *pkt_buf,
    847                                int len,
    848                                void *cookie);
    849 
    850 STATIC void st_linkscan_handler(int unit,
    851                                 bcm_port_t port,
    852                                 bcm_port_info_t *info);
    853 
    854 STATIC void st_comm_failure(cpudb_key_t key);
    855 
    856 /* Generate the list of units with stack ports from local information */
    857 
    858 static int st_stk_units[CPUDB_UNITS_MAX];
    859 static int st_num_stk_units;
    860 
    861 STATIC int
    862 stk_units_gen(void)
    863 {
    864     cpudb_unit_port_t *sp;
    865     int i, j;
    866     int found;
    867 
    868     st_num_stk_units = 0;
    869 
    870     /* Create list of unique entries */
    871     for (i = 0; i < st_config.base.num_stk_ports; i++) {
    872         found = FALSE;
    873         sp = (cpudb_unit_port_t *)&st_config.base.stk_ports[i];
    874         for (j = 0; j < st_num_stk_units; j++) {
    875             if (st_stk_units[j] == sp->unit) {
    876                 found = TRUE;
    877                 break;
    878             }
    879         }
    880         if (!found) {
    881             if (st_num_stk_units >= CPUDB_UNITS_MAX) {
    882                 LOG_ERROR(BSL_LS_TKS_STKTASK,
    883                           (BSL_META("ST: cannot stack more than %d units\n"),
    884                            CPUDB_UNITS_MAX));
    885                 return BCM_E_FAIL;
    886             }
    887             st_stk_units[st_num_stk_units++] = sp->unit;
    888         }
    889     }
    890     
    891     return BCM_E_NONE;
    892 }
    893 
    894 /* Link and communications setup.  Called once during ST initialization */
    895 
    896 STATIC void
    897 st_comm_setup(void)
    898 {
    899     int i, rv;
    900     int unit, port;
    901 
    902     /* Register for communication failure notification */
    903     if (bcm_st_cfg_flags & BCM_STC_COMM_FAIL_REGISTER) {
    904         atp_timeout_register(st_comm_failure);
    905     }
    906 
    907     for (i = 0; i < st_config.base.num_stk_ports; i++) {
    908         unit = st_config.base.stk_ports[i].unit;
    909         port = st_config.base.stk_ports[i].port;
    910         rv = next_hop_port_add(unit, port, 0);
    911         if (rv < 0) {
    912             LOG_WARN(BSL_LS_TKS_STKTASK,
    913                      (BSL_META("ST: stkport %d.%d nexthop add failed: %s\n"),
    914                       unit, port, bcm_errmsg(rv)));
    915         }
    916     }
    917 
    918     /* Register to receive discovery packets; warn if NH not running */
    919     if (bcm_st_cfg_flags & BCM_STC_DISC_PKT_REGISTER) {
    920         rv = next_hop_register(st_nh_callback, NULL, SHARED_PKT_TYPE_DISC_NH);
    921         if (rv < 0) {
    922             LOG_WARN(BSL_LS_TKS_STKTASK,
    923                      (BSL_META("ST: nexthop register failed: %s\n"),
    924                       bcm_errmsg(rv)));
    925         }
    926     }
    927     if (!next_hop_running()) {
    928         LOG_WARN(BSL_LS_TKS_STKTASK,
    929                  (BSL_META("ST: Nexthop is not running\n")));
    930     }
    931     if ((rv = next_hop_update((cpudb_base_t *)&st_config.base)) < 0) {
    932         LOG_WARN(BSL_LS_TKS_STKTASK,
    933                  (BSL_META("ST: Nexthop update returns %s\n"),
    934                   bcm_errmsg(rv)));
    935     }
    936 
    937     if (bcm_st_cfg_flags & BCM_STC_START_ATP) { /* Start and bring up ATP */
    938         if (!atp_running()) {
    939             uint32 unit_bmp = 0;
    940 
    941             for (i = 0; i < st_num_stk_units; i++) {
    942                 unit_bmp |= (1 << st_stk_units[i]);
    943             }
    944             if ((rv = atp_start(bcm_st_atp_flags, unit_bmp,
    945                                 BCM_RCO_F_ALL_COS)) < 0) {
    946                 LOG_WARN(BSL_LS_TKS_STKTASK,
    947                          (BSL_META("ST: ATP start returns %s\n"),
    948                           bcm_errmsg(rv)));
    949             }
    950         }
    951     }
    952 
    953     /* Add local key to ATP */
    954     if ((rv = atp_key_add(st_config.base.key, TRUE)) < 0) {
    955         LOG_WARN(BSL_LS_TKS_STKTASK,
    956                  (BSL_META("ST: ATP key add returns %s\n"),
    957                   bcm_errmsg(rv)));
    958     }
    959 }
    960 
    961 /* Undo above code on shutdown */
    962 
    963 STATIC void
    964 st_comm_shutdown(void)
    965 {
    966     int i;
    967 
    968     for (i = 0; i < st_num_stk_units; i++) {
    969         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
    970                     (BSL_META("ST: linkscan unregister\n")));
    971         (void)bcm_linkscan_unregister(st_stk_units[i], st_linkscan_handler);
    972     }
    973     if (bcm_st_cfg_flags & BCM_STC_COMM_FAIL_REGISTER) {
    974         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
    975                     (BSL_META("ST: unregister comm fail\n")));
    976         (void)atp_timeout_register(NULL);
    977     }
    978     if (bcm_st_cfg_flags & BCM_STC_DISC_PKT_REGISTER) {
    979         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
    980                     (BSL_META("ST: disc packet unregister\n")));
    981         (void)next_hop_unregister(st_nh_callback, SHARED_PKT_TYPE_DISC_NH);
    982     }
    983     if (bcm_st_cfg_flags & BCM_STC_START_ATP) {
    984         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
    985                     (BSL_META("ST: stopping ATP\n")));
    986         (void)atp_stop();
    987     }
    988 }
    989 
    990 /****************************************************************
    991  *
    992  * Next hop packet handler (to check for discovery pkts)
    993  */
    994 
    995 /*
    996  * Return index of stack port if present, -1 if not
    997  * Note that disabled ports are ignored.
    998  */
    999 
   1000 STATIC int
   1001 st_stk_port_find(int unit, int port, int include_disabled)
   1002 {
   1003     int i;
   1004     cpudb_unit_port_t *sp_info;
   1005 
   1006     if (!(bcm_st_flags & BCM_STF_CONFIG_LOADED)) {
   1007         return -1;
   1008     }
   1009 
   1010     if (include_disabled) {
   1011         for (i = 0; i < st_config.base.num_stk_ports; i++) {
   1012             sp_info = (cpudb_unit_port_t *)&st_config.base.stk_ports[i];
   1013             if (unit == sp_info->unit && port == sp_info->port) {
   1014                 return i;
   1015             }
   1016         }
   1017     } else {
   1018         FOREACH_STK_PORT(i) {
   1019             sp_info = (cpudb_unit_port_t *)&st_config.base.stk_ports[i];
   1020             if (unit == sp_info->unit && port == sp_info->port) {
   1021                 return i;
   1022             }
   1023         }
   1024     }
   1025     return -1;
   1026 }
   1027 
   1028 STATIC bcm_rx_t
   1029 st_nh_callback(cpudb_key_t src_key,
   1030                int mplx_num,
   1031                int rx_unit,
   1032                int rx_port,
   1033                uint8 *pkt_buf,
   1034                int len,
   1035                void *cookie)
   1036 {
   1037     int idx;
   1038 
   1039     if (!(bcm_st_flags & BCM_STF_RUNNING)) {        /* Task isn't running */
   1040         return BCM_RX_NOT_HANDLED;
   1041     }
   1042     if (bcm_st_flags & BCM_STF_ABORT) {             /* Aborted */
   1043         return BCM_RX_NOT_HANDLED;
   1044     }
   1045     if (!(bcm_st_flags & BCM_STF_DISC_SLEEPING)) {  /* Discovery running */
   1046         return BCM_RX_NOT_HANDLED;
   1047     }
   1048 
   1049     /*
   1050      * If received on a stack port and it's a probe packet,
   1051      * count as an event
   1052      */
   1053     idx = st_stk_port_find(rx_unit, rx_port, FALSE);
   1054     if (idx < 0) {
   1055         LOG_WARN(BSL_LS_TKS_STKTASK,
   1056                  (BSL_META("ST: Nexthop pkt on non-stk port (%d, %d)\n"),
   1057                   rx_unit, rx_port));
   1058         return BCM_RX_NOT_HANDLED;
   1059     }
   1060 
   1061     /* If link state of stack link is not up, ignore packet */
   1062     if (!STK_PORT_LINK_UP(idx)) {
   1063         return BCM_RX_HANDLED;
   1064     }
   1065 
   1066     if (disc_pkt_type_get(pkt_buf) != SHARED_PKT_TYPE_PROBE) {
   1067         /* Only pay attention to probe packets */
   1068         return BCM_RX_HANDLED;
   1069     }
   1070 
   1071     if (st_state == BCM_STS_TOPO) {
   1072         /* If we're in TOPO and we receive a probe, fail TOPO.
   1073            There's no point in completing topology configuration if
   1074            there's another system that needs to be part of the stack.
   1075         */
   1076         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1077                     (BSL_META("ST: Probe pkt in TOPO from " CPUDB_KEY_FMT_EOLN),
   1078                      CPUDB_KEY_DISP(src_key)));
   1079 
   1080         bcm_st_event_send(BCM_STE_TOPO_FAILURE);
   1081 
   1082     } else {
   1083         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1084                     (BSL_META("ST: Probe pkt in from " CPUDB_KEY_FMT_EOLN),
   1085                      CPUDB_KEY_DISP(src_key)));
   1086         /* Discovery pkt on stack port */
   1087         bcm_st_event_send(BCM_STE_DISC_PKT);
   1088     }
   1089 
   1090 
   1091     return BCM_RX_HANDLED;
   1092 }
   1093 
   1094 /****************************************************************
   1095  *
   1096  * Callback for link change event
   1097  */
   1098 
   1099 STATIC void
   1100 st_linkscan_handler(int unit, bcm_port_t port, bcm_port_info_t *info)
   1101 {
   1102     /* Enqueue the response to a list which will be read aysncronously */
   1103     _bcm_st_linkscan_list_push(unit, port, info->linkstatus, sal_time_usecs());
   1104 
   1105     /* Wake the linkscan processing thread to process the link event */
   1106     ST_LSCAN_WAKE;
   1107 }
   1108 
   1109 /*
   1110  * Function:
   1111  *      st_link_state_set/get
   1112  * Purpose:
   1113  *      Get/set the internal (stacktask) link state of stack ports
   1114  * Parameters:
   1115  *      unit, port   -- of stack port to examine
   1116  * Returns:
   1117  *      BCM_E_XXX
   1118  * Notes:
   1119  *      Applications may control link signalling to stack task by
   1120  * clearing the BCM_STC_LINK_REGISTER in bcm_st_cfg_flags and setting up
   1121  * their own routines to send signals.  Note, however, that stack task
   1122  * maintains a lot of link state and does its own debounce.  If the
   1123  * application sends link signals, it must call st_link_state_set as
   1124  * well.  Debounce is then disabled.
   1125  */
   1126 
   1127 int
   1128 bcm_st_link_state_set(int unit, bcm_port_t port, int link)
   1129 {
   1130     int idx;
   1131 
   1132     ST_LOCK;
   1133     idx = st_stk_port_find(unit, port, FALSE);
   1134     if (idx < 0) {   /* Not a stack port */
   1135         ST_UNLOCK;
   1136         return BCM_E_NOT_FOUND;
   1137     }
   1138 
   1139     /* Setting both disables debounce */
   1140     STK_PORT_CUR_LINK_SET(idx, link);
   1141     STK_PORT_LAST_LINK_SET(idx, link);
   1142     ST_UNLOCK;
   1143 
   1144     return BCM_E_NONE;
   1145 }
   1146 
   1147 int
   1148 bcm_st_link_state_get(int unit, bcm_port_t port, int *link)
   1149 {
   1150     int idx;
   1151 
   1152     if (link == NULL) {
   1153         return BCM_E_PARAM;
   1154     }
   1155 
   1156     ST_LOCK;
   1157     idx = st_stk_port_find(unit, port, FALSE);
   1158     if (idx < 0) {   /* Not a stack port */
   1159         ST_UNLOCK;
   1160         return BCM_E_NOT_FOUND;
   1161     }
   1162 
   1163     *link = STK_PORT_LINK_UP(idx);        
   1164     ST_UNLOCK;
   1165 
   1166     return BCM_E_NONE;
   1167 }
   1168 
   1169 
   1170 /****************************************************************
   1171  *
   1172  * Callback for communication failure registered with
   1173  * atp_timeout_register to catch ATP transmission timeouts.
   1174  */
   1175 
   1176 STATIC void
   1177 st_comm_failure(cpudb_key_t key)
   1178 {
   1179     LOG_WARN(BSL_LS_TKS_STKTASK,
   1180              (BSL_META("STACK: communication timeout to " CPUDB_KEY_FMT_EOLN),
   1181               CPUDB_KEY_DISP(key)));
   1182 
   1183     bcm_st_event_send(BCM_STE_COMM_FAILURE);
   1184 }
   1185 
   1186 /****************************************************************
   1187  *
   1188  * Discovery related code
   1189  */
   1190 
   1191 /* Assumes LOCK held; called before each entry into discovery */
   1192 
   1193 STATIC int
   1194 pre_discovery_prep(void)
   1195 {
   1196     if (bcm_st_disc_db != CPUDB_REF_NULL) { /* Destroy disc db if present */
   1197         CPUDB_TOPO_DESTROY(bcm_st_disc_db);
   1198         bcm_st_disc_db = CPUDB_REF_NULL;
   1199     }
   1200 
   1201     bcm_st_disc_db = cpudb_create();
   1202     if (bcm_st_disc_db == CPUDB_REF_NULL) {
   1203         LOG_ERROR(BSL_LS_TKS_STKTASK,
   1204                   (BSL_META("ST ERR: Error creating DB.\n")));
   1205         return -1;
   1206     }
   1207     st_config.base.dseq_num = st_ev_count;
   1208     if (cpudb_local_base_info_set(bcm_st_disc_db,
   1209                                   (cpudb_base_t *)&st_config.base) < 0) {
   1210         LOG_ERROR(BSL_LS_TKS_STKTASK,
   1211                   (BSL_META("ST ERR: Error creating local entry.\n")));
   1212         CPUDB_TOPO_DESTROY(bcm_st_disc_db);
   1213         bcm_st_disc_db = CPUDB_REF_NULL;
   1214         return -1;
   1215     }
   1216 
   1217     bcm_st_disc_db->old_db = bcm_st_cur_db;
   1218 
   1219     return 0;
   1220 }
   1221 
   1222 /*
   1223  * st_encap_check()
   1224  *
   1225  * Check stack port encapsulation. Note that we can't just check to
   1226  * see if the port configuration says it's an external stack port (via
   1227  * stack_ext), because the port could have changed mode without a link
   1228  * state event.
   1229  *
   1230  * Record the encapsulation found in both the CPU database stack port
   1231  * flags, and stktask stack ports flags, and add or remove the port
   1232  * from the next hop database.
   1233  *
   1234  * Also, set or clear CPUDB_SPF_INACTIVE so the CPU database flags
   1235  * field is still compatible with older software that doesn not
   1236  * support CPUDB_SPF_ETHERNET directly.
   1237  */
   1238 
   1239 STATIC void
   1240 st_encap_check(cpudb_entry_t *local_entry)
   1241 {
   1242     int i;
   1243     cpudb_stk_port_t *stk_ports;
   1244 
   1245     stk_ports = local_entry->sp_info;
   1246 
   1247     /* Check for current link encapsulation mode of stack ports */
   1248     for (i = 0; i < local_entry->base.num_stk_ports; i++) {
   1249         int unit = local_entry->base.stk_ports[i].unit;
   1250         int port = local_entry->base.stk_ports[i].port;
   1251         bcm_port_encap_config_t mode;
   1252         uint32 stk_mode;
   1253 
   1254         sal_memset(&mode, 0, sizeof(mode));
   1255         if (BCM_SUCCESS(bcm_port_encap_config_get(unit, port, &mode)) &&
   1256             BCM_SUCCESS(bcm_stk_mode_get(unit, &stk_mode))) {
   1257             int idx = st_stk_port_find(unit, port, FALSE);
   1258 
   1259             LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1260                         (BSL_META_U(unit,
   1261                         "ST: sp(%d,%d) idx=%d encap=%d stkmode=%d\n"),
   1262                          unit,port,idx,mode.encap, stk_mode));
   1263 
   1264             if (mode.encap != BCM_PORT_ENCAP_IEEE || stk_mode == BCM_STK_SL) {
   1265                 stk_ports[i].flags &= ~(CPUDB_SPF_ETHERNET|CPUDB_SPF_INACTIVE);
   1266                 next_hop_port_add(unit, port, 0);
   1267                 if (idx >= 0) {
   1268                     st_stk_port_flags[idx] &= ~ST_SPF_ETHERNET;
   1269                 }
   1270             } else {
   1271                 stk_ports[i].flags |= (CPUDB_SPF_ETHERNET|CPUDB_SPF_INACTIVE);
   1272                 next_hop_port_remove(unit, port);
   1273                 if (idx >= 0) {
   1274                     st_stk_port_flags[idx] |= ST_SPF_ETHERNET;
   1275                 }
   1276             }
   1277         } else {
   1278             LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1279                         (BSL_META_U(unit,
   1280                         "ST: sp(%d,%d): could not get encap/mode.\n"),
   1281                          unit,port));
   1282         }
   1283     }
   1284 }
   1285 
   1286 STATIC void
   1287 st_link_check(cpudb_entry_t *local_entry)
   1288 {
   1289     int i;
   1290     cpudb_stk_port_t *stk_ports;
   1291 
   1292     stk_ports = local_entry->sp_info;
   1293 
   1294     /* Check for current mode and link states of stack ports */
   1295     for (i = 0; i < local_entry->base.num_stk_ports; i++) {
   1296         int unit = local_entry->base.stk_ports[i].unit;
   1297         int port = local_entry->base.stk_ports[i].port;
   1298 
   1299         if (STK_PORT_LINK_UP(i)) {
   1300             if (!(stk_ports[i].flags & CPUDB_SPF_ETHERNET)) {
   1301                 next_hop_port_add(unit, port, 0);
   1302             }
   1303             stk_ports[i].flags &= ~CPUDB_SPF_NO_LINK;
   1304         } else {
   1305             stk_ports[i].flags |= CPUDB_SPF_NO_LINK;
   1306             next_hop_port_remove(unit, port);
   1307         }
   1308     }
   1309 }
   1310 
   1311 /*
   1312  * Loop through discovery until success, failure or abort.
   1313  * ST_LOCK is held on entry and exit of this function, but released
   1314  * while discovery is running.
   1315  */
   1316 
   1317 STATIC int
   1318 st_disc_loop(void)
   1319 {
   1320     int rv = BCM_E_FAIL;
   1321 
   1322     /* Allocate and/or initialize discovery DB; disable fabric trunking */
   1323     if (pre_discovery_prep() < 0) { /* Error during preparation */
   1324         return BCM_E_FAIL;
   1325     }
   1326 
   1327     do { /* Repeat discovery while it returns "restart" */
   1328         cpudb_clear(bcm_st_disc_db, TRUE);
   1329         /* Get latest link info */
   1330         st_encap_check(bcm_st_disc_db->local_entry); 
   1331         st_link_check(bcm_st_disc_db->local_entry); 
   1332 
   1333         ST_UNLOCK;
   1334         rv = st_config.st_disc_start(bcm_st_disc_db, st_config.st_master);
   1335         ST_LOCK;
   1336 
   1337         ++st_ev_count; /* End of discovery is an event */
   1338         if (rv == DISC_RESTART_NEW_SEQ) { /* Update sequence number */
   1339             bcm_st_disc_db->local_entry->base.dseq_num = st_ev_count;
   1340         }
   1341     } while (((rv == DISC_RESTART_REQUEST) ||
   1342               (rv == DISC_RESTART_NEW_SEQ)) &&
   1343              (!(bcm_st_flags & BCM_STF_ABORT)));
   1344 
   1345     return rv;  /* ST_LOCK held */
   1346 }
   1347 
   1348 #define BREAK_IF_ABORT(_state) if ((_state) & BCM_STF_ABORT) break
   1349 
   1350 /* Thread to provide context to discovery */
   1351 
   1352 STATIC void
   1353 st_disc_thread(void *cookie)
   1354 {
   1355     int rv = BCM_E_NONE;
   1356 
   1357     COMPILER_REFERENCE(cookie);
   1358     /* Synchronize with calling thread */
   1359     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1360                 (BSL_META("ST [T=%u]: Disc thread started, id %d\n"),
   1361                  sal_time_usecs(), PTR_TO_INT(sal_thread_self())));
   1362     /* Signal about to sleep */
   1363     ST_LOCK;
   1364     bcm_st_flags |= BCM_STF_DISC_SLEEPING;
   1365     ST_UNLOCK;
   1366 
   1367     /* NOTE: LOCK is held on exit from this loop */
   1368     while (TRUE) {
   1369         ST_DISC_SLEEP(sal_sem_FOREVER);
   1370 
   1371         ST_LOCK;
   1372         bcm_st_flags &= ~BCM_STF_DISC_SLEEPING;
   1373         BREAK_IF_ABORT(bcm_st_flags);
   1374         if (st_state != BCM_STS_DISC) { /* Bad state */
   1375             bcm_st_flags |= BCM_STF_DISC_SLEEPING;
   1376             ST_UNLOCK;
   1377             LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1378                         (BSL_META("ST: Disc thread awake, but state %s; continuing\n"),
   1379                          bcm_st_state_strings[st_state]));
   1380             continue;
   1381         }
   1382 
   1383         /* disc_loop unlocks ST for discovery; returns locked */
   1384         rv = st_disc_loop();
   1385 
   1386         BREAK_IF_ABORT(bcm_st_flags);
   1387         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1388                     (BSL_META("ST: Disc ends: %d [T=%u]\n"),
   1389                      rv, sal_time_usecs()));
   1390         if (rv == BCM_E_NONE) {
   1391             st_new_events |= BCM_STE_FLAG(BCM_STE_DISC_SUCCESS);
   1392         } else {
   1393             st_new_events |= BCM_STE_FLAG(BCM_STE_DISC_FAILURE);
   1394         }
   1395         ST_WAKE;
   1396         /* Signal about to sleep */
   1397         bcm_st_flags |= BCM_STF_DISC_SLEEPING;
   1398         ST_UNLOCK;
   1399     }
   1400 
   1401     /* LOCK IS HELD HERE */
   1402     st_disc_tid = SAL_THREAD_ERROR;
   1403     ST_UNLOCK;
   1404 
   1405     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1406                 (BSL_META("STACK: Disc thread exiting\n")));
   1407     sal_thread_exit(rv);
   1408 }
   1409 
   1410 /*
   1411  * Function:
   1412  *    st_linkscan_cb_thread_quit
   1413  * Purpose:
   1414  *      This function checks if the linkscan cb thread has gracefully
   1415  *      shutdown itself. This is needed as when the Appl is shutdown the
   1416  *      a global flag is set to indicate it. All stacking threads look for
   1417  *      this flag periodically and shuts itself gracefully by marking the
   1418  *      global variable of its thread ID to be SAL_THREAD_ERROR.
   1419  *      Top level thread should only return back if all the threads it had
   1420  *      spawned gracefully exits.
   1421  * Parameters:
   1422  *
   1423  * Returns:
   1424  *      BCM_E_NONE
   1425  *      BCM_E_FAIL
   1426  *
   1427  * Notes:
   1428  */
   1429 STATIC int
   1430 st_linkscan_cb_thread_quit(int retry_max)
   1431 {
   1432     int rv = BCM_E_NONE;
   1433     int retries = 0;
   1434 
   1435     ST_LOCK;
   1436     bcm_st_flags |= BCM_STF_ABORT;
   1437     ST_UNLOCK;
   1438 
   1439     /*
   1440      * Wake the link scan cb processing thread so that it
   1441      * can see the abort flag and exit itself gracefully.
   1442      */
   1443     ST_LSCAN_WAKE;
   1444 
   1445     sal_thread_yield();
   1446     while (st_lscan_tid != SAL_THREAD_ERROR && (retries++ < retry_max)) {
   1447         sal_usleep(10000);
   1448     }
   1449 
   1450     if (st_lscan_tid != SAL_THREAD_ERROR) {
   1451         LOG_WARN(BSL_LS_TKS_STKTASK,
   1452                  (BSL_META("ST: LinkScan CB thread won't quit\n")));
   1453         rv = BCM_E_FAIL;
   1454     }
   1455     return rv;
   1456 }
   1457 
   1458 
   1459 #define BREAK_IF_NOT_RUNNING_OR_ABORT(_state)   \
   1460     if (!((_state) & BCM_STF_RUNNING) || ((_state) & BCM_STF_ABORT)) break
   1461 /*
   1462  * Function:
   1463  *    st_linkscan_cb_thread
   1464  * Purpose:
   1465  *      This function is the starting point for the linkscan cb processing
   1466  *      thread. It just waits on the linkscan cb list. If it finds an entry
   1467  *      in it grabs it and processes the data.
   1468  *      This function is the sole consumer of the linkscan cb list which makes
   1469  *      it easy to manage the list as there is only one producer for the list.
   1470  *      All through the infinite loop there are checks to see if the Appl
   1471  *      was marked to abort or is now not running. In such case it will break
   1472  *      the loop, mark the thread as INVALID to indicate that the thread is no
   1473  *      longer working. Before it exits, it also marks the linkscan cb list as
   1474  *      not ready as the sole consumer of it is going down.
   1475  * Parameters:
   1476  *      cookie: (IN) User data when the thread was created
   1477  *
   1478  * Returns:
   1479  *
   1480  * Notes:
   1481  */
   1482 STATIC void
   1483 st_linkscan_cb_thread(void *cookie)
   1484 {
   1485     int rv = BCM_E_NONE;
   1486     int unit;
   1487     bcm_port_t port;
   1488     int link_status;
   1489     sal_usecs_t timestamp;
   1490     int idx;
   1491     static const char *ll_status[] = {"down", "up", "failed", "fault"};
   1492 
   1493     COMPILER_REFERENCE(cookie);
   1494     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1495                 (BSL_META("ST [T=%u]: LinkScan CB thread started, id %d\n"),
   1496                  sal_time_usecs(), PTR_TO_INT(sal_thread_self())));
   1497 
   1498     /* NOTE: LOCK is held on exit from this loop */
   1499     while (TRUE) {
   1500         rv = _bcm_st_linkscan_list_pop(&unit, &port, &link_status, &timestamp);
   1501         if(BCM_E_EMPTY == rv) {
   1502             ST_LOCK;
   1503             BREAK_IF_NOT_RUNNING_OR_ABORT(bcm_st_flags);
   1504             ST_UNLOCK;
   1505             ST_LSCAN_SLEEP(sal_sem_FOREVER);
   1506             continue;
   1507         }
   1508 
   1509         ST_LOCK;
   1510         BREAK_IF_NOT_RUNNING_OR_ABORT(bcm_st_flags);
   1511 
   1512         idx = st_stk_port_find(unit, port, FALSE);
   1513         if (idx < 0 || st_stk_port_flags[idx] & ST_SPF_ETHERNET) {
   1514             /* Not a stack port */
   1515             ST_UNLOCK;
   1516             continue;
   1517         }
   1518 
   1519         STK_PORT_LAST_LINK_SET(idx, link_status);
   1520         stk_port_last_event_us[idx] = timestamp;
   1521 
   1522         /* If port was up, send link down now; */
   1523         if ((link_status != BCM_PORT_LINK_STATUS_UP) &&
   1524              STK_PORT_LINK_UP(idx)) {
   1525 
   1526             STK_PORT_CUR_LINK_SET(idx, FALSE);
   1527             /*
   1528              * Rapid Recovery
   1529              *
   1530              * If rapid recovery was successful, just return.
   1531              * Note:  This method will NOT trigger any state
   1532              *        transition for the Stack task.  This approach
   1533              *        causes the minimal impact in the previous Stacking
   1534              *        application.
   1535              */
   1536             if (BCM_SUCCESS(topo_board_rapid_recovery(bcm_st_cur_db,
   1537                                                       unit, port))) {
   1538                 ST_UNLOCK;
   1539                 LOG_INFO(BSL_LS_TKS_STKTASK,
   1540                             (BSL_META_U(unit,
   1541                             "Rapid rcvry ok for (%d,%d)\n"),
   1542                              unit, port));
   1543                 continue;
   1544             }
   1545 
   1546             st_new_events |= BCM_STE_FLAG(BCM_STE_LINK_DOWN);
   1547 
   1548             /* Clear the NH TX source mod ID for the port if the reserved
   1549                modid feature is not enabled. */
   1550             if (!bcm_st_reserved_modid_enable_get()) {
   1551                 if ((rv = nh_tx_src_mod_port_set(unit, port, -1, -1)) < 0) {
   1552                     LOG_WARN(BSL_LS_TKS_STKTASK,
   1553                              (BSL_META_U(unit,
   1554                              "ST: Link down, error clearing mod/port: %s\n"),
   1555                               bcm_errmsg(rv)));
   1556                 }
   1557             }
   1558         }
   1559         /* Check if we have to abort before waking up the event thread */
   1560         BREAK_IF_NOT_RUNNING_OR_ABORT(bcm_st_flags);
   1561 
   1562         /* In either case, wake the thread so it will check for link debounce */
   1563         ST_WAKE;
   1564         ST_UNLOCK;
   1565 
   1566         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1567                     (BSL_META_U(unit,
   1568                     "ST: Link change idx %d, (%d, %d) %s\n"),
   1569                      idx, unit, port,
   1570                      ((link_status >= BCM_PORT_LINK_STATUS_DOWN &&
   1571                        link_status <= BCM_PORT_LINK_STATUS_REMOTE_FAULT) ?
   1572                                ll_status[link_status] : "Invalid")));
   1573     }
   1574 
   1575     /* LOCK IS HELD HERE */
   1576     st_lscan_tid = SAL_THREAD_ERROR;
   1577     ST_UNLOCK;
   1578 
   1579     /* Disable the list enqueuing and deallocate the nodes if any */
   1580     _bcm_st_linkscan_list_disable();
   1581 
   1582     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1583                 (BSL_META("STACK: Link Scan CB processing thread exiting\n")));
   1584     sal_thread_exit(rv);
   1585 }
   1586 #undef BREAK_IF_NOT_RUNNING_OR_ABORT
   1587 #undef BREAK_IF_ABORT
   1588 
   1589 /****************************************************************
   1590  *
   1591  * Main stack task API functions
   1592  */
   1593 
   1594 /* This local variable indicates if any stack link is found up
   1595  * during initial check.  It is only used in st_disc_thread_started
   1596  * to check if a kick-start is needed.
   1597  */
   1598 
   1599 static int st_init_link_up_found = FALSE;
   1600 
   1601 /* Initialize the link state for the given stack port */
   1602 
   1603 STATIC void
   1604 stk_port_link_state_init(int unit, bcm_port_t port, int sp)
   1605 {
   1606     int rv;
   1607     int link;
   1608 
   1609     /* Get this port's current link state */
   1610     ST_LOCK;
   1611     rv = bcm_port_link_status_get(unit, port, &link);
   1612     if (rv < 0) {
   1613         LOG_ERROR(BSL_LS_TKS_STKTASK,
   1614                   (BSL_META_U(unit,
   1615                   "ST: activating stkport %d.%d link status failed: %s\n"),
   1616                    unit, port, bcm_errmsg(rv)));
   1617         link = 0;
   1618     }
   1619 
   1620     STK_PORT_CUR_LINK_SET(sp, FALSE);
   1621     STK_PORT_LAST_LINK_SET(sp, link);
   1622     stk_port_last_event_us[sp] = sal_time_usecs();
   1623 
   1624     if (link) {
   1625         st_init_link_up_found = TRUE;
   1626     }
   1627     ST_UNLOCK;
   1628 }
   1629 
   1630 /* Initialize all stack port link states */
   1631 
   1632 STATIC void
   1633 st_link_init(void)
   1634 {
   1635     int i;
   1636     int unit, port;
   1637     int rv;
   1638 
   1639     /* Register for link changes on stack units */
   1640     if (bcm_st_cfg_flags & BCM_STC_LINK_REGISTER) {
   1641         /* Init the linkscan cb list before the handler is registered */
   1642         _bcm_st_linkscan_list_enable();
   1643 
   1644         for (i = 0; i < st_num_stk_units; i++) {
   1645             rv = bcm_linkscan_register(st_stk_units[i], st_linkscan_handler);
   1646             if (rv < 0) {
   1647                 LOG_ERROR(BSL_LS_TKS_STKTASK,
   1648                           (BSL_META("ST: link register failed: %s\n"),
   1649                            bcm_errmsg(rv)));
   1650             }
   1651         }
   1652     }
   1653 
   1654     /* Check for current link states of stack ports */
   1655     FOREACH_STK_PORT(i) {
   1656         unit = st_config.base.stk_ports[i].unit;
   1657         port = st_config.base.stk_ports[i].port;
   1658         stk_port_link_state_init(unit, port, i);
   1659     }
   1660 }
   1661 
   1662 /* Program the stand alone topology for the board and set up link states */
   1663 
   1664 STATIC int
   1665 local_board_setup(cpudb_key_t key)
   1666 {
   1667     cpudb_ref_t local_ref = NULL;
   1668     int rv;
   1669 
   1670     if ((local_ref = cpudb_create()) == NULL) {
   1671         LOG_WARN(BSL_LS_TKS_STKTASK,
   1672                  (BSL_META("ST: Could not create setup DB\n")));
   1673         return BCM_E_MEMORY;
   1674     }
   1675 
   1676     
   1677     if ((rv = cpudb_local_base_info_set(local_ref, &st_config.base)) < 0) {
   1678         LOG_WARN(BSL_LS_TKS_STKTASK,
   1679                  (BSL_META("ST: Setup could not set base info: %s\n"),
   1680                   bcm_errmsg(rv)));
   1681         return rv;
   1682     }
   1683     
   1684     st_encap_check(local_ref->local_entry); 
   1685 
   1686     if ((rv = topology_mod_ids_assign(local_ref)) < 0) {
   1687         LOG_WARN(BSL_LS_TKS_STKTASK,
   1688                  (BSL_META("ST: Could not setup modids: %s\n"),
   1689                   bcm_errmsg(rv)));
   1690         return rv;
   1691     }
   1692     
   1693     if ((rv = topo_board_setup(local_ref)) < 0) {
   1694         LOG_WARN(BSL_LS_TKS_STKTASK,
   1695                  (BSL_META("ST: Could not setup local topology: %s\n"),
   1696                   bcm_errmsg(rv)));
   1697         return rv;
   1698     }
   1699 
   1700     CPUDB_TOPO_DESTROY(local_ref);
   1701 
   1702     return BCM_E_NONE;
   1703 }
   1704 
   1705 
   1706 STATIC int
   1707 _config_load(bcm_st_config_t *config)
   1708 {
   1709     if (bcm_st_flags & BCM_STF_RUNNING) {
   1710         return BCM_E_BUSY;
   1711     }
   1712 
   1713     /* config may be NULL if already loaded a configuration */
   1714     if (config == NULL) {
   1715         if (!(bcm_st_flags & BCM_STF_CONFIG_LOADED)) {
   1716             return BCM_E_PARAM;
   1717         } else {
   1718             return BCM_E_NONE;
   1719         }
   1720     }
   1721 
   1722     if ((config->st_disc_start == NULL) || (config->st_disc_abort == NULL)) {
   1723         return BCM_E_PARAM;
   1724     }
   1725 
   1726     sal_memcpy((void*)&st_config, config, sizeof(bcm_st_config_t));
   1727     /* Default topo/attach/transition settings */
   1728     if (st_config.st_topo == NULL) {
   1729         st_config.st_topo = bcm_stack_topo_update;
   1730     }
   1731     if (st_config.st_attach == NULL) {
   1732         st_config.st_attach = bcm_stack_attach_update;
   1733     }
   1734     if (st_config.st_transition == NULL) {
   1735         st_config.st_transition = bcm_st_transition;
   1736 
   1737     }
   1738 
   1739     ST_LOCK;
   1740     bcm_st_flags |= BCM_STF_CONFIG_LOADED;
   1741     ST_UNLOCK;
   1742     
   1743     return BCM_E_NONE;
   1744 }
   1745 /*
   1746  * Function:
   1747  *      bcm_st_config_load
   1748  * Purpose:
   1749  *      Initialize stack task without starting it.
   1750  * Parameters:
   1751  *      config     - The configuration structure to use
   1752  * Returns:
   1753  *      BCM_E_XXX
   1754  */
   1755 
   1756 int
   1757 bcm_st_config_load(bcm_st_config_t *config)
   1758 {
   1759     int rv;
   1760 
   1761     if (!ST_INIT_DONE) {
   1762         rv = st_init();
   1763         if (rv < 0) {
   1764             return rv;
   1765         }
   1766     }
   1767 
   1768     ST_LOCK;
   1769     rv = _config_load(config);
   1770     ST_UNLOCK;
   1771 
   1772     return BCM_E_NONE;
   1773 }
   1774 
   1775 /*
   1776  * Wait for disc thread to indicate it's there and sleeping; 2 seconds.
   1777  * See if we need to kick-start due to no links up.
   1778  */
   1779 
   1780 STATIC int
   1781 st_disc_thread_started(void)
   1782 {
   1783     int count = 0;
   1784 
   1785     while (!(bcm_st_flags & (BCM_STF_DISC_SLEEPING|BCM_STF_ABORT))) {
   1786         sal_thread_yield();
   1787         sal_usleep(10000);
   1788         if (++count > 400) {
   1789             LOG_WARN(BSL_LS_TKS_STKTASK,
   1790                      (BSL_META("ST: Discovery thread not alive on startup\n")));
   1791             return BCM_E_TIMEOUT;
   1792         }
   1793     }
   1794 
   1795     /* Check for all links down and in ready state; send event if so */
   1796     if ((st_state == BCM_STS_READY) && !st_init_link_up_found &&
   1797         (!(bcm_st_flags & BCM_STF_ABORT))) {
   1798         bcm_st_event_send(BCM_STE_LINK_DOWN);
   1799     }
   1800 
   1801     return BCM_E_NONE;
   1802 }
   1803 
   1804 /*
   1805  * Function:
   1806  *      bcm_st_start
   1807  * Purpose:
   1808  *      Start the stack manager task
   1809  * Parameters:
   1810  *      config           - Config structure; see stktask.h
   1811  *      enabled          - Start discovery immediately?
   1812  * Returns:
   1813  *      BCM_E_XXX
   1814  * Notes:
   1815  *      Requires a thread context; does not start its own thread.
   1816  *      However, does launch thread for discovery.
   1817  */
   1818 
   1819 int
   1820 bcm_st_start(bcm_st_config_t *config, int enable)
   1821 {
   1822     int rv;
   1823 
   1824     if (!ST_INIT_DONE && ((rv = st_init()) < 0)) {
   1825         return rv;
   1826     }
   1827 
   1828     if (st_disc_tid != SAL_THREAD_ERROR) {
   1829         LOG_WARN(BSL_LS_TKS_STKTASK,
   1830                  (BSL_META("ST: Discovery thread is running\n")));
   1831         return BCM_E_FAIL;
   1832     }
   1833 
   1834     if (!atp_running() && !(bcm_st_cfg_flags & BCM_STC_START_ATP)) {
   1835         LOG_WARN(BSL_LS_TKS_STKTASK,
   1836                  (BSL_META("ST: ATP is not running\n")));
   1837     }
   1838 
   1839     ST_LOCK;
   1840     if ((rv = _config_load(config)) < 0) {
   1841         ST_UNLOCK;
   1842         return rv;
   1843     }
   1844     bcm_st_flags |= BCM_STF_RUNNING;
   1845     local_board_setup(st_config.base.key);
   1846 
   1847     /* Init flags, state and transition time */
   1848     bcm_st_flags &= ~(BCM_STF_ABORT | BCM_STF_DISC_SLEEPING);
   1849 
   1850     if (enable) {
   1851         st_state = BCM_STS_READY;
   1852     } else {
   1853         st_state = BCM_STS_BLOCKED;
   1854     }
   1855     st_transition_time = sal_time_usecs();
   1856 
   1857     /* Generate the list of stack units; setup link and communications */
   1858     rv = stk_units_gen();
   1859     if (BCM_FAILURE(rv)) {
   1860         bcm_st_flags &= ~BCM_STF_RUNNING;
   1861         ST_UNLOCK;   
   1862         return rv;
   1863     }
   1864 
   1865     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1866                 (BSL_META("Stack task started [T=%u]: %s with %d stk unit%s.\n"),
   1867                  st_transition_time, enable ? "ready" : "blocked",
   1868                  st_num_stk_units, st_num_stk_units != 1 ? "s" : ""));
   1869 
   1870     ST_UNLOCK;
   1871     st_link_init();
   1872     st_comm_setup();
   1873     ST_LOCK;
   1874 
   1875     /* Start discovery thread */
   1876     st_disc_tid = sal_thread_create("bcmDISC",
   1877                                     bcm_st_disc_stk_size,
   1878                                     bcm_st_disc_priority,
   1879                                     st_disc_thread,
   1880                                     (void *)NULL);
   1881 
   1882     if (st_disc_tid == SAL_THREAD_ERROR) {
   1883         bcm_st_flags &= ~BCM_STF_RUNNING;
   1884         ST_UNLOCK;
   1885         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1886                     (BSL_META("ST: Could not create discovery thread\n")));
   1887         st_comm_shutdown();
   1888         return BCM_E_FAIL;
   1889     }
   1890 
   1891     ST_UNLOCK;
   1892 
   1893     rv = st_disc_thread_started();
   1894     if (BCM_FAILURE(rv)) {
   1895         ST_LOCK;
   1896         bcm_st_flags &= ~BCM_STF_RUNNING;
   1897         ST_UNLOCK;
   1898         st_comm_shutdown();
   1899         return rv;
   1900     }
   1901 
   1902     /* Start the link scan callback response thread*/
   1903     ST_LOCK;
   1904     st_lscan_tid = sal_thread_create("bcmStkLScan",
   1905                                     SAL_THREAD_STKSZ,
   1906                                     BCM_ST_LSCAN_PRIORITY_DEFAULT,
   1907                                     st_linkscan_cb_thread,
   1908                                     (void *)NULL);
   1909 
   1910     if (st_lscan_tid == SAL_THREAD_ERROR) {
   1911         bcm_st_flags &= ~BCM_STF_RUNNING;
   1912         ST_UNLOCK;
   1913         LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1914                     (BSL_META("ST: Could not create linkscan cb thread\n")));
   1915         st_comm_shutdown();
   1916         /* End discovery thread */
   1917         rv = discovery_quit(BCM_ST_DISC_STOP_RETRIES_MAX);
   1918         return rv;
   1919     }
   1920 
   1921     ST_UNLOCK;
   1922 
   1923     /* Process events until forced to exit */
   1924     st_event_loop();
   1925 
   1926     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   1927                 (BSL_META("ST: Aborting [T=%u]\n"),
   1928                  sal_time_usecs()));
   1929     st_comm_shutdown();
   1930 
   1931     ST_LOCK;
   1932     bcm_st_flags &= ~BCM_STF_RUNNING;
   1933     ST_UNLOCK;
   1934 
   1935     /* End discovery thread */
   1936     rv = discovery_quit(BCM_ST_DISC_STOP_RETRIES_MAX);
   1937 
   1938     if(BCM_SUCCESS(rv)) {
   1939         /* If the discovery thread has exited successfully
   1940          * then retrieve the status of linkscan thread exit
   1941          */
   1942         rv = st_linkscan_cb_thread_quit(BCM_ST_LSCAN_CB_QUIT_RETRIES_MAX);
   1943     } else {
   1944         /* If the discovery thread has not exited successfully then still
   1945          * try to exit the linkscan thread. Do not bother to collect its
   1946          * status as we are going to return failure due to unsuccessfull
   1947          * exit of discovery thread.
   1948          */
   1949         st_linkscan_cb_thread_quit(BCM_ST_LSCAN_CB_QUIT_RETRIES_MAX);
   1950     }
   1951     return rv;
   1952 }
   1953 
   1954 
   1955 /*
   1956  * Function:
   1957  *      bcm_st_stop
   1958  * Purpose:
   1959  *      Stop the stack manager task
   1960  * Parameters:
   1961  *      timeout_us     - How long to wait for exit before returning error
   1962  * Returns:
   1963  *      BCM_E_FAIL if task does not appear stopped after time out.
   1964  */
   1965 
   1966 int
   1967 bcm_st_stop(int timeout_us)
   1968 {
   1969     int retries;
   1970     int i;
   1971 
   1972     if (!(bcm_st_flags & BCM_STF_RUNNING)) {
   1973         return BCM_E_NONE;
   1974     }
   1975 
   1976     ST_LOCK;
   1977     bcm_st_flags |= BCM_STF_ABORT;
   1978     ST_UNLOCK;
   1979 
   1980     ST_WAKE;
   1981     sal_thread_yield();
   1982     if (timeout_us > 0) {
   1983         retries = timeout_us / 10000 + 1;
   1984         for (i = 0; i < retries; i++) {
   1985             if (!(bcm_st_flags & BCM_STF_RUNNING)) {
   1986                 break;
   1987             }
   1988             sal_usleep(10000);
   1989         }
   1990     }
   1991 
   1992     return (bcm_st_flags & BCM_STF_RUNNING) ? BCM_E_FAIL : BCM_E_NONE;
   1993 }
   1994 
   1995 /*
   1996  * Function:
   1997  *      bcm_st_base_update
   1998  * Purpose:
   1999  *      Update the CPUDB base information for the system
   2000  * Parameters:
   2001  *      base      - Pointer to structure w/ info to update
   2002  *      restart   - Should restart event be sent
   2003  *                  NOTE: This parameter is ignored. Discovery
   2004  *                  will restart if a stack port is either
   2005  *                  already up, or transitions from down to up
   2006  *                  through the normal stack port link state checking.
   2007  * Returns:
   2008  *      BCM_E_XXX
   2009  * Notes:
   2010  *      Copies the information to a local copy.  Checks if discovery
   2011  *      is currently under way in which case it returns _E_BUSY.
   2012  */
   2013 
   2014 int
   2015 bcm_st_base_update(cpudb_base_t *base, int restart)
   2016 {
   2017     int rv = BCM_E_NONE;
   2018     int i, unit;
   2019     bcm_port_t port;
   2020 
   2021     if (st_lock == NULL) {
   2022         return BCM_E_INIT;
   2023     }
   2024 
   2025     ST_LOCK;
   2026     sal_memcpy((void *)&st_config.base, base, sizeof(*base));
   2027 
   2028     /* Check for current link states of stack ports from base config */
   2029     FOREACH_STK_PORT(i) {
   2030         unit = st_config.base.stk_ports[i].unit;
   2031         port = st_config.base.stk_ports[i].port;
   2032         stk_port_link_state_init(unit, port, i);
   2033     }
   2034 
   2035     if (bcm_st_disc_db) {
   2036         bcm_st_disc_db->local_entry->base.master_pri = st_config.base.master_pri;
   2037     }
   2038     if (bcm_st_cur_db) {
   2039         bcm_st_cur_db->local_entry->base.master_pri = st_config.base.master_pri;
   2040     }
   2041     
   2042     ST_UNLOCK;
   2043 
   2044     COMPILER_REFERENCE(restart);
   2045 
   2046     return rv;
   2047 }
   2048 
   2049 
   2050 /*
   2051  * Function:
   2052  *      bcm_st_event_send
   2053  * Purpose:
   2054  *      Send an event to the stack task
   2055  * Parameters:
   2056  *      event        - Event type.  See stktask.h
   2057  * Returns:
   2058  *      BCM_E_XXX
   2059  * Notes:
   2060  *      Although any event can be sent through this routine,
   2061  *      it should not be used for link events or discovery packet
   2062  *      events.
   2063  */
   2064 
   2065 int
   2066 bcm_st_event_send(bcm_st_event_t event)
   2067 {
   2068     if (!ST_INIT_DONE) {
   2069         return BCM_E_INIT;
   2070     }
   2071 
   2072     if (!BCM_STE_VALID(event)) {
   2073         return BCM_E_PARAM;
   2074     }
   2075 
   2076     ST_LOCK;
   2077     st_new_events |= BCM_STE_FLAG(event);
   2078     ST_UNLOCK;
   2079     ST_WAKE;
   2080 
   2081     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   2082                 (BSL_META("ST: sending event %s [T=%u]\n"),
   2083                  bcm_st_event_strings[event],
   2084                  sal_time_usecs()));
   2085 
   2086     return BCM_E_NONE;
   2087 }
   2088 
   2089 /****************************************************************
   2090  *
   2091  * Configuration API
   2092  */
   2093 
   2094 /*
   2095  * Function:
   2096  *      bcm_st_timeout_set/get
   2097  * Purpose:
   2098  *      Access the timeout associated with a state
   2099  * Parameters:
   2100  *      state     - Which timeout to update
   2101  *      to        - Timeout in microseconds
   2102  * Returns:
   2103  *      BCM_E_XXX
   2104  * Notes:
   2105  *      A timeout of 0 disables the timer for that state.
   2106  */
   2107 
   2108 int
   2109 bcm_st_timeout_set(bcm_st_state_t state, sal_usecs_t to)
   2110 {
   2111     int rv = BCM_E_PARAM;
   2112 
   2113     if (BCM_STS_VALID(state)) {
   2114         state_timeouts[state] = to;
   2115         rv = BCM_E_NONE;
   2116     }
   2117 
   2118     return rv;
   2119 }
   2120 
   2121 int
   2122 bcm_st_timeout_get(bcm_st_state_t state, sal_usecs_t *to)
   2123 {
   2124     int rv = BCM_E_PARAM;
   2125 
   2126     if (BCM_STS_VALID(state)) {
   2127         if (to != NULL) {
   2128             *to = state_timeouts[state];
   2129             rv = BCM_E_NONE;
   2130         }
   2131     }
   2132 
   2133     return rv;
   2134 }
   2135 
   2136 /*
   2137  * Function:
   2138  *      bcm_st_stk_port_enable_set/get
   2139  * Purpose:
   2140  *      Set/get status of stack ports
   2141  * Parameters:
   2142  *      unit.port     - Stack port indication
   2143  *      enable        - (OUT for get) is port enabled
   2144  * Returns:
   2145  *      BCM_E_XXX
   2146  * Notes:
   2147  *      By default, all stack ports are enabled when config is loaded.
   2148  */
   2149 
   2150 int
   2151 bcm_st_stk_port_enable_set(int unit, int port, int enable)
   2152 {
   2153     int sp;
   2154     int rv = BCM_E_NOT_FOUND;
   2155 
   2156     ST_LOCK;
   2157     sp = st_stk_port_find(unit, port, TRUE);
   2158     if (sp >= 0) {
   2159         if (enable) {
   2160             st_stk_port_flags[sp] &= ~ST_SPF_DISABLED;
   2161             stk_port_link_state_init(unit, port, sp);
   2162         } else { /* Mark port as disabled */
   2163             st_stk_port_flags[sp] |= ST_SPF_DISABLED;
   2164         }
   2165         rv = BCM_E_NONE;
   2166     }
   2167     ST_UNLOCK;
   2168     return rv;
   2169 }
   2170 
   2171 int
   2172 bcm_st_stk_port_enable_get(int unit, int port, int *enable)
   2173 {
   2174     int sp;
   2175 
   2176     if (enable == NULL) {
   2177         return BCM_E_PARAM;
   2178     }
   2179 
   2180     sp = st_stk_port_find(unit, port, TRUE);
   2181     if (sp < 0) {
   2182         return BCM_E_NOT_FOUND;
   2183     }
   2184 
   2185     if (STK_PORT_DISABLED(sp)) {
   2186         *enable = FALSE;
   2187     } else {
   2188         *enable = TRUE;
   2189     }
   2190 
   2191     return BCM_E_NONE;
   2192 }
   2193 
   2194 
   2195 int
   2196 bcm_st_reserved_modid_enable_set(int value)
   2197 {
   2198     reserved_modid_enable = value;
   2199 
   2200     return BCM_E_NONE;
   2201 }
   2202 
   2203 int
   2204 bcm_st_reserved_modid_enable_get(void)
   2205 {
   2206     return reserved_modid_enable;
   2207 }
   2208 
   2209 
   2210 /****************************************************************
   2211  *
   2212  * Default stack task transition function
   2213  */
   2214 
   2215 /*
   2216  * Stack task event transition handler (default version)
   2217  *
   2218  * This callback occurs as the last step before changing the st_state
   2219  * variable on a state transition.  It may return an error (this
   2220  * version doesn't) which will force the state to blocked (without
   2221  * an additional callback).
   2222  *
   2223  * The application may override this function by specifying
   2224  * st_config.st_transition.
   2225  */
   2226 
   2227 int
   2228 bcm_st_transition(bcm_st_state_t from,
   2229                   bcm_st_event_t event,
   2230                   bcm_st_state_t to,
   2231                   cpudb_ref_t disc_db,
   2232                   cpudb_ref_t cur_db)
   2233 {
   2234     if (event == BCM_STE_TIMEOUT && from != BCM_STS_BLOCKED) {
   2235         LOG_WARN(BSL_LS_TKS_STKTASK,
   2236                  (BSL_META("TKS ST TIMEOUT in state %s\n"),
   2237                   bcm_st_state_strings[from]));
   2238     }
   2239     LOG_VERBOSE(BSL_LS_TKS_STKTASK,
   2240                 (BSL_META("TKS ST transition: disc_db %p. cur_db %p\n"),
   2241                  disc_db, cur_db));
   2242 
   2243     /* Always clear topo expect moving out of TOPO...unless into DISC too */
   2244     if (from == BCM_STS_TOPO) {
   2245         topo_pkt_expect_set(FALSE);
   2246     }
   2247 
   2248     switch (to) {
   2249     case BCM_STS_DISC:
   2250         topo_pkt_expect_set(TRUE);
   2251         break;
   2252     case BCM_STS_BLOCKED:
   2253         topo_pkt_expect_set(FALSE);
   2254         if (bcm_st_cfg_flags & BCM_STC_AUTO_B2R) {
   2255             /* Auto transition unblocked from blocked */
   2256             bcm_st_event_send(BCM_STE_UNBLOCK);
   2257         }
   2258         break;
   2259     case BCM_STS_TOPO:
   2260         /*
   2261          * During this state, the discovery database contains the most
   2262          * update list of known CPUs.  However, information on all
   2263          * CPUs are at its initial state.
   2264          *
   2265          * In order to reduce the amount of disruption while the
   2266          * stack task completes the discovery/topology cycle,
   2267          * previous information on still existing CPUs is retrieved
   2268          * and used during the CPU update.
   2269          */
   2270         if (disc_db != NULL) {
   2271             cpudb_ref_t    transition_db;
   2272             cpudb_entry_t  *entry, *cur_entry;
   2273         
   2274             transition_db = cpudb_copy(disc_db);
   2275             if (cur_db != NULL) {
   2276                 CPUDB_FOREACH_ENTRY(transition_db, entry) {
   2277                     /* Replace previous information on existing entries */
   2278                     CPUDB_KEY_SEARCH(cur_db, entry->base.key, cur_entry);
   2279                     if (cur_entry != NULL) {
   2280                         cpudb_entry_copy(entry, cur_entry);
   2281                     }
   2282                 }
   2283             }
   2284             atp_db_update(transition_db);
   2285             cpudb_destroy(transition_db);
   2286         }
   2287         break;
   2288     case BCM_STS_ATTACH:
   2289         atp_db_update(disc_db);
   2290         break;
   2291     default:
   2292         break;
   2293     }
   2294 
   2295     return BCM_E_NONE;
   2296 }
   2297 
   2298 /*
   2299  * Function:
   2300  *      bcm_st_current_db_get
   2301  * Purpose:
   2302  *      Get a copy of the stktask current CPU database
   2303  * Parameters:
   2304  *      none
   2305  * Returns:
   2306  *      CPU database
   2307  */
   2308 
   2309 cpudb_ref_t bcm_st_current_db_get(void)
   2310 {
   2311     cpudb_ref_t dbref = NULL;
   2312 
   2313     if (ST_INIT_DONE) {
   2314         ST_LOCK;
   2315         dbref = cpudb_copy(bcm_st_cur_db);
   2316         ST_UNLOCK;
   2317     }
   2318 
   2319     return dbref;
   2320 }
   2321 
   2322 /*
   2323  * Function:
   2324  *      bcm_st_discovery_db_get
   2325  * Purpose:
   2326  *      Get a copy of the stktask discovery CPU database
   2327  * Parameters:
   2328  *      none
   2329  * Returns:
   2330  *      CPU database
   2331  */
   2332 
   2333 cpudb_ref_t bcm_st_discovery_db_get(void)
   2334 {
   2335     cpudb_ref_t dbref = NULL;
   2336 
   2337     if (ST_INIT_DONE) {
   2338         ST_LOCK;
   2339         dbref = cpudb_copy(bcm_st_disc_db);
   2340         ST_UNLOCK;
   2341     }
   2342     
   2343     return dbref;
   2344 }
   2345 
   2346 /*
   2347  * Function:
   2348  *     __bcm_st_linkscan_list_clean
   2349  * Purpose:
   2350  *      Iterate through all the nodes in the list and deallocate
   2351  *      the memory one by one. When the function returns the link
   2352  *      scan cb list should be empty.
   2353  * Parameters:
   2354  *
   2355  * Returns:
   2356  *
   2357  * Notes:
   2358  *      This function assumes that the caller would acquire the mutex
   2359  *      before calling it and will release the mutex after the call.
   2360  *      Hence no synchronization is taken care within it.
   2361  */
   2362 STATIC
   2363 void __bcm_st_linkscan_list_clean(void) {
   2364     bcm_st_lscan_rsp_node_t *node = NULL;
   2365     /* Iterate through all the nodes and free its memnory*/
   2366     while (st_linkscan_list.head) {
   2367         node = st_linkscan_list.head;
   2368         st_linkscan_list.head = st_linkscan_list.head->nxt;
   2369         sal_free(node);
   2370     }
   2371     /* Mark the tail pointer to NULL as the list is empty now */
   2372     st_linkscan_list.tail = NULL;
   2373 }
   2374 
   2375 /*
   2376  * Function:
   2377  *     _bcm_st_linkscan_list_enable
   2378  * Purpose:
   2379  *      This function restores the linkscan cb list to its original
   2380  *      status i.e. empty list. At first it marks the list as not ready
   2381  *      to inform other threads that the list can not be used currently.
   2382  *      Then it empties the list gracefully and then mark it ready to be
   2383  *      used.
   2384  * Parameters:
   2385  *
   2386  * Returns:
   2387  *
   2388  * Notes:
   2389  *      This function does acquire and release the linkscan mutex.
   2390  *      Hence none of the other threads can get access to the list while
   2391  *      its being prepared for usage.
   2392  */
   2393 STATIC
   2394 void _bcm_st_linkscan_list_enable(void) {
   2395 
   2396     ST_LSCAN_LOCK;
   2397     /* Mark the list as not usable */
   2398     st_linkscan_list.ready = 0;
   2399     /* Delete the list gracefully */
   2400     __bcm_st_linkscan_list_clean();
   2401     /* Mark the list as ready to be used */
   2402     st_linkscan_list.ready = 1;
   2403     ST_LSCAN_UNLOCK;
   2404 }
   2405 
   2406 /*
   2407  * Function:
   2408  *     _bcm_st_linkscan_list_disable
   2409  * Purpose:
   2410  *      This function restores the linkscan cb list to its original
   2411  *      status i.e. empty list and marks it non-ready so that producer
   2412  *      for the link list would not be able to enqueue nodes to it.
   2413  * Parameters:
   2414  *
   2415  * Returns:
   2416  *
   2417  * Notes:
   2418  *      This function does acquire and release the linkscan mutex.
   2419  *      Hence none of the other threads can get access to the list while
   2420  *      its being purged.
   2421  *      The user of the linkscan cb list should check for the ready field
   2422  *      before any operation can be performed on it.
   2423  */
   2424 STATIC
   2425 void _bcm_st_linkscan_list_disable(void) {
   2426     ST_LSCAN_LOCK;
   2427     /* Mark the list as not usable */
   2428     st_linkscan_list.ready = 0;
   2429     /* Delete the list gracefully */
   2430     __bcm_st_linkscan_list_clean();
   2431     ST_LSCAN_UNLOCK;
   2432 }
   2433 
   2434 /*
   2435  * Function:
   2436  *     _bcm_st_linkscan_list_push
   2437  * Purpose:
   2438  *      This function creates a node with the user given data and inserts
   2439  *      it at the end of the list. If the list is marked as non-ready
   2440  *      then it skips insertion and returns with SUCCESS status. Hence the
   2441  *      caller can not know if the list enqueue  happened ot not.
   2442  * Parameters:
   2443  *      unit            - (IN) Device Unit #
   2444  *      port            - (IN) BCM local port #
   2445  *      link_status     - (IN) Link status from the linkscan thread.
   2446  *      snapshot_time   - (IN) Time in usecs when the link_status was recorded
   2447  *
   2448  * Returns:
   2449  *      BCM_E_MEMORY
   2450  *      BCM_E_NONE
   2451  *
   2452  * Notes:
   2453  *      Caller is returned with SUCCESS status when the list is not ready
   2454  *      to be used although the data was not pushed to it. This behavior
   2455  *      is by design as there is only one producer at this time - the
   2456  *      linkscan cb handler. The cb handler should not delay the return
   2457  *      to the linkscan thread and further if the list is not ready that
   2458  *      would mean that the stk task has been stopped and there is no one
   2459  *      to process the list contents.
   2460  */
   2461 STATIC
   2462 int _bcm_st_linkscan_list_push(
   2463     int unit, bcm_port_t port, int link_status, sal_usecs_t snapshot_time) {
   2464 
   2465     uint8 node_pushed = 0;
   2466     bcm_st_lscan_rsp_node_t *rsp_node = NULL;
   2467 
   2468     /* Allocate memory for the node */
   2469     rsp_node = (bcm_st_lscan_rsp_node_t *)
   2470         sal_alloc(sizeof(bcm_st_lscan_rsp_node_t), "CB_Node");
   2471 
   2472     if (NULL == rsp_node) {
   2473         return BCM_E_MEMORY;
   2474     }
   2475 
   2476     /* Populate the node with the data */
   2477     rsp_node->unit = unit;
   2478     rsp_node->port = port;
   2479     rsp_node->link_evt_time = snapshot_time;
   2480     rsp_node->link_status = link_status;
   2481     rsp_node->nxt = NULL;
   2482 
   2483     ST_LSCAN_LOCK;
   2484     /* Skip the insertion if the list is not ready */
   2485     if (st_linkscan_list.ready) {
   2486         /* Insert the node at the end of the list */
   2487         if (st_linkscan_list.tail) {
   2488             st_linkscan_list.tail->nxt = rsp_node;
   2489         } else {
   2490             /* If this is the 1st node then head pointer should point to it */
   2491             st_linkscan_list.head = rsp_node;
   2492         }
   2493         /* Update the tail pointer to point to the last node */
   2494         st_linkscan_list.tail = rsp_node;
   2495         node_pushed = 1;
   2496     }
   2497     ST_LSCAN_UNLOCK;
   2498 
   2499     /* If the node could not be inserted then release it to avoid mem leak */
   2500     if (0 == node_pushed) {
   2501         sal_free(rsp_node);
   2502     }
   2503 
   2504     return BCM_E_NONE;
   2505 }
   2506 
   2507 /*
   2508  * Function:
   2509  *     _bcm_st_linkscan_list_pop
   2510  * Purpose:
   2511  *      This function retrieves a node and fills in the user given buffers.
   2512  *      It deletes the node from the list. The Node retrieved is the first
   2513  *      Node pointed at by the Head pointer.
   2514  * Parameters:
   2515  *      unit            - (OUT) Device Unit #
   2516  *      port            - (OUT) BCM local port #
   2517  *      link_status     - (OUT) Link status from the linkscan thread.
   2518  *      snapshot_time   - (OUT) Time in usecs when the link_status was recorded
   2519  *
   2520  * Returns:
   2521  *      BCM_E_EMPTY
   2522  *      BCM_E_NONE
   2523  *
   2524  * Notes:
   2525  *      The function assumes that the given buffers are valid addresses.
   2526  *      The values returned in the given buffers would make sense only
   2527  */
   2528 STATIC
   2529 int _bcm_st_linkscan_list_pop(
   2530     int *unit, bcm_port_t *port, int *link_status, sal_usecs_t *snapshot_time) {
   2531 
   2532     bcm_st_lscan_rsp_node_t *rsp_node;
   2533 
   2534     ST_LSCAN_LOCK;
   2535     /* Retrieve the first node from the list*/
   2536     rsp_node = st_linkscan_list.head;
   2537     /* Since it is a pop operation delink the node from the list */
   2538     if (st_linkscan_list.head) {
   2539         if (NULL == st_linkscan_list.head->nxt) {
   2540             /* If the list had only one Node then Tail should point to NULL */
   2541             st_linkscan_list.tail = st_linkscan_list.head->nxt;
   2542         }
   2543         /*
   2544          * Move the Head to the next node. If list had only
   2545          * one Node then now The HEAD would point to NULL too.
   2546          */
   2547         st_linkscan_list.head = st_linkscan_list.head->nxt;
   2548     }
   2549     ST_LSCAN_UNLOCK;
   2550 
   2551     if (rsp_node) {
   2552         /* If the Node was pop'd then fill in the user given parameters */
   2553         *unit = rsp_node->unit;
   2554         *port = rsp_node->port;
   2555         *link_status = rsp_node->link_status;
   2556         *snapshot_time = rsp_node->link_evt_time;
   2557 
   2558         /* Deallocate the memory for the node */
   2559         sal_free(rsp_node);
   2560         return BCM_E_NONE;
   2561     } else {
   2562         /* List was empty to begin with */
   2563         return BCM_E_EMPTY;
   2564     }
   2565 }