l2x.c (64810B)
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 */ 8 9 #include <shared/bsl.h> 10 11 #include <sal/core/libc.h> 12 #include <sal/types.h> 13 #include <shared/bsl.h> 14 #include <soc/drv.h> 15 #include <soc/l2x.h> 16 #include <soc/ptable.h> 17 #include <soc/debug.h> 18 #include <soc/util.h> 19 #include <soc/mem.h> 20 #include <soc/iproc.h> 21 #include <soc/mcm/intr_iproc.h> 22 #include <soc/tomahawk3.h> 23 24 #if defined(BCM_TOMAHAWK3_SUPPORT) 25 #ifdef BCM_XGS_SWITCH_SUPPORT 26 27 /* Size of AVL table used for learning entries */ 28 #define _SOC_TH3_L2_LRN_TBL_SIZE 8192 29 30 #define SOC_MEM_COMPARE_RETURN(a, b) { \ 31 if ((a) < (b)) { return -1; } \ 32 if ((a) > (b)) { return 1; } \ 33 } 34 35 typedef struct soc_l2_lrn_avl_info_s { 36 vlan_id_t vlan; 37 soc_module_t mod; 38 int dest_type; /* 0=dest. is port, 1=dest. is trunk */ 39 int port_tgid; /* Holds port num if dest_type is 0. 40 Holds TGID if dest_type is 1 */ 41 sal_mac_addr_t mac; 42 int in_hw; /* Entry programmed in h/w. Used to avoid hits due 43 to duplicate pkts */ 44 } soc_l2_lrn_avl_info_t, *soc_l2_lrn_avl_info_p; 45 46 static int _soc_th3_l2_bulk_age_iter[SOC_MAX_NUM_DEVICES] = {0}; 47 48 static uint8 rev_id = 0; 49 static uint16 dev_id = 0; 50 51 /* 52 * Function: 53 * soc_th3_l2x_shadow_callback 54 * Purpose: 55 * Internal callback routine for updating an AVL tree shadow table 56 * Parameters: 57 * unit - StrataSwitch unit number. 58 * entry_del - Entry to be deleted or updated, NULL if none. 59 * entry_add - Entry to be inserted or updated, NULL if none. 60 * fn_data - unused. 61 * Notes: 62 * Used only if L2X shadow table is enabled. 63 */ 64 65 STATIC void 66 soc_th3_l2x_shadow_callback(int unit, 67 int flags, 68 l2x_entry_t *entry_del, 69 l2x_entry_t *entry_add, 70 void *fn_data) 71 { 72 soc_control_t *soc = SOC_CONTROL(unit); 73 74 if (flags & (SOC_L2X_ENTRY_DUMMY | SOC_L2X_ENTRY_NO_ACTION | 75 SOC_L2X_ENTRY_OVERFLOW)) { 76 return; 77 } 78 79 /* Since sync thread (bcmL2X) updates both its own database and learn 80 * shadow database, we make sure both threads are running, and are synced 81 * together 82 */ 83 if ((soc->l2x_pid != SAL_THREAD_ERROR) && 84 (soc->arlShadowMutex != NULL) && 85 (soc->arlShadow != NULL) && 86 (soc->l2x_learn_pid != SAL_THREAD_ERROR) && 87 (soc->l2x_lrn_shadow_mutex != NULL)) { 88 int rv; 89 90 sal_mutex_take(soc->arlShadowMutex, sal_mutex_FOREVER); 91 92 if (entry_del != NULL) { 93 rv = shr_avl_delete(soc->arlShadow, soc_l2x_entry_compare_key, 94 (shr_avl_datum_t *)entry_del); 95 if (rv == 0) { 96 sal_mac_addr_t mac; 97 vlan_id_t vlan; 98 int dest; 99 100 soc_mem_mac_addr_get(unit, L2Xm, entry_del, MAC_ADDRf, mac); 101 vlan = soc_mem_field32_get(unit, L2Xm, entry_del, VLAN_IDf); 102 dest = soc_mem_field32_get(unit, L2Xm, entry_del, DESTINATIONf); 103 104 LOG_INFO(BSL_LS_SOC_L2, 105 (BSL_META_U(unit, 106 "AVL delete: datum not found:\n dest %d, vlan %d," 107 " mac(hex) %02X:%02X:%02X:%02X:%02X:%02X\n"), dest, vlan, 108 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])); 109 } 110 } 111 112 if (entry_add != NULL) { 113 shr_avl_insert(soc->arlShadow, soc_l2x_entry_compare_key, 114 (shr_avl_datum_t *)entry_add); 115 } 116 117 sal_mutex_give(soc->arlShadowMutex); 118 119 /* Update shadow learn table after successful h/w update(s) */ 120 /* Note the order of delete and insert below is important, since for 121 * station move condition in _soc_th3_learn_cache_entry_process, we 122 * delete an entry first and then insert it with the new port info 123 */ 124 sal_mutex_take(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex, 125 sal_mutex_FOREVER); 126 if (entry_del != NULL) { 127 soc_th3_lrn_shadow_delete(unit, entry_del); 128 } 129 130 if (entry_add != NULL) { 131 soc_th3_lrn_shadow_insert(unit, entry_add); 132 } 133 sal_mutex_give(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex); 134 } 135 } 136 137 /* 138 * Function: 139 * soc_th3_l2x_detach 140 * Purpose: 141 * Deallocate L2X subsystem resources 142 * Parameters: 143 * unit - StrataSwitch unit number. 144 * Returns: 145 * SOC_E_XXX 146 * Notes: 147 * Learn cache interrupt is disabled. Learn cache is cleared, learn cache 148 * status bits are cleared 149 */ 150 int 151 soc_th3_l2x_detach(int unit) 152 { 153 soc_control_t *soc = SOC_CONTROL(unit); 154 155 156 157 /* Free reources allocated for shadow table */ 158 159 soc_l2x_unregister(unit, soc_th3_l2x_shadow_callback, NULL); 160 161 /* Free cml_freeze structure */ 162 _soc_l2x_cml_struct_free(unit); 163 164 if (soc->arlShadow != NULL) { 165 shr_avl_destroy(soc->arlShadow); 166 soc->arlShadow = NULL; 167 } 168 169 if (soc->arlShadowMutex != NULL) { 170 sal_mutex_destroy(soc->arlShadowMutex); 171 soc->arlShadowMutex = NULL; 172 } 173 174 175 176 return SOC_E_NONE; 177 } 178 179 /* 180 * Function: 181 * soc_th3_l2x_attach 182 * Purpose: 183 * Allocate L2X subsystem resources 184 * Parameters: 185 * unit - StrataSwitch unit number. 186 * Returns: 187 * SOC_E_XXX 188 * Notes: 189 * The L2X tree shadow table is always allocated, since it will be used in 190 * learning, aging and table management. Value of spn_L2XMSG_AVL will be 191 * ignored 192 */ 193 194 int 195 soc_th3_l2x_attach(int unit) 196 { 197 soc_control_t *soc = SOC_CONTROL(unit); 198 int datum_bytes, datum_max; 199 200 (void)soc_th3_l2x_detach(unit); 201 202 datum_bytes = sizeof (l2x_entry_t); 203 datum_max = soc_mem_index_count(unit, L2Xm); 204 205 if (shr_avl_create(&soc->arlShadow, 206 INT_TO_PTR(unit), 207 datum_bytes, 208 datum_max) < 0) { 209 return SOC_E_MEMORY; 210 } 211 212 if ((soc->arlShadowMutex = sal_mutex_create("asMutex")) == NULL) { 213 (void)soc_l2x_detach(unit); 214 return SOC_E_MEMORY; 215 } 216 217 soc_l2x_register(unit, soc_th3_l2x_shadow_callback, NULL); 218 219 /* Reset l2 freeze structure */ 220 soc_th3_l2x_reset_freeze_state(unit); 221 222 /* Allocate cml freeze structure */ 223 SOC_IF_ERROR_RETURN(_soc_l2x_cml_struct_alloc(unit)); 224 225 return SOC_E_NONE; 226 } 227 228 /* 229 * Function: 230 * _soc_th3_l2_age_entries_process 231 * Purpose: 232 * This function is invoked as part of aging mechanism to check for hit bits 233 * and take appropriate action (either clear the hit bits, or delete the entry) 234 * Since there is no h/w aging support, nor do we have bulk operations block in 235 * hardware's L2 implementation, this function reads L2X entries, and takes 236 * decision one entry at a time 237 * Parameters: 238 * unit - unit number 239 * Returns: 240 * SOC_E_NONE on success, or other SOC_E_* error code on failure 241 * Notes: 242 * This function will execute much slower than other devices that have 243 * hardware support for aging. Entries are processed one at a time 244 */ 245 STATIC int 246 _soc_th3_l2_age_entries_process(int unit, l2x_entry_t *l2x_entries) 247 { 248 uint32 index_min, index_max, count; 249 int i; 250 int rv; 251 252 index_min = soc_mem_index_min(unit, L2Xm); 253 index_max = soc_mem_index_max(unit, L2Xm); 254 count = soc_mem_index_count(unit, L2Xm); 255 256 sal_memset((void *)l2x_entries, 0, sizeof(l2x_entry_t) * count); 257 258 /* Read L2 table */ 259 soc_mem_lock(unit, L2Xm); 260 rv = soc_mem_read_range(unit, L2Xm, MEM_BLOCK_ANY, 261 index_min, index_max, l2x_entries); 262 soc_mem_unlock(unit, L2Xm); 263 264 if (SOC_FAILURE(rv)) { 265 LOG_ERROR(BSL_LS_SOC_L2, 266 (BSL_META_U(unit, 267 "%s:DMA read failed: %s\n"), __FUNCTION__, soc_errmsg(rv))); 268 /* We do not return error, otherwise thread will be killed. If thread 269 * is alive it can be debugged 270 */ 271 return SOC_E_NONE; 272 } 273 274 for (i = index_min; i <= index_max; i++) { 275 l2x_entry_t *l2x_entry; 276 uint32 hit_da, hit_sa, local_sa; 277 278 l2x_entry = soc_mem_table_idx_to_pointer(unit, L2Xm, l2x_entry_t*, 279 l2x_entries, i); 280 281 /* Skip invalid entry */ 282 if (!soc_L2Xm_field32_get(unit, l2x_entry, BASE_VALIDf)) { 283 continue; 284 } 285 286 /* Skip static entry */ 287 if (soc_L2Xm_field32_get(unit, l2x_entry, STATIC_BITf)) { 288 continue; 289 } 290 291 hit_da = soc_L2Xm_field32_get(unit, l2x_entry, HITDAf); 292 hit_sa = soc_L2Xm_field32_get(unit, l2x_entry, HITSAf); 293 local_sa = soc_L2Xm_field32_get(unit, l2x_entry, LOCAL_SAf); 294 295 /* If no hot bits are set, delete the entry */ 296 if (!(hit_da || hit_sa || local_sa)) { 297 /* Delete entry */ 298 soc_mem_lock(unit, L2Xm); 299 rv = soc_mem_delete(unit, L2Xm, MEM_BLOCK_ALL, 300 (void *)l2x_entry); 301 soc_mem_unlock(unit, L2Xm); 302 if (SOC_FAILURE(rv)) { 303 /* If entry is not found, it has been deleted by other source, 304 * e.g. address delete from application. So we ignore not found 305 * condition here 306 */ 307 if (rv != SOC_E_NOT_FOUND) { 308 LOG_WARN(BSL_LS_SOC_L2, 309 (BSL_META_U(unit, 310 "%s:soc mem delete failed: %s\n"), __FUNCTION__, 311 soc_errmsg(rv))); 312 } 313 } 314 } else { 315 /* Clear hit bits */ 316 soc_L2Xm_field32_set(unit, l2x_entry, HITDAf, 0); 317 soc_L2Xm_field32_set(unit, l2x_entry, HITSAf, 0); 318 soc_L2Xm_field32_set(unit, l2x_entry, LOCAL_SAf, 0); 319 320 soc_mem_lock(unit, L2Xm); 321 rv = soc_mem_write(unit, L2Xm, MEM_BLOCK_ALL, i, (void *)l2x_entry); 322 soc_mem_unlock(unit, L2Xm); 323 if (SOC_FAILURE(rv)) { 324 /* We do not return error, otherwise thread will be killed. If 325 * thread is alive it can be debugged 326 */ 327 LOG_WARN(BSL_LS_SOC_L2, 328 (BSL_META_U(unit, 329 "%s:soc mem write failed: %s\n"), __FUNCTION__, 330 soc_errmsg(rv))); 331 } 332 } 333 334 /* When an entry is deleted, soc_th3_l2x_shadow_callback will call 335 * learn shadow upadate function. So we don't call learn shadow update 336 * function here 337 */ 338 } 339 340 return SOC_E_NONE; 341 } 342 343 /* 344 * Function: 345 * _soc_th3_l2_age 346 * Purpose: 347 * Handler function for L2 entry aging thread 348 * Parameters: 349 * unit - unit number 350 * Returns: 351 * none 352 */ 353 STATIC void 354 _soc_th3_l2_age(void *unit_ptr) 355 { 356 int unit = PTR_TO_INT(unit_ptr); 357 int c, m, r, rv, iter = 0; 358 soc_control_t *soc = SOC_CONTROL(unit); 359 sal_usecs_t interval; 360 sal_usecs_t stime, etime; 361 l2x_entry_t *buffer; 362 363 /* Allocate memory to accomodate L2X table */ 364 buffer = soc_cm_salloc(unit, 365 sizeof(l2x_entry_t) * 366 soc_mem_index_count(unit, L2Xm), 367 "L2Xm_age"); 368 369 if (buffer == NULL) { 370 LOG_ERROR(BSL_LS_SOC_L2, (BSL_META_U(unit, "_soc_th3_l2_age: " 371 "Memory alloc failed, size %d\n"), 372 (int)(sizeof(l2x_entry_t) * 373 soc_mem_index_count(unit, L2Xm)))); 374 375 goto cleanup_exit; 376 } 377 378 while((interval = soc->l2x_age_interval) != 0) { 379 if (!iter) { 380 goto age_delay; 381 } 382 383 LOG_VERBOSE(BSL_LS_SOC_ARL, 384 (BSL_META_U(unit, 385 "l2_age_thread: " 386 "Process iters(total:%d, this run:%d\n"), 387 ++_soc_th3_l2_bulk_age_iter[unit], iter)); 388 389 stime = sal_time_usecs(); 390 391 if (!soc->l2x_age_enable) { 392 goto age_delay; 393 } 394 395 if (soc_mem_index_count(unit, L2Xm) == 0) { 396 goto cleanup_exit; 397 } 398 399 rv = _soc_th3_l2_age_entries_process(unit, buffer); 400 401 if (!SOC_SUCCESS(rv)) { 402 goto cleanup_exit; 403 } 404 405 etime = sal_time_usecs(); 406 LOG_VERBOSE(BSL_LS_SOC_ARL, 407 (BSL_META_U(unit, 408 "l2_bulk_age_thread: unit=%d: done in %d usec\n"), 409 unit, SAL_USECS_SUB(etime, stime))); 410 age_delay: 411 rv = -1; /* timeout */ 412 if (interval > 2147) { 413 m = (interval / 2147) * 1000; 414 r = (interval % 2147) * 1000000; 415 for (c = 0; c < m; c++) { 416 rv = sal_sem_take(soc->l2x_age_notify, 2147000); 417 /* age interval is changed */ 418 if (rv == 0 || interval != soc->l2x_age_interval) { 419 break; 420 } 421 } 422 /* age interval is changed */ 423 if (soc->l2x_age_interval && 424 (rv == 0 || interval != soc->l2x_age_interval)) { 425 interval = soc->l2x_age_interval; 426 goto age_delay; 427 } else if (r) { 428 /* age interval is not changed */ 429 (void)sal_sem_take(soc->l2x_age_notify, r); 430 } 431 } else { 432 rv = sal_sem_take(soc->l2x_age_notify, interval * 1000000); 433 /* age interval is changed */ 434 if (soc->l2x_age_interval && 435 (rv == 0 || interval != soc->l2x_age_interval)) { 436 interval = soc->l2x_age_interval; 437 goto age_delay; 438 } 439 } 440 iter++; 441 } 442 443 cleanup_exit: 444 if (buffer != NULL) { 445 soc_cm_sfree(unit, buffer); 446 } 447 448 LOG_VERBOSE(BSL_LS_SOC_COMMON, 449 (BSL_META_U(unit, 450 "l2_age_thread: exiting\n"))); 451 soc->l2x_age_pid = SAL_THREAD_ERROR; 452 sal_thread_exit(0); 453 454 /* return; */ 455 } 456 457 /* 458 * Function: 459 * soc_th3_l2_age_start 460 * Purpose: 461 * Start L2 aging thread 462 * Parameters: 463 * unit - unit number 464 * Returns: 465 * SOC_E_XXX 466 */ 467 int 468 soc_th3_l2_age_start(int unit, int interval) 469 { 470 int cfg_interval; 471 soc_control_t *soc = SOC_CONTROL(unit); 472 473 cfg_interval = soc_property_get(unit, spn_L2_SW_AGING_INTERVAL, 474 SAL_BOOT_QUICKTURN ? 30 : 10); 475 SOC_CONTROL_LOCK(unit); 476 477 soc->l2x_age_interval = interval ? interval : cfg_interval; 478 sal_snprintf(soc->l2x_age_name, sizeof (soc->l2x_age_name), 479 "bcmL2age.%d", unit); 480 481 soc->l2x_age_pid = sal_thread_create(soc->l2x_age_name, SAL_THREAD_STKSZ, 482 soc_property_get(unit, 483 spn_L2AGE_THREAD_PRI, 50), 484 _soc_th3_l2_age, INT_TO_PTR(unit)); 485 486 if (soc->l2x_age_pid == SAL_THREAD_ERROR) { 487 LOG_ERROR(BSL_LS_SOC_COMMON, 488 (BSL_META_U(unit, "soc_th3_l2_age_start: Could not start" 489 " L2 aging thread\n"))); 490 491 SOC_CONTROL_UNLOCK(unit); 492 493 return SOC_E_MEMORY; 494 } 495 496 SOC_CONTROL_UNLOCK(unit); 497 498 return SOC_E_NONE; 499 } 500 501 /* 502 * Function: 503 * soc_th3_l2_age_stop 504 * Purpose: 505 * Stop l2 aging thread 506 * Parameters: 507 * unit - unit number 508 * Returns: 509 * SOC_E_XXX 510 */ 511 int 512 soc_th3_l2_age_stop(int unit) 513 { 514 soc_control_t *soc = SOC_CONTROL(unit); 515 int rv = SOC_E_NONE; 516 soc_timeout_t to; 517 518 SOC_CONTROL_LOCK(unit); 519 soc->l2x_age_interval = 0; /* Request exit */ 520 SOC_CONTROL_UNLOCK(unit); 521 522 if (soc->l2x_age_pid && (soc->l2x_age_pid != SAL_THREAD_ERROR)) { 523 /* Wake up thread so it will check the exit flag */ 524 sal_sem_give(soc->l2x_age_notify); 525 526 /* Give thread a few seconds to wake up and exit */ 527 if (SAL_BOOT_SIMULATION) { 528 soc_timeout_init(&to, 300 * 1000000, 0); 529 } else { 530 soc_timeout_init(&to, 60 * 1000000, 0); 531 } 532 533 while (soc->l2x_age_pid != SAL_THREAD_ERROR) { 534 if (soc_timeout_check(&to)) { 535 LOG_ERROR(BSL_LS_SOC_COMMON, 536 (BSL_META_U(unit, 537 "thread will not exit\n"))); 538 rv = SOC_E_INTERNAL; 539 break; 540 } 541 } 542 } 543 544 return rv; 545 } 546 547 /* 548 * Function: 549 * soc_th3_l2_lrn_cache_entry_invalidate 550 * Purpose: 551 * This function clears a specific L2 learn cache entry from a given pipe. 552 * It will be called during learning process to clear entries after they are 553 * learned 554 * Parameters: 555 * unit - unit number 556 * pipe - pipe number (0 based) 557 * entry - entry index with learn cache (0 based) 558 * Returns: 559 * SOC_E_XXX 560 */ 561 STATIC 562 int soc_th3_l2_lrn_cache_entry_invalidate(int unit, int pipe, int entry) 563 { 564 soc_mem_t mem; 565 566 if ((pipe < 0) || (pipe > (NUM_PIPE(unit) - 1))) { 567 return SOC_E_PARAM; 568 } 569 570 mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe]; 571 572 if ((entry < soc_mem_index_min(unit, mem)) || 573 (entry > soc_mem_index_max(unit, mem))) { 574 return SOC_E_PARAM; 575 } 576 577 soc_mem_lock(unit, mem); 578 SOC_IF_ERROR_RETURN(soc_mem_write(unit, mem, MEM_BLOCK_ALL, entry, 579 soc_mem_entry_zeroes(unit, mem))); 580 soc_mem_unlock(unit, mem); 581 582 return SOC_E_NONE; 583 } 584 585 /* 586 * Function: 587 * soc_th3_l2_learn_cache_clear 588 * Purpose: 589 * This function clears L2 learn cache. All copies of learn cache (in all 590 * pipes) are cleared 591 * Parameters: 592 * unit - unit number 593 * Returns: 594 * SOC_E_XXX 595 */ 596 STATIC 597 int soc_th3_l2_learn_cache_clear(int unit) 598 { 599 int pipe; 600 soc_info_t *si; 601 soc_mem_t mem; 602 603 si = &SOC_INFO(unit); 604 605 for (pipe = 0; pipe < NUM_PIPE(unit); pipe++) { 606 607 if (SOC_PBMP_IS_NULL(si->pipe_pbm[pipe])) { 608 continue; 609 } 610 611 mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe]; 612 613 soc_mem_lock(unit, mem); 614 615 SOC_IF_ERROR_RETURN(soc_mem_clear(unit, mem, MEM_BLOCK_ALL, TRUE)); 616 617 soc_mem_unlock(unit, mem); 618 } 619 620 return SOC_E_NONE; 621 } 622 623 /* 624 * Function: 625 * soc_th3_l2_learn_cache_status_clear 626 * Purpose: 627 * This function clears L2 learn cache status registers. All copies of learn 628 * cache (in all pipes) are cleared. These are sticky bits and need to be 629 * explicitly cleared by software during init or shutdown of L2 module 630 * Parameters: 631 * unit - unit number 632 * Returns: 633 * SOC_E_XXX 634 */ 635 STATIC 636 int soc_th3_l2_learn_cache_status_clear(int unit) 637 { 638 int pipe; 639 soc_reg_t reg = INVALIDr; 640 uint32 rval = 0; 641 642 reg = SOC_REG_UNIQUE_ACC(unit, L2_LEARN_CACHE_STATUSr)[0]; 643 SOC_IF_ERROR_RETURN(soc_reg32_get(unit, reg, REG_PORT_ANY, 0, &rval)); 644 soc_reg_field_set(unit, reg, &rval, L2_LEARN_CACHE_FULLf, 0x0); 645 soc_reg_field_set(unit, reg, &rval, L2_LEARN_CACHE_THRESHOLD_EXCEEDEDf, 646 0x0); 647 for (pipe = 0; pipe < NUM_PIPE(unit); pipe++) { 648 reg = SOC_REG_UNIQUE_ACC(unit, L2_LEARN_CACHE_STATUSr)[pipe]; 649 SOC_IF_ERROR_RETURN(soc_reg32_set(unit, reg, REG_PORT_ANY, 0, rval)); 650 } 651 652 return SOC_E_NONE; 653 } 654 655 /* 656 * Function: 657 * _soc_th3_l2_learn_cache_status_check_clear 658 * Purpose: 659 * This function checks status of one or more status bits in a pipe's 660 * L2 learn cache status register for the specified pipe. If any bit is set, 661 * it clears the bit(s). These are sticky bits and need to be explicitly 662 * cleared by software 663 * Parameters: 664 * unit - Unit number 665 * pipe - Pipe whose status bit needs to be cleared 666 * fld_ptr - Pointer to an array of one or more fields which are to be cleared 667 * num_flds - Size of fld_ptr array 668 * Returns: 669 * SOC_E_XXX 670 * Notes: 671 * Caller must provide correct pipe number and correct field value(s) 672 */ 673 STATIC 674 int _soc_th3_l2_learn_cache_status_check_clear(int unit, int pipe, 675 soc_field_t *fld_ptr, 676 int num_flds) 677 { 678 soc_reg_t reg = INVALIDr; 679 uint32 rval = 0; 680 uint32 bit_val; 681 int i; 682 int clear; 683 684 reg = SOC_REG_UNIQUE_ACC(unit, L2_LEARN_CACHE_STATUSr)[pipe]; 685 686 SOC_IF_ERROR_RETURN(soc_reg32_get(unit, reg, REG_PORT_ANY, 0, &rval)); 687 688 clear = FALSE; 689 690 for (i = 0; i < num_flds; i++) { 691 692 /* Check if a status bit is set. If so clear it, else check next bit */ 693 bit_val = soc_reg_field_get(unit, reg, rval, fld_ptr[i]); 694 695 if (bit_val) { 696 soc_reg_field_set(unit, reg, &rval, fld_ptr[i], 0x0); 697 clear = TRUE; 698 } 699 } 700 701 /* Program register only if atleast one bit was modified */ 702 if (clear == TRUE) { 703 SOC_IF_ERROR_RETURN(soc_reg32_set(unit, reg, REG_PORT_ANY, 0, rval)); 704 } 705 706 return SOC_E_NONE; 707 } 708 709 /* 710 * Function: 711 * soc_th3_l2_learn_cache_read 712 * Purpose: 713 * This function reads all L2 learn cache entries for a given pipe 714 * Parameters: 715 * unit - unit number 716 * pipe - pipe to read from (range: 0-7) 717 * buffer - Buffer filled by memory read operation 718 * Returns: 719 * SOC_E_XXX 720 * Notes: 721 * Caller must do range check and provide correct pipe number 722 */ 723 STATIC 724 int soc_th3_l2_learn_cache_read(int unit, int pipe, uint32 *buffer) 725 { 726 soc_mem_t mem; 727 uint32 index_min, index_max; 728 int rv; 729 730 rv = SOC_E_NONE; 731 732 mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe]; 733 734 index_min = soc_mem_index_min(unit, mem); 735 index_max = soc_mem_index_max(unit, mem); 736 737 soc_mem_lock(unit, mem); 738 739 /* Read learn cache entries from the specified pipe */ 740 rv = soc_mem_read_range(unit, mem, MEM_BLOCK_ANY, 741 index_min, index_max, buffer); 742 743 /* Explicitly clear learn cache for rev A0 */ 744 if (SOC_CONTROL(unit)->lrn_cache_clr_on_rd && 745 (rev_id == BCM56980_A0_REV_ID)) { 746 if (rv == SOC_E_NONE) { 747 rv = soc_mem_clear(unit, mem, MEM_BLOCK_ALL, FALSE); 748 } 749 } 750 751 soc_mem_unlock(unit, mem); 752 753 if (SOC_FAILURE(rv)) { 754 LOG_ERROR(BSL_LS_SOC_L2, 755 (BSL_META_U(unit, 756 "%s:DMA read failed: %s\n"), __FUNCTION__, soc_errmsg(rv))); 757 } 758 759 return rv; 760 } 761 762 /* 763 * Function: 764 * _soc_th3_learn_avl_compare_key 765 * Purpose: 766 * Comparison function for AVL shadow table operations 767 * Parameters: 768 * user_data - User supplied data reuired by AVL library 769 * datum1 - First data item to compare 770 * datum2 - Second data item to compare 771 * Returns: 772 * SOC_E_XXX 773 * Notes: 774 * None 775 */ 776 STATIC int 777 _soc_th3_learn_avl_compare_key(void *user_data, 778 shr_avl_datum_t *datum1, 779 shr_avl_datum_t *datum2) 780 { 781 soc_l2_lrn_avl_info_p k1, k2; 782 783 /* COMPILER_REFERENCE(user_data);*/ 784 k1 = (soc_l2_lrn_avl_info_p)datum1; 785 k2 = (soc_l2_lrn_avl_info_p)datum2; 786 787 SOC_MEM_COMPARE_RETURN(k1->vlan, k2->vlan); 788 789 return ENET_CMP_MACADDR(k1->mac, k2->mac); 790 } 791 792 /* 793 * Function: 794 * soc_th3_lrn_shadow_insert 795 * Purpose: 796 * This function is used to insert an entry in to learn shadow table, 797 * corresponding to the hardware L2 entry added before calling this function. 798 * Since there is no relevance of L2 multicast, static entries and 799 * vlan cross connect entries for learning process we ignore these entry types. 800 * See Notes 801 * Parameters: 802 * unit - Switch unit # 803 * l2x_entry_t - Entry to be inserted in to AVL tree 804 * Returns: 805 * SOC_E_XXX 806 * Notes: 807 * Caller _must_ lock mutex l2x_lrn_shadow_mutex before calling this function 808 */ 809 int 810 soc_th3_lrn_shadow_insert(int unit, l2x_entry_t *entry) 811 { 812 soc_l2_lrn_avl_info_t k; 813 int rv; 814 815 /* If shadow memory is freed, or not set up, do nothing */ 816 if (SOC_CONTROL(unit)->l2x_lrn_shadow == NULL) { 817 return SOC_E_NONE; 818 } 819 820 /* If entry is not valid, do not insert in to shadow table */ 821 if (!soc_mem_field32_get(unit, L2Xm, entry, BASE_VALIDf)) { 822 return SOC_E_NONE; 823 } 824 825 /* Ignore static entry */ 826 if (soc_L2Xm_field32_get(unit, entry, STATIC_BITf)) { 827 return SOC_E_NONE; 828 } 829 830 /* Do not add single cross connect entries, since they are niether learned, 831 * nor aged 832 */ 833 if (soc_mem_field32_get(unit, L2Xm, entry, KEY_TYPEf) != 834 TH3_L2_HASH_KEY_TYPE_BRIDGE) { 835 return SOC_E_NONE; 836 } 837 838 sal_memset(&k, 0x0, sizeof(k)); 839 soc_mem_mac_addr_get(unit, L2Xm, entry, MAC_ADDRf, k.mac); 840 841 /* Do not add multicast entries */ 842 if (SOC_TH3_MAC_IS_MCAST(k.mac)) { 843 return SOC_E_NONE; 844 } 845 846 k.vlan = soc_mem_field32_get(unit, L2Xm, entry, VLAN_IDf); 847 k.dest_type = soc_mem_field32_get(unit, L2Xm, entry, Tf); 848 k.port_tgid = soc_mem_field32_get(unit, L2Xm, entry, DESTINATIONf); 849 /* Entry has already been added to h/w, so we set in_hw to 'true' */ 850 k.in_hw = TRUE; 851 852 rv = shr_avl_insert(SOC_CONTROL(unit)->l2x_lrn_shadow, 853 _soc_th3_learn_avl_compare_key, 854 (shr_avl_datum_t *)&k); 855 856 /* We do not return error since normally there will always be space for a 857 * new entry to add in the tree. If this were not the case, mem insert/write 858 * called before invoking this function will fail. Also, the full condition 859 * may be cleared by software replace mechanism, or aging, or application 860 * deleting L2 entries. Also hardware h/w has already been updated at this 861 * point 862 */ 863 if (rv == -1) { 864 LOG_WARN(BSL_LS_SOC_COMMON, (BSL_META_U(unit, 865 "shr_avl_insert - tree full\n"))); 866 } 867 868 return SOC_E_NONE; 869 } 870 871 /* 872 * Function: 873 * soc_th3_lrn_shadow_delete 874 * Purpose: 875 * This function deletes an entry from learn shadow table, 876 * after the hardware L2 entry is deleted. Since there is no relevance of L2 877 * multicast, static entries and vlan cross connect entries for learning 878 * process we ignore these entry types. See Notes 879 * Parameters: 880 * unit - Switch unit # 881 * l2x_entry_t - Entry to be inserted in to AVL tree 882 * Returns: 883 * SOC_E_XXX 884 * Notes: 885 * Caller _must_ lock mutex l2x_lrn_shadow_mutex before calling this function 886 */ 887 int 888 soc_th3_lrn_shadow_delete(int unit, l2x_entry_t *entry) 889 { 890 soc_l2_lrn_avl_info_t k; 891 int rv; 892 893 /* If shadow memory is freed, or not set up, don't do anything */ 894 if (SOC_CONTROL(unit)->l2x_lrn_shadow == NULL) { 895 return SOC_E_NONE; 896 } 897 898 #if 0 899 /* If entry is not valid, do not insert in to shadow table */ 900 if (!soc_mem_field32_get(unit, L2Xm, entry, BASE_VALIDf)) { 901 return SOC_E_NONE; 902 } 903 #endif 904 905 /* Ignore static entry */ 906 if (soc_L2Xm_field32_get(unit, entry, STATIC_BITf)) { 907 return SOC_E_NONE; 908 } 909 910 /* Ignore single cross connect entries, since they are niether learned, 911 * nor aged 912 */ 913 if (soc_mem_field32_get(unit, L2Xm, entry, KEY_TYPEf) != 914 TH3_L2_HASH_KEY_TYPE_BRIDGE) { 915 return SOC_E_NONE; 916 } 917 918 sal_memset(&k, 0x0, sizeof(k)); 919 soc_mem_mac_addr_get(unit, L2Xm, entry, MAC_ADDRf, k.mac); 920 921 /* Ignore multicast entries */ 922 if (SOC_TH3_MAC_IS_MCAST(k.mac)) { 923 return SOC_E_NONE; 924 } 925 926 k.vlan = soc_mem_field32_get(unit, L2Xm, entry, VLAN_IDf); 927 k.dest_type = soc_mem_field32_get(unit, L2Xm, entry, Tf); 928 k.port_tgid = soc_mem_field32_get(unit, L2Xm, entry, DESTINATIONf); 929 /* Entry has already been deleted from h/w, so we set in_hw to 'false' */ 930 k.in_hw = FALSE; 931 932 rv = shr_avl_delete(SOC_CONTROL(unit)->l2x_lrn_shadow, 933 _soc_th3_learn_avl_compare_key, 934 (shr_avl_datum_t *)&k); 935 if (rv == 0) { 936 LOG_INFO(BSL_LS_SOC_COMMON, (BSL_META_U(unit, 937 "shr_avl_delete: Did not find datum\n"))); 938 } 939 940 return SOC_E_NONE; 941 } 942 943 /* 944 * Function: 945 * soc_th3_lrn_shadow_show 946 * Purpose: 947 * Debug display function for AVL learn shadow table 948 * Parameters: 949 * user_data - Used to pass StrataSwitch unit # 950 * datum - AVL node to display 951 * extra_data - Unused 952 * Returns: 953 * SOC_E_XXX 954 */ 955 956 int 957 soc_th3_lrn_shadow_show(void *user_data, shr_avl_datum_t *datum, void *extra_data) 958 { 959 int unit = PTR_TO_INT(user_data); 960 soc_l2_lrn_avl_info_p k = (soc_l2_lrn_avl_info_p)datum; 961 962 COMPILER_REFERENCE(extra_data); 963 964 BSL_LOG(BSL_LSS_CLI, (BSL_META_U(unit, "dest_type: %d, port_tgid: %d, mod: %d, in_hw: %d \n"),k->dest_type, k->port_tgid, k->mod, k->in_hw)); 965 BSL_LOG(BSL_LSS_CLI, (BSL_META_U(unit, "mac(in hex) %02X:%02X:%02X:%02X:%02X:%02X, vlan: %d\n"),k->mac[0], k->mac[1], k->mac[2], k->mac[3], k->mac[4], k->mac[5], k->vlan)); 966 LOG_CLI((BSL_META_U(unit, 967 "----------------------------------------\n"))); 968 969 return SOC_E_NONE; 970 } 971 972 /* 973 * Function: 974 * _soc_th3_learn_do_lookup 975 * Purpose: 976 * This function searches shadow table for matching key, and sets passed 977 * arguments accordingly 978 * Parameters: 979 * unit(IN) - Device unit number 980 * k(IN/OUT) - key items to search for. The AVL library updates other 981 * fields of this structure, if key is found 982 * found(OUT) - Set to true if item is found, else set to false 983 * stn_move(OUT) - Set to true if station move condition is detected, 984 * else set to false 985 * Returns: 986 * SOC_E_XXX 987 * Notes: 988 * None 989 */ 990 STATIC int 991 _soc_th3_learn_do_lookup(int unit, 992 soc_l2_lrn_avl_info_p k, 993 int *found, 994 int *stn_move) 995 { 996 997 int result; 998 int port_tgid; 999 int dest_type; 1000 1001 /* Save port number obtained from hardware 1002 * Note AVL lookup overwrites 'k' completely, if the entry was found 1003 */ 1004 port_tgid = k->port_tgid; 1005 dest_type = k->dest_type; 1006 1007 sal_mutex_take(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex, sal_mutex_FOREVER); 1008 1009 result = shr_avl_lookup(SOC_CONTROL(unit)->l2x_lrn_shadow, 1010 _soc_th3_learn_avl_compare_key, (shr_avl_datum_t *)k); 1011 1012 sal_mutex_give(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex); 1013 1014 /* Check if a matching node was found in AVL tree. If so, check if the 1015 * entry has been added to L2 table in h/w. If so, then do nothing. 1016 * If not, the entry needs to be flagged for programming in h/w 1017 */ 1018 *found = FALSE; 1019 *stn_move = FALSE; 1020 if (result) { 1021 1022 /* Entry found */ 1023 *found = TRUE; 1024 1025 /* If entry is found, check for station move */ 1026 *stn_move = ((k->dest_type == dest_type) && (k->port_tgid == port_tgid)) ? FALSE : TRUE; 1027 } 1028 1029 return SOC_E_NONE; 1030 } 1031 1032 /* 1033 * Function: 1034 * _soc_th3_learn_cache_entry_process 1035 * Purpose: 1036 * This function processes each entry from the learn cache. It check if the 1037 * if the entry is present in learn shadow table. If entry is not found, it is 1038 * a new L2 flow, so its added programmed in to main L2 table. Learn cache 1039 * is updated after addition. 1040 * Parameters: 1041 * unit - Unit number of device 1042 * pipe - Pipe in which the entry was detected (range: 0-7) 1043 * entry - Learn cache entry from the pipe 1044 * index - Location of entry within the learn cache (range 0-15) 1045 * Returns: 1046 * SOC_E_XXX 1047 * Notes: 1048 * None 1049 */ 1050 STATIC int 1051 _soc_th3_learn_cache_entry_process(int unit, 1052 int pipe, 1053 l2_learn_cache_entry_t *entry, 1054 int entry_idx) 1055 { 1056 int rv; 1057 int found; 1058 int stn_move; 1059 soc_mem_t mem; 1060 soc_l2_lrn_avl_info_t k; 1061 int invalidated = FALSE; 1062 int curr_l2_table_entries; 1063 int max_l2_table_entries; 1064 int l2copyno; 1065 1066 max_l2_table_entries = soc_mem_index_count(unit, L2Xm); 1067 l2copyno = SOC_MEM_BLOCK_ANY(unit, L2Xm); 1068 1069 mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe]; 1070 1071 sal_memset(&k, 0x0, sizeof(k)); 1072 1073 soc_mem_mac_addr_get(unit, mem, entry, MAC_ADDRf, k.mac); 1074 k.vlan = soc_mem_field32_get(unit, mem, entry, VLAN_IDf); 1075 k.dest_type = soc_mem_field32_get(unit, mem, entry, DEST_TYPEf); 1076 k.port_tgid = soc_mem_field32_get(unit, mem, entry, DESTINATIONf); 1077 k.in_hw = FALSE; 1078 1079 rv = _soc_th3_learn_do_lookup(unit, &k, &found, &stn_move); 1080 1081 /* Check if learn interrupt needs to be disabled during processing below */ 1082 if (rv == SOC_E_NONE) { 1083 l2x_entry_t l2x_entry; 1084 soc_port_t port_tgid = 0; 1085 soc_field_t field = INVALIDf; 1086 1087 if (SOC_CONTROL(unit)->lrn_cache_clr_on_rd) { 1088 /* Check if entry was added to h/w by previous learn cache entry */ 1089 /* Duplicate entries can result only in clear on read mode */ 1090 if (found == TRUE) { 1091 if (k.in_hw == TRUE) { 1092 /* In case of station move, L2X entry is already present in 1093 * h/w (in_hw is true); we should not invalidate the first 1094 * learn cache entry here (for station move), without 1095 * checking station move condition (station move is handled 1096 * later in this function) 1097 */ 1098 if (stn_move == FALSE) { 1099 /* Entry cleared by h/w in clr-on-rd mode, so we do not 1100 * explicitly invalidate the entry here 1101 */ 1102 /* SOC_IF_ERROR_RETURN( 1103 soc_th3_l2_lrn_cache_entry_invalidate(unit, pipe, 1104 entry_idx)); */ 1105 LOG_INFO(BSL_LS_SOC_L2, 1106 (BSL_META_U(unit, "Duplicate lrn cache entry:" 1107 " pipe %d, index %d\n"), pipe, entry_idx)); 1108 1109 return rv; 1110 } 1111 } else { 1112 /* If k.in_hw is FALSE, it means h/w was 1113 * updated, but software table entry was not, which should 1114 * never happen. It may point to software table corruption, 1115 * or a problem arising out of table write sequence. 1116 * Tables should always be in sync 1117 */ 1118 LOG_ERROR(BSL_LS_SOC_COMMON, (BSL_META_U(unit, "%s: S/w" 1119 " entry %d, in pipe %d out of sync with" 1120 " h/w\n"), __FUNCTION__, entry_idx, pipe)); 1121 return SOC_E_INTERNAL; 1122 } 1123 } 1124 } 1125 1126 sal_memset(&l2x_entry, 0, sizeof(l2x_entry)); 1127 soc_L2Xm_field32_set(unit, &l2x_entry, BASE_VALIDf, 0x1); 1128 soc_L2Xm_field32_set(unit, &l2x_entry, VLAN_IDf, k.vlan); 1129 soc_L2Xm_mac_addr_set(unit, &l2x_entry, MAC_ADDRf, k.mac); 1130 soc_L2Xm_field32_set(unit, &l2x_entry, KEY_TYPEf, 1131 TH3_L2_HASH_KEY_TYPE_BRIDGE); 1132 if (k.dest_type) { 1133 soc_L2Xm_field32_set(unit, &l2x_entry, Tf, 0x1); 1134 } 1135 1136 if (found == FALSE) { 1137 /* Replace original port with new port */ 1138 field = k.dest_type ? TGIDf : PORT_NUMf; 1139 soc_L2Xm_field32_set(unit, &l2x_entry, field, k.port_tgid); 1140 soc_L2Xm_field32_set(unit, &l2x_entry, HITSAf, 1); 1141 1142 /* Insert L2 entry in h/w */ 1143 soc_mem_lock(unit, L2Xm); 1144 curr_l2_table_entries = SOP_MEM_STATE(unit, L2Xm).count[l2copyno]; 1145 1146 /* If there is no space in the L2 table, do not issue insert. Note 1147 * that current L2 table size is dynamically changing; entries can 1148 * be added/deleted though other sources like application thread 1149 * (using L2 APIs), cmd shell, other internal SDK modules and so on. 1150 * So the current table enttries is only a tentative (but closer to 1151 * accurate) value 1152 */ 1153 rv = SOC_E_NONE; 1154 if ((curr_l2_table_entries >= 0) && 1155 (curr_l2_table_entries < max_l2_table_entries)) { 1156 rv = soc_mem_insert(unit, L2Xm, MEM_BLOCK_ALL, &l2x_entry); 1157 } 1158 soc_mem_unlock(unit, L2Xm); 1159 1160 /* AVL tree will be updated through soc_th3_l2x_shadow_callback */ 1161 1162 if ((rv == SOC_E_FULL) || (rv == SOC_E_EXISTS) || 1163 (rv == SOC_E_NOT_FOUND)) { 1164 /* If full, exist or not found conditions are encountered, we 1165 * simply log an error. If we return error, learn thread will 1166 * exit. We don't want the thread to exit based on certain 1167 * 'conditions', or search result, since it will stop learning 1168 * altogether. Full condition can get cleared later on by aging, 1169 * deletions by application(s), or by s/w replace operation. 1170 */ 1171 LOG_INFO(BSL_LS_SOC_L2, 1172 (BSL_META_U(unit, "%s: soc_mem_insert retval %d\n"), 1173 __FUNCTION__, rv)); 1174 rv = SOC_E_NONE; 1175 } else { 1176 if (SOC_FAILURE(rv)) { 1177 return rv; 1178 } 1179 } 1180 1181 /* Clear entry in learn cache only if clr on rd is not enabled */ 1182 if (!(SOC_CONTROL(unit)->lrn_cache_clr_on_rd)) { 1183 SOC_IF_ERROR_RETURN(soc_th3_l2_lrn_cache_entry_invalidate(unit, 1184 pipe, entry_idx)); 1185 } 1186 1187 invalidated = TRUE; 1188 } 1189 1190 /* Handle station move */ 1191 if ((stn_move == TRUE) && (found == TRUE)) { 1192 soc_mem_lock(unit, L2Xm); 1193 rv = soc_mem_delete(unit, L2Xm, MEM_BLOCK_ALL, &l2x_entry); 1194 if (SOC_FAILURE(rv)) { 1195 soc_mem_unlock(unit, L2Xm); 1196 if (rv == SOC_E_NOT_FOUND) { 1197 /* If entry is not found, log an error. Do not return this 1198 * error code since it is not a critical/fatal error. If 1199 * same error code is returned, the learn thread will exit 1200 * and learning will stop 1201 */ 1202 LOG_INFO(BSL_LS_SOC_L2, 1203 (BSL_META_U(unit, "%s: soc_mem_delete" 1204 " retval %d\n"), __FUNCTION__, rv)); 1205 rv = SOC_E_NONE; 1206 } 1207 1208 return rv; 1209 } 1210 1211 /* Replace original port with new port */ 1212 port_tgid = soc_mem_field32_get(unit, mem, entry, DESTINATIONf); 1213 /* k.dest_type, k.port_tgid are overwritten by 1214 * _soc_th3_learn_do_lookup, so we get k.dest_type from learn cache 1215 * again (k.port_tgid is not used here though) 1216 */ 1217 k.dest_type = soc_mem_field32_get(unit, mem, entry, DEST_TYPEf); 1218 field = k.dest_type ? TGIDf : PORT_NUMf; 1219 if (k.dest_type) { 1220 soc_L2Xm_field32_set(unit, &l2x_entry, Tf, 0x1); 1221 } else { 1222 soc_L2Xm_field32_set(unit, &l2x_entry, Tf, 0x0); 1223 } 1224 soc_L2Xm_field32_set(unit, &l2x_entry, field, port_tgid); 1225 soc_L2Xm_field32_set(unit, &l2x_entry, HITSAf, 1); 1226 1227 rv = soc_mem_insert(unit, L2Xm, MEM_BLOCK_ALL, &l2x_entry); 1228 soc_mem_unlock(unit, L2Xm); 1229 1230 if ((rv == SOC_E_FULL) || (rv == SOC_E_EXISTS) || 1231 (rv == SOC_E_NOT_FOUND)) { 1232 /* If full, exist or not found conditions are encountered, we 1233 * simply log an error. If we return error, learn thread will 1234 * exit. We don't want the thread to exit based on certain 1235 * 'conditions', or search result, since it will stop learning 1236 * altogether. Full condition can get cleared later on by aging, 1237 * deletions by application(s), or by s/w replace operation. 1238 */ 1239 LOG_INFO(BSL_LS_SOC_L2, 1240 (BSL_META_U(unit, "%s: soc_mem_insert retval %d\n"), 1241 __FUNCTION__, rv)); 1242 rv = SOC_E_NONE; 1243 } else { 1244 if (SOC_FAILURE(rv)) { 1245 return rv; 1246 } 1247 } 1248 1249 1250 /* AVL tree will be updated through soc_th3_l2x_shadow_callback */ 1251 1252 /* Clear entry in learn cache only if clr on rd is not enabled */ 1253 if (!(SOC_CONTROL(unit)->lrn_cache_clr_on_rd)) { 1254 SOC_IF_ERROR_RETURN(soc_th3_l2_lrn_cache_entry_invalidate(unit, 1255 pipe, entry_idx)); 1256 } 1257 invalidated = TRUE; 1258 } 1259 } 1260 1261 /* This case should not happen, added here as a precaution to avoid 1262 * full condition for learn cache due to unprocessed entries (if 1263 * this condition is reached, it means the entry was not processed 1264 * earlier) 1265 */ 1266 if (invalidated == FALSE) { 1267 LOG_INFO(BSL_LS_SOC_L2, 1268 (BSL_META_U(unit, "%s: Entry %d in pipe %d not processed," 1269 " removing it from lrn cache\n"), __FUNCTION__, 1270 entry_idx, pipe)); 1271 1272 SOC_IF_ERROR_RETURN(soc_th3_l2_lrn_cache_entry_invalidate(unit, 1273 pipe, entry_idx)); 1274 } 1275 1276 return rv; 1277 } 1278 1279 /* 1280 * Function: 1281 * _soc_th3_lrn_cache_intr_configure 1282 * Purpose: 1283 * This function is used to enable or disable L2 learn cache interrupt 1284 * generation 1285 * Parameters: 1286 * unit - SOC unit # 1287 * bit - Bit number corresponding to a pipe. Bit 8 is for pipe 0 and 1288 * bit 15 is for pipe 7. See Notes. 1289 * enable - To enable interrupt, use 1 or a non-zero value. 1290 * To disable interrupt, use 0. 1291 * Returns: 1292 * SOC_E_XXX 1293 * Notes: 1294 * This function uses hard-coded values for interrupt numbers/bit positions. 1295 * Also it assumes fixed number of bits (1 per pipe), contiguous numbering, 1296 * and fixed bit positions for each pipe; so it is not portable 1297 */ 1298 STATIC int 1299 _soc_th3_lrn_cache_intr_configure(int unit, int bit, int enable) 1300 { 1301 int rv = SOC_E_NONE; 1302 uint32 regval = 0; 1303 soc_reg_t reg = ICFG_CHIP_LP_INTR_ENABLE_REG1r; 1304 1305 /* Accept only bits 8-15 (both numbers included) */ 1306 if ((bit < 8) || (bit > 15)) { 1307 return SOC_E_INTERNAL; 1308 } 1309 1310 rv = soc_iproc_getreg(unit, soc_reg_addr(unit, reg, REG_PORT_ANY, 0), 1311 ®val); 1312 if (rv == SOC_E_NONE) { 1313 if (enable) { 1314 regval |= 1 << bit; 1315 } else { 1316 regval &= ~(1 << bit); 1317 } 1318 1319 rv = soc_iproc_setreg(unit, soc_reg_addr(unit, reg, REG_PORT_ANY, 0), 1320 regval); 1321 } 1322 1323 return rv; 1324 } 1325 1326 /* 1327 * Function: 1328 * soc_th3_lrn_cache_intr_handler 1329 * Purpose: 1330 * Interrupt handler for (per-pipe) learn cache (fifo) interrupt 1331 * Parameters: 1332 * unit - SOC unit # 1333 * data - Data used by the isr, initialized during interrupt registration 1334 * Returns: 1335 * Nothing 1336 */ 1337 void 1338 soc_th3_lrn_cache_intr_handler(int unit, void *data) 1339 { 1340 soc_control_t *soc = SOC_CONTROL(unit); 1341 int i; 1342 1343 if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) { 1344 /* Disable learn cache interrupts from all pipes */ 1345 for (i = 8; i <= 15; i++) { 1346 (void)_soc_th3_lrn_cache_intr_configure(unit, i, 0); 1347 } 1348 1349 /* Signal lrn thread of learn event(s) */ 1350 sal_sem_give(soc->arl_notify); 1351 } 1352 1353 /* If we see interrupt in polled mode, then there is some misconfiguration. 1354 * In this case, check L2_LEARN_COPY_CACHE_CTRL's interrupt control bit 1355 */ 1356 1357 return; 1358 } 1359 1360 /* 1361 * Function: 1362 * soc_th3_l2_learn_alloc_resources 1363 * Purpose: 1364 * This function is used to create learn shadow table memory and mutex for 1365 * safe access to the shadow table. It is called during L2 initialization 1366 * Parameters: 1367 * u - Pointer to unit number 1368 * Returns: 1369 * SOC_E_NONE on success 1370 * Other SOC_E_* codes on error 1371 */ 1372 int 1373 soc_th3_l2_learn_alloc_resources(int unit) 1374 { 1375 /* Create AVL table and semaphore used for L2 learning */ 1376 if (SOC_CONTROL(unit)->l2x_lrn_shadow != NULL) { 1377 1378 if (shr_avl_destroy(SOC_CONTROL(unit)->l2x_lrn_shadow) < 0) { 1379 LOG_ERROR(BSL_LS_SOC_COMMON, 1380 (BSL_META_U(unit, "%d: Error calling shr_avl_destroy\n"), unit)); 1381 1382 return SOC_E_INTERNAL; 1383 } 1384 1385 SOC_CONTROL(unit)->l2x_lrn_shadow = NULL; 1386 } 1387 1388 if (shr_avl_create(&SOC_CONTROL(unit)->l2x_lrn_shadow, INT_TO_PTR(unit), 1389 sizeof(soc_l2_lrn_avl_info_t), _SOC_TH3_L2_LRN_TBL_SIZE) < 0) { 1390 1391 LOG_ERROR(BSL_LS_SOC_COMMON, 1392 (BSL_META_U(unit, "%d: Error calling shr_avl_create\n"), unit)); 1393 1394 return SOC_E_MEMORY; 1395 } 1396 1397 if ((SOC_CONTROL(unit)->l2x_lrn_shadow_mutex = 1398 sal_mutex_create("L2AvlMutex")) == NULL) { 1399 1400 if (SOC_CONTROL(unit)->l2x_lrn_shadow != NULL) { 1401 shr_avl_destroy(SOC_CONTROL(unit)->l2x_lrn_shadow); 1402 SOC_CONTROL(unit)->l2x_lrn_shadow = NULL; 1403 } 1404 1405 LOG_ERROR(BSL_LS_SOC_COMMON, 1406 (BSL_META_U(unit, "%d: Error calling sal_mutex_create for" 1407 " L2 AVL Mutex\n"), unit)); 1408 1409 return SOC_E_MEMORY; 1410 } 1411 1412 LOG_INFO(BSL_LS_SOC_L2, 1413 (BSL_META_U(unit, "%d: %s: Created" 1414 " shadow table and mutex\n"), unit, __FUNCTION__)); 1415 1416 return SOC_E_NONE; 1417 } 1418 1419 /* 1420 * Function: 1421 * _soc_th3_l2_learn_process 1422 * Purpose: 1423 * This function is the main handler for learn thread. It will be used by 1424 * learn thread during learning. It will read learn cache entries, perform 1425 * look-ups in shadow (AVL) table during station move, and write to it 1426 * after an L2 entry is learned 1427 * Parameters: 1428 * u - Pointer to unit number 1429 * Returns: 1430 * Nothing 1431 */ 1432 STATIC void 1433 _soc_th3_l2_learn_process(void *u) 1434 { 1435 int unit = PTR_TO_INT(u); 1436 soc_control_t *soc = SOC_CONTROL(unit); 1437 int interval; 1438 void *buffer; 1439 uint32 index_min; 1440 int count; 1441 int num_bytes; 1442 int rv; 1443 int pipe; 1444 soc_mem_t mem; 1445 int i; 1446 int valid; 1447 1448 LOG_INFO(BSL_LS_SOC_L2, 1449 (BSL_META_U(unit, "%d: In _soc_th3_l2_learn_process\n"), unit)); 1450 1451 mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[0]; 1452 num_bytes = soc_mem_entry_bytes(unit, mem); 1453 count = soc_mem_index_count(unit, mem); 1454 index_min = soc_mem_index_min(unit, mem); 1455 1456 buffer = soc_cm_salloc(unit, num_bytes * count, "L2_LEARN_CACHEm"); 1457 1458 if (buffer == NULL) { 1459 soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR, 1460 SOC_SWITCH_EVENT_THREAD_L2X_LEARN, __LINE__, 1461 SOC_E_MEMORY); 1462 goto cleanup_exit; 1463 } 1464 1465 if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) { 1466 /* Enable learn cache interrupts for all pipes */ 1467 for (i = 8; i <= 15; i++) { 1468 (void)_soc_th3_lrn_cache_intr_configure(unit, i, 1); 1469 } 1470 } 1471 1472 while((interval = soc->l2x_learn_interval)) { 1473 1474 uint32 sts_reg = 0; 1475 uint32 en_reg = 0; 1476 uint32 mask = 0; 1477 uint32 poll_all_pipes = 1; 1478 1479 if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) { 1480 soc_reg_t reg = INVALIDr; 1481 uint32 shift = 0; 1482 1483 sal_sem_take(soc->arl_notify, interval); 1484 1485 /* 1486 * We read the interrupt status here, and process the pipes whose 1487 * learn cache has reached or exceeded threshold. While processing, 1488 * if other pipes raise interrupts, we will process them in the 1489 * next cycle 1490 */ 1491 reg = ICFG_CHIP_LP_INTR_RAW_STATUS_REG1r; 1492 1493 rv = soc_iproc_getreg(unit, 1494 soc_reg_addr(unit, reg, REG_PORT_ANY, 0), 1495 &sts_reg); 1496 1497 if (SOC_SUCCESS(rv)) { 1498 reg = ICFG_CHIP_LP_INTR_ENABLE_REG1r; 1499 1500 rv = soc_iproc_getreg(unit, 1501 soc_reg_addr(unit, reg, REG_PORT_ANY, 0), 1502 &en_reg); 1503 } 1504 1505 if (SOC_FAILURE(rv)) { 1506 LOG_ERROR(BSL_LS_SOC_COMMON, 1507 (BSL_META_U(unit, "Failed to read register" 1508 " %s, rv = %d\n"), SOC_REG_NAME(unit, reg), rv)); 1509 1510 goto cleanup_exit; 1511 } 1512 1513 shift = 8; /* L2 learn interrupt bits start at bit 8 */ 1514 1515 /* Create mask to pick the set of learning bits */ 1516 mask = (1U << NUM_PIPE(unit)) - 1; 1517 mask <<= shift; 1518 1519 /* Clear out intr bits other than those that are for learning */ 1520 en_reg &= mask; 1521 sts_reg &= mask; 1522 1523 /* Select intr bits which are currently disabled by the isr 1524 * (soc_th3_lrn_cache_intr_handler), since those are the ones that 1525 * need to be serviced. (Note currently we reset all bits in the 1526 * isr to simplify interrupt processing) 1527 */ 1528 sts_reg &= ~en_reg; 1529 sts_reg >>= shift; 1530 1531 /* Process all pipes if there was no interrupt during learn 1532 * interval. This way we handle pipes which have entries, but the 1533 * threshold has not reached, so the interrupt is not generated 1534 * (for those pipes) 1535 */ 1536 poll_all_pipes = !sts_reg ? 1 : 0; 1537 } 1538 1539 /* The system is in warmboot phase. We do not do any learn processing 1540 * until we are out of warmboot 1541 */ 1542 if (SOC_WARM_BOOT(unit)) { 1543 goto skip_processing; 1544 } 1545 1546 for (pipe = 0; pipe < NUM_PIPE(unit); pipe++) { 1547 int full_cleared; 1548 int thr_cleared; 1549 /* Number of valid learn cache entries processed successfully */ 1550 int processed_cnt; 1551 soc_info_t *si = &SOC_INFO(unit); 1552 1553 /* For half-pipe configuration, this check has been added */ 1554 if (SOC_PBMP_IS_NULL(si->pipe_pbm[pipe])) { 1555 continue; 1556 } 1557 1558 /* The system is in warmboot phase. We do not do any learn 1559 * processing until we are out of warmboot 1560 */ 1561 if (SOC_WARM_BOOT(unit)) { 1562 goto skip_processing; 1563 } 1564 1565 full_cleared = FALSE; 1566 thr_cleared = FALSE; 1567 1568 /* Count the number of valid entries processed, and use it to 1569 * compare to threshold value 1570 */ 1571 processed_cnt = 0; 1572 1573 if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) { 1574 /* sts_reg has bits set for pipes whose caches need 1575 * to be processed 1576 * If we are not polling all pipes, then we should check 1577 * individual bits in the sts_reg bitmap (corresponding to each 1578 * pipe), and process entries in that pipe (interrupt was 1579 * asserted for pipes in sts_reg bitmap) 1580 */ 1581 /* If we shouldn't process all pipes, then specific pipes in the 1582 * bitmap are the ones which need to be serviced 1583 */ 1584 if ((!poll_all_pipes) && (!(sts_reg & (1U << pipe)))) { 1585 continue; 1586 } 1587 } 1588 1589 /* Read cache entries for the specified pipe */ 1590 rv = soc_th3_l2_learn_cache_read(unit, pipe, buffer); 1591 1592 if (SOC_FAILURE(rv)) { 1593 soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR, 1594 SOC_SWITCH_EVENT_THREAD_L2X_LEARN, 1595 __LINE__, rv); 1596 goto cleanup_exit; 1597 } 1598 1599 /* Process each learn cache entry */ 1600 for (i = index_min; i < (index_min + count); i++) { 1601 l2_learn_cache_entry_t *entry; 1602 1603 if (!soc->l2x_learn_interval) { 1604 goto cleanup_exit; 1605 } 1606 1607 /* The system is in warmboot phase. We do not do any learn 1608 * processing until we are out of warmboot 1609 */ 1610 if (SOC_WARM_BOOT(unit)) { 1611 goto skip_processing; 1612 } 1613 1614 /* Point to the next entry in the buffer */ 1615 entry = (l2_learn_cache_entry_t *)((uint8 *)buffer + 1616 i * num_bytes); 1617 1618 mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe]; 1619 1620 valid = soc_mem_field32_get(unit, mem, entry, VALIDf); 1621 1622 /* Process valid entries only */ 1623 if (valid) { 1624 LOG_DEBUG(BSL_LS_SOC_L2, 1625 (BSL_META_U(unit, "%s: Valid entry in pipe %d, index %d\n"), __FUNCTION__, pipe, i)); 1626 1627 rv = _soc_th3_learn_cache_entry_process(unit, pipe, entry, i); 1628 if (SOC_FAILURE(rv)) { 1629 /* In case of failure, we do not exit the thread, since 1630 * learning will stop altogether. Assumption is that 1631 * the error may be transitory in nature 1632 */ 1633 LOG_INFO(BSL_LS_SOC_COMMON, 1634 (BSL_META_U(unit, "Failed to add entry" 1635 " in pipe %d, index %d, rv = %d\n"), 1636 pipe, i, rv)); 1637 continue; 1638 } 1639 1640 if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) { 1641 processed_cnt++; 1642 1643 /* Check if cache full condition exists. If so, clear 1644 * it only once in this processing cycle, for each pipe, 1645 * after 1st entry is processed. (If cache fills again, 1646 * we will handle it in the next interrupt handling 1647 * cycle) 1648 */ 1649 if (full_cleared == FALSE) { 1650 soc_field_t fld = L2_LEARN_CACHE_FULLf; 1651 1652 rv = _soc_th3_l2_learn_cache_status_check_clear( 1653 unit, pipe, &fld, 1); 1654 1655 if (SOC_FAILURE(rv)) { 1656 LOG_ERROR(BSL_LS_SOC_COMMON, 1657 (BSL_META_U(unit, "Cache full bit" 1658 " could not be cleared in pipe %d," 1659 " index %d, rv = %d\n"), 1660 pipe, i, rv)); 1661 goto cleanup_exit; 1662 } 1663 1664 full_cleared = TRUE; 1665 } 1666 1667 if (thr_cleared == FALSE) { 1668 /* Clear thresold exceeded bit if number of valid 1669 * entries processed crossed the programmed 1670 * threshold value. If the threshold is set to 1, 1671 * we have already processed the entry, so we 1672 * immediately clear threshold bit 1673 */ 1674 if ((processed_cnt > soc->lrn_cache_threshold) || 1675 (soc->lrn_cache_threshold == 1)) { 1676 1677 soc_field_t fld = 1678 L2_LEARN_CACHE_THRESHOLD_EXCEEDEDf; 1679 1680 rv = _soc_th3_l2_learn_cache_status_check_clear( 1681 unit, pipe, &fld, 1); 1682 1683 if (SOC_FAILURE(rv)) { 1684 LOG_ERROR(BSL_LS_SOC_COMMON, 1685 (BSL_META_U(unit, "Threshold exc. bit" 1686 " could not be cleared in pipe %d," 1687 " index %d, rv = %d\n"), 1688 pipe, i, rv)); 1689 goto cleanup_exit; 1690 } 1691 1692 thr_cleared = TRUE; 1693 } 1694 } 1695 } 1696 } 1697 } 1698 } 1699 1700 skip_processing: 1701 if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) { 1702 /* Enable learn cache interrupts for all pipes */ 1703 for (i = 8; i <= 15; i++) { 1704 (void)_soc_th3_lrn_cache_intr_configure(unit, i, 1); 1705 } 1706 } else { 1707 sal_usleep(interval); 1708 } 1709 } 1710 1711 cleanup_exit: 1712 /* 1713 Check if this is required 1714 if (SOC_CONTROL(unit)->l2x_lrn_shadow != NULL) { 1715 shr_avl_destroy(SOC_CONTROL(unit)->l2x_lrn_shadow); 1716 SOC_CONTROL(unit)->l2x_lrn_shadow = NULL; 1717 } 1718 1719 if (SOC_CONTROL(unit)->l2x_lrn_shadow_mutex != NULL) { 1720 sal_mutex_destroy(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex); 1721 SOC_CONTROL(unit)->l2x_lrn_shadow_mutex = NULL; 1722 } 1723 */ 1724 1725 if (buffer != NULL) { 1726 soc_cm_sfree(unit, buffer); 1727 } 1728 soc->l2x_learn_pid = SAL_THREAD_ERROR; 1729 sal_thread_exit(0); 1730 } 1731 1732 /* 1733 * Function: 1734 * soc_th3_l2_learn_start 1735 * Purpose: 1736 * Start l2 learn thread 1737 * Parameters: 1738 * unit - unit number. 1739 * Returns: 1740 * SOC_E_XXX 1741 * Notes: 1742 * soc_th3_l2_learn_alloc_resources must be called before calling this function 1743 * for the _first_ time 1744 */ 1745 int 1746 soc_th3_l2_learn_thread_start(int unit, int interval) 1747 { 1748 soc_control_t *soc = SOC_CONTROL(unit); 1749 uint32 reg_val = 0; 1750 int pri = SOC_TH3_LRN_THREAD_PRI_DEFAULT; 1751 1752 if (soc->l2x_learn_interval != 0) { 1753 SOC_IF_ERROR_RETURN(soc_th3_l2_learn_thread_stop(unit)); 1754 } 1755 1756 SOC_CONTROL_LOCK(unit); 1757 sal_snprintf(soc->l2x_learn_name, sizeof (soc->l2x_age_name), "L2Lrn.%d", 1758 unit); 1759 1760 if (soc->l2x_learn_pid == SAL_THREAD_ERROR) { 1761 soc_th3_l2x_lrn_mode_t mode; 1762 1763 soc_cm_get_id(unit, &dev_id, &rev_id); 1764 1765 if (soc_property_get(unit, spn_L2XLRN_INTR_EN, 1766 SOC_TH3_LRN_CACHE_INTR_CTL_DEFAULT)) { 1767 mode = L2_LRN_MODE_INTR; 1768 } else { 1769 mode = L2_LRN_MODE_POLL; 1770 } 1771 1772 /* Always polled mode for simulation */ 1773 if (SAL_BOOT_BCMSIM) { 1774 mode = L2_LRN_MODE_POLL; 1775 } 1776 1777 soc->l2x_lrn_mode = mode; 1778 1779 /* Do not use clear-on-read by default, unless user wants it */ 1780 soc->lrn_cache_clr_on_rd = soc_property_get(unit, 1781 spn_L2XLRN_CLEAR_ON_READ, 1782 SOC_TH3_LRN_CACHE_CLR_ON_RD_DEFAULT); 1783 1784 soc->l2x_learn_interval = interval; 1785 1786 if (interval == 0) { 1787 SOC_CONTROL_UNLOCK(unit); 1788 return SOC_E_NONE; 1789 } 1790 1791 /* Set initial values for learn cache operation */ 1792 SOC_IF_ERROR_RETURN(READ_L2_LEARN_COPY_CACHE_CTRLr(unit, ®_val)); 1793 1794 soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, ®_val, 1795 L2_LEARN_CACHE_ENf, 0x1); 1796 soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, ®_val, 1797 CLEAR_ON_READ_ENf, 1798 (uint32)(soc->lrn_cache_clr_on_rd)); 1799 1800 if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) { 1801 /* 1802 * 1. Set default interrupt threshold (from soc property, or soc) 1803 * 2. Mark all cache entries as 'invalid' - done below this 'if' 1804 * 3. Enable l2 learn cache interrupt. 1805 */ 1806 soc->lrn_cache_threshold = soc_property_get(unit, 1807 spn_L2XLRN_INTR_THRESHOLD, 1808 SOC_TH3_LRN_CACHE_THRESHOLD_DEFAULT); 1809 1810 /* A value of 0 or less is illegal. Also a value above 16 is 1811 * illegal, flag error 1812 */ 1813 if ((soc->lrn_cache_threshold <= 0) || 1814 (soc->lrn_cache_threshold > 16)) { 1815 LOG_ERROR(BSL_LS_SOC_COMMON, (BSL_META_U(unit, 1816 "soc_th3_l2_learn_start: Illegal value of intr" 1817 " threshold: %d\n"), soc->lrn_cache_threshold)); 1818 return SOC_E_CONFIG; 1819 } 1820 1821 /* By default, generate interrupt only when # cache entries equals 1822 * the programmed threshold value. For generating interrupt on each 1823 * learn event, user may set spn_L2XLRN_INTR_THRESHOLD to 1 1824 */ 1825 soc->lrn_cache_intr_ctl = 0x0; 1826 1827 soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, ®_val, 1828 CACHE_INTERRUPT_CTRLf, 1829 (uint32)(soc->lrn_cache_intr_ctl)); 1830 1831 soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, ®_val, 1832 CACHE_INTERRUPT_THRESHOLDf, 1833 (uint32)(soc->lrn_cache_threshold)); 1834 1835 } else { 1836 soc->lrn_cache_intr_ctl = 0x0; 1837 1838 /* In polled mode, set these fields to reset values */ 1839 soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, ®_val, 1840 CACHE_INTERRUPT_CTRLf, 1841 (uint32)(soc->lrn_cache_intr_ctl)); 1842 1843 soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, ®_val, 1844 CACHE_INTERRUPT_THRESHOLDf, 0x8); 1845 1846 } 1847 1848 SOC_IF_ERROR_RETURN(WRITE_L2_LEARN_COPY_CACHE_CTRLr(unit, reg_val)); 1849 1850 /* Clear all entries of L2 learn cache */ 1851 SOC_IF_ERROR_RETURN(soc_th3_l2_learn_cache_clear(unit)); 1852 1853 /* Reset cache status bits */ 1854 SOC_IF_ERROR_RETURN(soc_th3_l2_learn_cache_status_clear(unit)); 1855 1856 pri = soc_property_get(unit, spn_L2XLRN_THREAD_PRI, 1857 SOC_TH3_LRN_THREAD_PRI_DEFAULT); 1858 1859 /* Make sure that learn cache interrupt is enabled in CMICx, 1860 * later in the main initialization sequence 1861 */ 1862 soc->l2x_learn_pid = sal_thread_create(soc->l2x_learn_name, 1863 SAL_THREAD_STKSZ, pri, 1864 _soc_th3_l2_learn_process, 1865 INT_TO_PTR(unit)); 1866 1867 if (soc->l2x_learn_pid == SAL_THREAD_ERROR) { 1868 LOG_ERROR(BSL_LS_SOC_COMMON, 1869 (BSL_META_U(unit, 1870 "soc_th3_l2_learn_start: Could not start L2 learn" 1871 " thread\n"))); 1872 SOC_CONTROL_UNLOCK(unit); 1873 return SOC_E_MEMORY; 1874 } 1875 } 1876 1877 SOC_CONTROL_UNLOCK(unit); 1878 1879 /* More programming might be reqd depending on learn cache en/dis setting */ 1880 1881 return SOC_E_NONE; 1882 } 1883 1884 1885 int 1886 soc_th3_l2_learn_thread_stop(int unit) 1887 { 1888 soc_control_t *soc = SOC_CONTROL(unit); 1889 int rv = SOC_E_NONE; 1890 soc_timeout_t to; 1891 sal_usecs_t interval; 1892 1893 LOG_INFO(BSL_LS_SOC_ARL, (BSL_META_U(unit, "Stopping learn" 1894 " thread: unit=%d\n"), unit)); 1895 1896 /* Save interval to wait for thread to wake up again */ 1897 if (SAL_BOOT_SIMULATION) { 1898 /* Allow more time on simulation, similar to other devices */ 1899 interval = 30 * 1000000; 1900 } else { 1901 interval = 10 * 1000000; 1902 } 1903 1904 SOC_CONTROL_LOCK(unit); 1905 soc->l2x_learn_interval = 0; /* Request exit */ 1906 SOC_CONTROL_UNLOCK(unit); 1907 1908 if (soc->l2x_learn_pid != SAL_THREAD_ERROR) { 1909 1910 if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) { 1911 int i; 1912 1913 /* Disable learn cache interrupts from all pipes */ 1914 for (i = 8; i <= 15; i++) { 1915 (void)_soc_th3_lrn_cache_intr_configure(unit, i, 0); 1916 } 1917 } 1918 1919 /* Wake up thread so it will check the exit flag */ 1920 /*sal_sem_give(soc->arl_notify); Check if notification to learn thread 1921 is required */ 1922 1923 /* Give thread a few seconds to wake up and exit */ 1924 soc_timeout_init(&to, interval, 0); 1925 1926 LOG_INFO(BSL_LS_SOC_COMMON, 1927 (BSL_META_U(unit, "Learn thread stop: Wait may be longer if" 1928 " cfg polling interval is high, cfg interval = %u\n"), 1929 interval)); 1930 1931 while (soc->l2x_learn_pid != SAL_THREAD_ERROR) { 1932 if (soc_timeout_check(&to)) { 1933 LOG_ERROR(BSL_LS_SOC_L2, 1934 (BSL_META_U(unit, "Learn thread did not stop\n"))); 1935 rv = SOC_E_INTERNAL; 1936 break; 1937 } 1938 } 1939 } 1940 1941 return (rv); 1942 } 1943 1944 int 1945 soc_th3_l2_learn_thread_running(int unit, sal_usecs_t* interval) 1946 { 1947 soc_control_t *soc = SOC_CONTROL(unit); 1948 1949 if (soc->l2x_learn_pid != SAL_THREAD_ERROR) { 1950 if (interval != NULL) { 1951 *interval = soc->l2x_learn_interval; 1952 } 1953 } 1954 1955 return(soc->l2x_learn_pid != SAL_THREAD_ERROR); 1956 } 1957 #endif /* BCM_XGS_SWITCH_SUPPORT */ 1958 #endif /* BCM_TOMAHAWK3_SUPPORT */