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, ×tamp); 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 }