l2xmsg.c (61494B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: l2xmsg.c 8 * Purpose: Provide a reliable stream of L2 insert/delete messages. 9 * 10 * This module monitors the L2X table for changes and performs callbacks 11 * for each insert, delete, or port movement that is detected. 12 * 13 * There is a time lag from the actual table change to the callback 14 * because the l2xmsg task scans the L2X table only periodically. 15 */ 16 17 #include <sal/core/libc.h> 18 #include <shared/alloc.h> 19 #include <shared/bsl.h> 20 #include <sal/core/spl.h> 21 #include <sal/core/time.h> 22 23 #include <soc/mem.h> 24 #include <soc/debug.h> 25 #include <soc/cm.h> 26 #include <soc/drv.h> 27 #include <soc/l2x.h> 28 #if defined(BCM_TRIDENT2_SUPPORT) 29 #include <soc/trident2.h> 30 #endif 31 #if defined(BCM_TOMAHAWK_SUPPORT) 32 #include <soc/tomahawk.h> 33 #endif 34 #if defined(BCM_MONTEREY_SUPPORT) 35 #include <soc/monterey.h> 36 #endif 37 #if defined(BCM_APACHE_SUPPORT) 38 #include <soc/apache.h> 39 #endif 40 #if defined(BCM_HURRICANE3_SUPPORT) 41 #include <soc/hurricane3.h> 42 #endif 43 #if defined(BCM_TOMAHAWK3_SUPPORT) 44 #include <soc/tomahawk3.h> 45 #endif 46 #ifdef BCM_XGS_SWITCH_SUPPORT 47 48 #define L2X_ENTRY_EQL(unit, e1, e2, sz, bits) \ 49 (!_soc_mem_cmp_l2x_sync(unit, (void *) e1, (void *) e2, sz, bits)) 50 51 #define L2X_IS_VALID_ENTRY(_u, _mem, _e, bitf) \ 52 soc_mem_field32_get((_u), (_mem), (_e), bitf) 53 54 #ifdef BCM_TRIUMPH3_SUPPORT 55 extern int 56 soc_tr3_l2x_running(int unit, uint32 *flags, sal_usecs_t *interval); 57 extern int 58 soc_tr3_l2x_start(int unit, uint32 flags, sal_usecs_t interval); 59 extern int 60 soc_tr3_l2x_stop(int unit); 61 #endif 62 63 STATIC void _soc_l2x_thread(void *unit_vp); 64 65 /**************************************************************************** 66 * 67 * L2X Message Registration 68 */ 69 70 #define L2X_CB_MAX 3 71 72 typedef struct l2x_cb_entry_s { 73 soc_l2x_cb_fn fn; 74 void *fn_data; 75 } l2x_cb_entry_t; 76 77 typedef struct l2x_data_s { 78 l2x_cb_entry_t cb[L2X_CB_MAX]; 79 int cb_count; 80 soc_mem_t l2mem; 81 int entry_bytes; 82 int entry_words; 83 uint32 *shadow_tab; 84 uint32 *chunk_buf; 85 SHR_BITDCL *delete_map; 86 SHR_BITDCL *callback_map; 87 SHR_BITDCL *sr_ref_map; 88 } l2x_data_t; 89 90 STATIC l2x_data_t l2x_data[SOC_MAX_NUM_DEVICES]; 91 92 #define L2X_ENTRY_CALLBACK_SET(_u_, _index_) \ 93 { \ 94 SHR_BITSET(l2x_data[(_u_)].callback_map, (_index_)); \ 95 LOG_VERBOSE(BSL_LS_SOC_ARL, \ 96 (BSL_META_U(unit, \ 97 "set_entry_callback: u:%d i=%d\n"), \ 98 _u_, _index_)); \ 99 } 100 101 #define L2X_ENTRY_DELETED_SET(_u_, _index_) \ 102 { \ 103 SHR_BITSET(l2x_data[(_u_)].delete_map, (_index_)); \ 104 LOG_VERBOSE(BSL_LS_SOC_ARL, \ 105 (BSL_META_U(unit, \ 106 "set_entry_deleted: u:%d i=%d\n"), \ 107 _u_, _index_)); \ 108 } 109 110 #define L2X_IS_CPU_DELETED(_u_, _index_) \ 111 SHR_BITGET(l2x_data[(_u_)].delete_map, (_index_)) 112 113 #define SOC_L2X_TGID_TRUNK_INDICATOR(unit) \ 114 SOC_IS_RAVEN(unit) ? 0x40 : 0x20 115 #define SOC_L2X_TGID_PORT_TRUNK_MASK 0x1f 116 #define SOC_L2X_TGID_PORT_TRUNK_MASK_HI 0x60 117 118 /* Get L2x mode from soc property */ 119 int 120 soc_l2x_mode_cfg_get(int unit) 121 { 122 int mode, def_mode = L2MODE_POLL; 123 124 if (!(SAL_BOOT_BCMSIM || SAL_BOOT_XGSSIM) && 125 soc_feature(unit, soc_feature_l2_modfifo) && 126 soc_feature(unit, soc_feature_l2_modfifo_def)) { 127 def_mode = L2MODE_FIFO; 128 } 129 130 mode = soc_property_get(unit, spn_L2XMSG_MODE, def_mode); 131 132 return mode; 133 } 134 135 /* 136 * Function: 137 * soc_l2x_register 138 * Purpose: 139 * Register a callback routine to be notified of all inserts, 140 * deletes, and updates to the L2X table. 141 * Parameters: 142 * unit - StrataSwitch unit number 143 * fn - Callback function to register 144 * fn_data - Extra data passed to callback function 145 * Returns: 146 * SOC_E_NONE - Success 147 * SOC_E_MEMORY - Too many callbacks registered 148 */ 149 150 int 151 soc_l2x_register(int unit, soc_l2x_cb_fn fn, void *fn_data) 152 { 153 l2x_data_t *ad = &l2x_data[unit]; 154 int i; 155 #ifdef BCM_XGS3_SWITCH_SUPPORT 156 soc_control_t *soc = SOC_CONTROL(unit); 157 int mode; 158 159 mode = soc_l2x_mode_cfg_get(unit); 160 161 if ((mode == L2MODE_FIFO) && !soc_feature(unit, soc_feature_l2_modfifo)) { 162 mode = L2MODE_POLL; 163 } 164 165 if ((mode == L2MODE_POLL) && soc->l2x_external) { 166 /* Not supported for external L2 memory configuration. */ 167 return SOC_E_UNAVAIL; 168 } 169 #endif 170 if (!soc_feature(unit, soc_feature_arl_hashed)) { 171 return SOC_E_UNAVAIL; 172 } 173 174 if (ad->cb_count >= L2X_CB_MAX) { 175 return SOC_E_MEMORY; 176 } 177 178 for (i = 0; i < ad->cb_count; i++) { 179 if ((ad->cb[i].fn == fn && 180 ad->cb[i].fn_data == fn_data)) { 181 return SOC_E_NONE; /* Already registered */ 182 } 183 } 184 185 ad->cb[ad->cb_count].fn = fn; 186 ad->cb[ad->cb_count].fn_data = fn_data; 187 188 ad->cb_count++; 189 190 return SOC_E_NONE; 191 } 192 193 /* 194 * Function: 195 * soc_l2x_unregister 196 * Purpose: 197 * Unregister a callback routine; requires same args as when registered 198 * Parameters: 199 * unit - StrataSwitch unit number 200 * fn - Callback function to unregister; NULL to unregister all 201 * fn_data - Extra data passed to callback function; 202 * must match registered value unless fn is NULL 203 * Returns: 204 * SOC_E_NONE - Success 205 * SOC_E_NOT_FOUND - Matching registered routine not found 206 */ 207 208 int 209 soc_l2x_unregister(int unit, soc_l2x_cb_fn fn, void *fn_data) 210 { 211 l2x_data_t *ad = &l2x_data[unit]; 212 int i; 213 if (fn == NULL) { 214 ad->cb_count = 0; 215 return SOC_E_NONE; 216 } 217 218 for (i = 0; i < ad->cb_count; i++) { 219 if ((ad->cb[i].fn == fn && 220 ad->cb[i].fn_data == fn_data)) { 221 222 for (ad->cb_count--; i < ad->cb_count; i++) { 223 sal_memcpy(&ad->cb[i], &ad->cb[i + 1], 224 sizeof (l2x_cb_entry_t)); 225 } 226 227 return SOC_E_NONE; 228 } 229 } 230 231 return SOC_E_NOT_FOUND; 232 } 233 234 /* 235 * Function: 236 * soc_l2x_callback 237 * Purpose: 238 * Routine to execute all callbacks on the list. 239 * Parameters: 240 * unit - unit number. 241 * entry_del - deleted or updated entry, NULL if none. 242 * entry_add - added or updated entry, NULL if none. 243 */ 244 245 void 246 soc_l2x_callback(int unit, int flags, l2x_entry_t *entry_del, l2x_entry_t *entry_add) 247 { 248 l2x_data_t *ad = &l2x_data[unit]; 249 int i; 250 for (i = 0; i < ad->cb_count; i++) { 251 (*ad->cb[i].fn)(unit, flags, entry_del, entry_add, ad->cb[i].fn_data); 252 } 253 } 254 255 /* 256 * Function: 257 * soc_l2x_running 258 * Purpose: 259 * Determine the L2X sync thread running parameters 260 * Parameters: 261 * unit - unit number. 262 * flags (OUT) - if non-NULL, receives the current flag settings 263 * interval (OUT) - if non-NULL, receives the current pass interval 264 * Returns: 265 * Boolean; TRUE if L2X sync thread is running 266 */ 267 268 int 269 soc_l2x_running(int unit, uint32 *flags, sal_usecs_t *interval) 270 { 271 soc_control_t *soc = SOC_CONTROL(unit); 272 #ifdef BCM_TRIUMPH3_SUPPORT 273 if SOC_IS_TRIUMPH3(unit) { 274 return soc_tr3_l2x_running(unit, flags, interval); 275 } 276 #endif 277 if (soc->l2x_pid != SAL_THREAD_ERROR) { 278 if (flags != NULL) { 279 *flags = soc->l2x_flags; 280 } 281 if (interval != NULL) { 282 *interval = soc->l2x_interval; 283 } 284 } 285 286 return(soc->l2x_pid != SAL_THREAD_ERROR); 287 } 288 289 /* It only can handle delete so far */ 290 int 291 _soc_l2x_sync_replace(int unit, l2x_entry_t *l2x_match_data, 292 l2x_entry_t *l2x_match_mask, uint32 flags) 293 { 294 soc_control_t *soc = SOC_CONTROL(unit); 295 uint32 *entry, *match_data, *match_mask; 296 int index_max, entry_words, idx, i; 297 298 if (soc->l2x_pid == SAL_THREAD_ERROR) { 299 /* thread not running */ 300 return SOC_E_NONE; 301 } 302 303 entry = l2x_data[unit].shadow_tab; 304 if (entry == NULL) { 305 return SOC_E_NONE; 306 } 307 match_data = (uint32 *)l2x_match_data; 308 match_mask = (uint32 *)l2x_match_mask; 309 310 index_max = soc_mem_index_max(unit, L2Xm); 311 entry_words = soc_mem_entry_words(unit, L2Xm); 312 313 (void)sal_sem_take(soc->l2x_lock, sal_sem_FOREVER); 314 315 for (idx = 0; idx <= index_max; idx++, entry += entry_words) { 316 for (i = 0; i < entry_words; i++) { 317 if ((entry[i] ^ match_data[i]) & match_mask[i]) { 318 break; 319 } 320 } 321 if (i != entry_words) { 322 continue; 323 } 324 soc_l2x_sync_delete(unit, entry, idx, flags); 325 } 326 327 (void)sal_sem_give(soc->l2x_lock); 328 329 return SOC_E_NONE; 330 } 331 332 /* 333 * Function: 334 * _soc_l2x_sync_delete_by 335 * Purpose: 336 * Search all the entries in the shadow table based on the 337 * criteria and mark them as deleted. 338 * Parameters: 339 * Returns: 340 * SOC_E_XX 341 */ 342 int 343 _soc_l2x_sync_delete_by(int unit, uint32 mod, uint32 port, 344 uint16 vid, uint32 tid, int vfi, 345 uint32 flags, uint32 search_key) 346 { 347 soc_control_t *soc = SOC_CONTROL(unit); 348 int index, max_index, stl2; 349 uint32 *entry = NULL; 350 soc_field_t valid_bit = VALIDf; 351 int trunk128, rv = SOC_E_NONE; 352 int static_bit_val; 353 uint32 tgid_hi = 0, tgid_lo = 0; 354 uint32 tgid_val, port_val, mod_id; 355 vlan_id_t vlan_id; 356 357 #ifdef BCM_TRIUMPH3_SUPPORT 358 if SOC_IS_TRIUMPH3(unit) { 359 return _soc_tr3_l2x_sync_delete_by(unit, mod, port, vid, tid, vfi, 360 flags, search_key); 361 } 362 #endif 363 364 365 if (soc->l2x_pid == SAL_THREAD_ERROR) { 366 /* thread not running */ 367 return SOC_E_NONE; 368 } 369 370 max_index = soc_mem_index_max(unit, l2x_data[unit].l2mem); 371 372 stl2 = (flags & SOC_L2X_INC_STATIC) ? 1 : 0; 373 374 (void)sal_sem_take(soc->l2x_lock, sal_sem_FOREVER); 375 376 entry = l2x_data[unit].shadow_tab; 377 if (!entry) { 378 goto exit; 379 } 380 381 if (search_key == SOC_L2X_PORTMOD_DEL) { 382 for (index = 0; index < max_index; index++, 383 entry += l2x_data[unit].entry_words) { 384 if (!L2X_IS_VALID_ENTRY(unit, l2x_data[unit].l2mem, 385 entry, valid_bit)) { 386 continue; 387 } 388 if (soc_feature(unit, soc_feature_trunk_group_overlay)) { 389 if (soc_mem_field32_get(unit, l2x_data[unit].l2mem, entry, 390 Tf)) { 391 continue; 392 } 393 port_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 394 entry, PORT_NUMf); 395 } else { 396 port_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 397 entry, TGID_PORTf); 398 } 399 mod_id = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 400 entry, MODULE_IDf); 401 if ((port != port_val) || (mod != mod_id)) { 402 continue; 403 } 404 405 static_bit_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 406 entry, STATIC_BITf); 407 if (stl2 || (stl2 == static_bit_val)) { 408 soc_l2x_sync_delete(unit, entry, index, flags); 409 } 410 } 411 goto exit; 412 } 413 414 if (search_key == SOC_L2X_VLAN_DEL) { 415 for (index = 0; index < max_index; index++, 416 entry += l2x_data[unit].entry_words) { 417 if (!L2X_IS_VALID_ENTRY(unit, l2x_data[unit].l2mem, 418 entry, valid_bit)) { 419 continue; 420 } 421 vlan_id = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 422 entry, VLAN_IDf); 423 if (vid != vlan_id) { 424 continue; 425 } 426 427 static_bit_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 428 entry, STATIC_BITf); 429 if (stl2 || (stl2 == static_bit_val)) { 430 soc_l2x_sync_delete(unit, entry, index, flags); 431 } 432 } 433 goto exit; 434 } 435 436 if (search_key == SOC_L2X_VFI_DEL) { 437 #ifdef BCM_TRIUMPH_SUPPORT 438 int vfi_id; 439 for (index = 0; index < max_index; index++, 440 entry += l2x_data[unit].entry_words) { 441 if (!L2X_IS_VALID_ENTRY(unit, l2x_data[unit].l2mem, 442 entry, valid_bit)) { 443 continue; 444 } 445 vfi_id = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 446 entry, VFIf); 447 if (vfi != vfi_id) { 448 continue; 449 } 450 451 static_bit_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 452 entry, STATIC_BITf); 453 if (stl2 || (stl2 == static_bit_val)) { 454 soc_l2x_sync_delete(unit, entry, index, flags); 455 } 456 } 457 #endif 458 goto exit; 459 } 460 461 if (search_key == SOC_L2X_PORTMOD_VLAN_DEL) { 462 for (index = 0; index < max_index; index++, 463 entry += l2x_data[unit].entry_words) { 464 if (!L2X_IS_VALID_ENTRY(unit, l2x_data[unit].l2mem, 465 entry, valid_bit)) { 466 continue; 467 } 468 if (soc_feature(unit, soc_feature_trunk_group_overlay)) { 469 if (soc_mem_field32_get(unit, l2x_data[unit].l2mem, entry, 470 Tf)) { 471 continue; 472 } 473 port_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 474 entry, PORT_NUMf); 475 } else { 476 port_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 477 entry, TGID_PORTf); 478 } 479 mod_id = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 480 entry, MODULE_IDf); 481 vlan_id = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 482 entry, VLAN_IDf); 483 if ((vid != vlan_id) ||(port != port_val) ||(mod != mod_id)) { 484 continue; 485 } 486 487 static_bit_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 488 entry, STATIC_BITf); 489 if (stl2 || (stl2 == static_bit_val)) { 490 soc_l2x_sync_delete(unit, entry, index, flags); 491 } 492 } 493 goto exit; 494 } 495 496 if ((search_key == SOC_L2X_TRUNK_DEL) || 497 (search_key == SOC_L2X_TRUNK_VLAN_DEL)) { 498 trunk128 = soc_feature(unit, soc_feature_trunk_extended); 499 500 if (trunk128) { 501 if (!soc_feature(unit, soc_feature_trunk_group_overlay)) { 502 tgid_hi = (tid & SOC_L2X_TGID_PORT_TRUNK_MASK_HI) >> 503 SOC_TRUNK_BIT_POS(unit); 504 tgid_lo = tid & SOC_L2X_TGID_PORT_TRUNK_MASK; 505 tgid_lo |= SOC_L2X_TGID_TRUNK_INDICATOR(unit); 506 } 507 508 for (index = 0; index < max_index; index++, 509 entry += l2x_data[unit].entry_words) { 510 if (!L2X_IS_VALID_ENTRY(unit, l2x_data[unit].l2mem, 511 entry, valid_bit)) { 512 continue; 513 } 514 if (soc_feature(unit, soc_feature_trunk_group_overlay)) { 515 if (!soc_mem_field32_get(unit, l2x_data[unit].l2mem, 516 entry, Tf) || 517 soc_mem_field32_get(unit, l2x_data[unit].l2mem, 518 entry, TGIDf) != tid) { 519 continue; 520 } 521 if ((search_key == SOC_L2X_TRUNK_VLAN_DEL) && 522 (vid != soc_mem_field32_get(unit, l2x_data[unit].l2mem, 523 entry, VLAN_IDf))) { 524 continue; 525 526 } 527 } else { 528 port_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 529 entry, TGID_PORTf); 530 tgid_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 531 entry, MODULE_IDf); 532 533 if ((tgid_lo != port_val) ||(tgid_hi != tgid_val)) { 534 continue; 535 } 536 if ((search_key == SOC_L2X_TRUNK_VLAN_DEL) && 537 (vid != soc_mem_field32_get(unit, l2x_data[unit].l2mem, 538 entry, VLAN_IDf))) { 539 continue; 540 541 } 542 } 543 544 static_bit_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 545 entry, STATIC_BITf); 546 if (stl2 || (stl2 == static_bit_val)) { 547 soc_l2x_sync_delete(unit, entry, index, flags); 548 } 549 } 550 } else { 551 tid &= SOC_L2X_TGID_PORT_TRUNK_MASK; 552 tid |= SOC_L2X_TGID_TRUNK_INDICATOR(unit); 553 554 for (index = 0; index < max_index; index++, 555 entry += l2x_data[unit].entry_words) { 556 if (!L2X_IS_VALID_ENTRY(unit, l2x_data[unit].l2mem, 557 entry, valid_bit)) { 558 continue; 559 } 560 port_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 561 entry, TGID_PORTf); 562 if (tid != port_val ) { 563 continue; 564 } 565 if ((search_key == SOC_L2X_TRUNK_VLAN_DEL) && 566 (vid != soc_mem_field32_get(unit, l2x_data[unit].l2mem, 567 entry, VLAN_IDf))) { 568 continue; 569 570 } 571 static_bit_val = soc_mem_field32_get(unit, l2x_data[unit].l2mem, 572 entry, STATIC_BITf); 573 if (stl2 || (stl2 == static_bit_val)) { 574 soc_l2x_sync_delete(unit, entry, index, flags); 575 } 576 } 577 } 578 goto exit; 579 } 580 581 rv = SOC_E_PARAM; 582 583 exit: 584 (void)sal_sem_give(soc->l2x_lock); 585 return rv; 586 } 587 588 /* 589 * Function: 590 * soc_l2x_sync_delete 591 * Purpose: 592 * sync the SW shadow copy for the deleted entry from HW. 593 * Parameters: 594 * unit - unit number. 595 * del_entry - entry deleted from the HW l2X memory 596 * Returns: 597 * SOC_E_XX 598 */ 599 600 int 601 soc_l2x_sync_delete(int unit, uint32 *del_entry, int index, uint32 flags) 602 { 603 soc_control_t *soc = SOC_CONTROL(unit); 604 int max_index; 605 uint32 *tab_p; 606 607 LOG_VERBOSE(BSL_LS_SOC_ARL, 608 (BSL_META_U(unit, 609 "soc_l2x_sync_delete: unit=%d index=%d\n"), 610 unit, index)); 611 612 if (soc->l2x_pid == SAL_THREAD_ERROR) { 613 /* thread not running */ 614 return SOC_E_NONE; 615 } 616 617 if (l2x_data[unit].l2mem == INVALIDm) { 618 return SOC_E_NONE; 619 } 620 621 max_index = soc_mem_index_max(unit, l2x_data[unit].l2mem); 622 if (index > max_index) { 623 #ifdef BCM_HURRICANE3_SUPPORT 624 if ((SOC_IS_HURRICANE3(unit) || SOC_IS_GREYHOUND2(unit)) && 625 (index & SOC_L2X_GEN_RESP_INDEX_L2_OVERFLOW(unit))) { 626 /* Entry learnt in L2_ENTRY_OVERFLOW */ 627 return SOC_E_NONE; 628 } 629 #endif /* BCM_HURRICANE3_SUPPORT */ 630 return SOC_E_PARAM; 631 } 632 if (l2x_data[unit].shadow_tab == NULL) { 633 LOG_WARN(BSL_LS_SOC_ARL, 634 (BSL_META_U(unit, 635 "soc_l2x_sync_delete: l2x_data[%d].shadow_tab is NULL!!!\n"), 636 unit)); 637 return SOC_E_NONE; 638 } 639 /* 640 * validate that the entry in the shadow copy is same as the entry 641 * deleted. 642 */ 643 tab_p = l2x_data[unit].shadow_tab + (index * l2x_data[unit].entry_words); 644 if (L2X_ENTRY_EQL(unit, tab_p, del_entry, 645 WORDS2BYTES(l2x_data[unit].entry_words), 0)) { 646 L2X_ENTRY_DELETED_SET(unit, index); 647 /* Bitmap that indicates to suppress callback */ 648 if ((flags & SOC_L2X_NO_CALLBACKS)) { 649 L2X_ENTRY_CALLBACK_SET(unit, index); 650 } 651 } 652 653 return SOC_E_NONE; 654 } 655 656 /* 657 * Function: 658 * soc_l2x_sync_delete_by_port 659 * Purpose: 660 * sync the SW shadow copy for the deleted entryies based on port. 661 * Parameters: 662 * unit - unit number. 663 * port - port number. 664 * Returns: 665 * SOC_E_XX 666 */ 667 668 int 669 soc_l2x_sync_delete_by_port(int unit, int mod, soc_port_t port, 670 uint32 flags) 671 { 672 return _soc_l2x_sync_delete_by(unit, mod, port, 0, 0, 0, 673 flags, SOC_L2X_PORTMOD_DEL); 674 } 675 676 /* 677 * Function: 678 * soc_l2x_sync_delete_by_trunk 679 * Purpose: 680 * sync the SW shadow copy for the deleted entryies based on trunk id. 681 * Parameters: 682 * unit - unit number. 683 * port - port number. 684 * Returns: 685 * SOC_E_XX 686 */ 687 688 int 689 soc_l2x_sync_delete_by_trunk(int unit, int tid, uint32 flags) 690 { 691 return _soc_l2x_sync_delete_by(unit, 0, 0, 0, tid, 0, 692 flags, SOC_L2X_TRUNK_DEL); 693 } 694 695 /* 696 * Function: 697 * soc_l2x_start 698 * Purpose: 699 * Initialize shadow table and start L2X thread to maintain it 700 * Parameters: 701 * unit - unit number. 702 * flags - SOC_L2X_FLAG_xxx 703 * interval - time between resynchronization passes 704 * Returns: 705 * SOC_E_MEMORY if can't create thread. 706 */ 707 708 int 709 soc_l2x_start(int unit, uint32 flags, sal_usecs_t interval) 710 { 711 soc_control_t *soc = SOC_CONTROL(unit); 712 int pri; 713 #ifdef BCM_XGS3_SWITCH_SUPPORT 714 int mode; 715 #endif 716 #ifdef BCM_TRIUMPH3_SUPPORT 717 if SOC_IS_TRIUMPH3(unit) { 718 return soc_tr3_l2x_start(unit, flags, interval); 719 } 720 #endif 721 LOG_INFO(BSL_LS_SOC_ARL, 722 (BSL_META_U(unit, 723 "soc_l2x_start: unit=%d flags=0x%x interval=%d\n"), 724 unit, flags, interval)); 725 726 if (!soc_feature(unit, soc_feature_arl_hashed)) { 727 return SOC_E_UNAVAIL; 728 } 729 730 if (soc->l2x_interval != 0) { 731 SOC_IF_ERROR_RETURN(soc_l2x_stop(unit)); 732 } 733 734 sal_snprintf(soc->l2x_name, sizeof (soc->l2x_name), 735 "bcmL2X.%d", unit); 736 737 if (soc->l2x_pid == SAL_THREAD_ERROR) { 738 pri = soc_property_get(unit, spn_L2XMSG_THREAD_PRI, 50); 739 #ifdef BCM_XGS3_SWITCH_SUPPORT 740 mode = soc_l2x_mode_cfg_get(unit); 741 742 if (soc_feature(unit, soc_feature_l2_modfifo) && (mode == L2MODE_FIFO)) { 743 744 SOC_CONTROL_LOCK(unit); 745 soc->l2x_mode = mode; 746 soc->l2x_flags = flags; 747 soc->l2x_interval = interval; 748 749 if (interval == 0) { 750 SOC_CONTROL_UNLOCK(unit); 751 return SOC_E_NONE; 752 } 753 #ifdef BCM_CMICX_SUPPORT 754 if (SOC_IS_TRIDENT3X(unit)) { 755 _soc_cmicx_l2mod_start(unit, flags, interval); 756 } else 757 #endif 758 #ifdef BCM_CMICM_SUPPORT 759 if (SOC_IS_TD2_TT2(unit) || SOC_IS_KATANA2(unit) || 760 soc_feature(unit, soc_feature_fifo_dma_hu2)) { 761 _soc_l2mod_start(unit, flags, interval); 762 } else 763 #endif 764 { 765 soc_l2mod_start(unit, flags, interval); 766 } 767 SOC_CONTROL_UNLOCK(unit); 768 l2x_data[unit].l2mem = INVALIDm; 769 } else 770 #endif 771 { 772 if (soc->l2x_external) { 773 /* Not supported for external L2 memory configuration. */ 774 return SOC_E_NONE; 775 } 776 777 SOC_CONTROL_LOCK(unit); 778 soc->l2x_mode = L2MODE_POLL; 779 soc->l2x_flags = flags; 780 soc->l2x_interval = interval; 781 782 if (interval == 0) { 783 SOC_CONTROL_UNLOCK(unit); 784 return SOC_E_NONE; 785 } 786 soc->l2x_pid = sal_thread_create(soc->l2x_name, 787 SAL_THREAD_STKSZ, 788 pri, 789 _soc_l2x_thread, 790 INT_TO_PTR(unit)); 791 if (soc->l2x_pid == SAL_THREAD_ERROR) { 792 LOG_ERROR(BSL_LS_SOC_L2, 793 (BSL_META_U(unit, 794 "soc_l2x_start: Could not start L2X thread\n"))); 795 SOC_CONTROL_UNLOCK(unit); 796 return SOC_E_MEMORY; 797 } 798 SOC_CONTROL_UNLOCK(unit); 799 } 800 } 801 802 return SOC_E_NONE; 803 } 804 805 /* 806 * Function: 807 * soc_l2x_stop 808 * Purpose: 809 * Stop L2X-related thread 810 * Parameters: 811 * unit - unit number. 812 * Returns: 813 * SOC_E_XXX 814 */ 815 816 int 817 soc_l2x_stop(int unit) 818 { 819 soc_control_t *soc = SOC_CONTROL(unit); 820 int rv = SOC_E_NONE; 821 soc_timeout_t to; 822 #ifdef BCM_XGS3_SWITCH_SUPPORT 823 int mode; 824 #endif 825 #ifdef BCM_TRIUMPH3_SUPPORT 826 if SOC_IS_TRIUMPH3(unit) { 827 return soc_tr3_l2x_stop(unit); 828 } 829 #endif 830 LOG_INFO(BSL_LS_SOC_ARL, 831 (BSL_META_U(unit, 832 "soc_l2x_stop: unit=%d\n"), unit)); 833 834 SOC_CONTROL_LOCK(unit); 835 soc->l2x_interval = 0; /* Request exit */ 836 SOC_CONTROL_UNLOCK(unit); 837 838 if (soc->l2x_pid != SAL_THREAD_ERROR) { 839 /* Wake up thread so it will check the exit flag */ 840 #ifdef BCM_XGS3_SWITCH_SUPPORT 841 mode = soc_l2x_mode_cfg_get(unit); 842 if (soc_feature(unit, soc_feature_l2_modfifo) && 843 (mode == L2MODE_FIFO)) { 844 #ifdef BCM_CMICX_SUPPORT 845 if (SOC_IS_TRIDENT3X(unit)) { 846 _soc_cmicx_l2mod_stop(unit); 847 } else 848 #endif 849 #ifdef BCM_CMICM_SUPPORT 850 if (SOC_IS_TD2_TT2(unit) || SOC_IS_KATANA2(unit) || 851 soc_feature(unit, soc_feature_fifo_dma_hu2)) { 852 _soc_l2mod_stop(unit); 853 } else 854 #endif 855 { 856 soc_l2mod_stop(unit); 857 } 858 } else 859 #endif 860 { 861 if (NULL != soc->l2x_notify) { 862 sal_sem_give(soc->l2x_notify); 863 } 864 } 865 866 /* Give thread a few seconds to wake up and exit */ 867 if (SAL_BOOT_SIMULATION) { 868 soc_timeout_init(&to, 30 * 1000000, 0); 869 } else { 870 soc_timeout_init(&to, 10 * 1000000, 0); 871 } 872 873 while (soc->l2x_pid != SAL_THREAD_ERROR) { 874 if (soc_timeout_check(&to)) { 875 LOG_ERROR(BSL_LS_SOC_L2, 876 (BSL_META_U(unit, 877 "soc_l2x_stop: thread will not exit\n"))); 878 rv = SOC_E_INTERNAL; 879 break; 880 } 881 } 882 } 883 884 return rv; 885 } 886 887 #if defined(BCM_TRIDENT2_SUPPORT) 888 STATIC int 889 _soc_trident2_l2x_sync_multi_buckets(int unit, uint32 *new_entry, 890 uint8 shadow_hit_bits, int *mac_moved) 891 { 892 int num_banks = 0; 893 int bank_idx = 0; 894 int hw_bank = 0; 895 int bucket_base = 0; 896 int entry_idx = 0; 897 int delete_marked, callback_suppress; 898 uint32 *shadow_entry = NULL; 899 uint32 bucket; 900 soc_control_t *soc = SOC_CONTROL(unit); 901 soc_mem_t l2mem = l2x_data[unit].l2mem; 902 int entry_words = l2x_data[unit].entry_words; 903 SHR_BITDCL *delete_map = l2x_data[unit].delete_map; 904 SHR_BITDCL *callback_map = l2x_data[unit].callback_map; 905 soc_field_t l2x_vld = VALIDf; 906 int bucket_size = 4; /* 4 entries per bucket */ 907 908 if (soc_feature(unit, soc_feature_base_valid)) { 909 l2x_vld = BASE_VALIDf; 910 } 911 912 SOC_IF_ERROR_RETURN( 913 soc_trident2_hash_bank_count_get(unit, l2mem, &num_banks)); 914 for (bank_idx = 0; bank_idx < num_banks; bank_idx++) { 915 #ifdef BCM_APACHE_SUPPORT 916 if (SOC_IS_APACHE(unit)) { 917 SOC_IF_ERROR_RETURN 918 (soc_apache_hash_bank_number_get(unit, l2mem, bank_idx, 919 &hw_bank)); 920 SOC_IF_ERROR_RETURN 921 (soc_ap_hash_bucket_get(unit, l2mem, hw_bank, new_entry, 922 &bucket)); 923 bucket_base = soc_ap_hash_index_get(unit, l2mem, hw_bank, bucket); 924 } else 925 #endif /* BCM_APACHE_SUPPORT */ 926 #ifdef BCM_TOMAHAWK_SUPPORT 927 if (SOC_IS_TOMAHAWKX(unit)) { 928 #if defined(BCM_TOMAHAWK3_SUPPORT) 929 if (SOC_IS_TOMAHAWK3(unit)) { 930 SOC_IF_ERROR_RETURN 931 (soc_tomahawk3_hash_bank_number_get(unit, l2mem, bank_idx, 932 &hw_bank)); 933 SOC_IF_ERROR_RETURN 934 (soc_tomahawk3_hash_bucket_get(unit, l2mem, hw_bank, 935 new_entry, &bucket)); 936 bucket_base = soc_tomahawk3_hash_index_get(unit, l2mem, hw_bank, 937 bucket); 938 } else 939 #endif /* BCM_TOMAHAWK3_SUPPORT */ 940 { 941 SOC_IF_ERROR_RETURN 942 (soc_tomahawk_hash_bank_number_get(unit, l2mem, bank_idx, 943 &hw_bank)); 944 SOC_IF_ERROR_RETURN 945 (soc_th_hash_bucket_get(unit, l2mem, hw_bank, new_entry, 946 &bucket)); 947 bucket_base = soc_th_hash_index_get(unit, l2mem, hw_bank, 948 bucket); 949 } 950 } else 951 #endif /* BCM_TOMAHAWK_SUPPORT */ 952 { 953 SOC_IF_ERROR_RETURN 954 (soc_trident2_hash_bank_number_get(unit, l2mem, bank_idx, 955 &hw_bank)); 956 SOC_IF_ERROR_RETURN 957 (soc_hash_bucket_get(unit, l2mem, hw_bank, new_entry, 958 &bucket)); 959 bucket_base = soc_hash_index_get(unit, l2mem, hw_bank, bucket); 960 } 961 shadow_entry = l2x_data[unit].shadow_tab + (bucket_base * entry_words); 962 for (entry_idx = 0; entry_idx < bucket_size; entry_idx++) { 963 if (soc_mem_field32_get(unit, l2mem, shadow_entry, l2x_vld) && 964 soc_mem_compare_key(unit, l2mem, new_entry, shadow_entry) == 0) { 965 break; 966 } 967 shadow_entry += entry_words; 968 } 969 if (entry_idx < bucket_size) { 970 break; 971 } 972 } 973 974 if (bank_idx < num_banks) { 975 /* This MAC existed previously in the bucket of another bank. 976 * Check if everything at the new location is the same (ignoring 977 * hit bit). If so, there is no delete or change processing. 978 */ 979 if (!(shadow_hit_bits & L2X_SHADOW_HIT_BITS)) { 980 if (!(shadow_hit_bits & L2X_SHADOW_HIT_DST)) { 981 soc_L2Xm_field32_set(unit, shadow_entry, HITDAf, 982 soc_L2Xm_field32_get(unit, new_entry, 983 HITDAf)); 984 } 985 if (!(shadow_hit_bits & L2X_SHADOW_HIT_SRC)) { 986 soc_L2Xm_field32_set(unit, shadow_entry, HITSAf, 987 soc_L2Xm_field32_get(unit, new_entry, 988 HITSAf)); 989 } 990 } 991 /* Check if the entry has been deleted and is marked for 992 * delete. 993 */ 994 if (SOC_L2_DEL_SYNC_LOCK(soc) < 0) { 995 return SOC_E_RESOURCE; 996 } 997 delete_marked = SHR_BITGET(delete_map, bucket_base + entry_idx); 998 callback_suppress = SHR_BITGET(callback_map, bucket_base + entry_idx); 999 SOC_L2_DEL_SYNC_UNLOCK(soc); 1000 if (L2X_ENTRY_EQL(unit, shadow_entry, new_entry, 1001 WORDS2BYTES(l2x_data[unit].entry_words), 0)) { 1002 if (delete_marked) { 1003 /* Entry got learnt again after delete */ 1004 if (callback_suppress) { 1005 /* Indicate re-learn */ 1006 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *)new_entry); 1007 } else { 1008 /* Indicate delete and re-learn */ 1009 soc_l2x_callback(unit, 0, (l2x_entry_t *)shadow_entry, NULL); 1010 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *)new_entry); 1011 } 1012 } 1013 /* Invalid entry in shadow table */ 1014 soc_L2Xm_field32_set(unit, shadow_entry, l2x_vld, 0); 1015 *mac_moved = TRUE; 1016 return SOC_E_NONE; 1017 } 1018 /* Issue change (delete & insert) */ 1019 if (!callback_suppress) { 1020 soc_l2x_callback(unit, 0, (l2x_entry_t *)shadow_entry, 1021 (l2x_entry_t *)new_entry); 1022 } 1023 /* Invalid entry in the shadow table */ 1024 soc_L2Xm_field32_set(unit, shadow_entry, l2x_vld, 0); 1025 *mac_moved = TRUE; 1026 return SOC_E_NONE; 1027 } 1028 *mac_moved = FALSE; 1029 return SOC_E_NONE; 1030 } 1031 #endif 1032 1033 /* 1034 * Function: 1035 * _soc_l2x_sync_bucket 1036 * Purpose: 1037 * Compare the old contents of a hash bucket (8 entries) to the 1038 * next contents, make any ARL callbacks that are necessary, 1039 * and sync that bucket in the shadow table. 1040 * Parameters: 1041 * unit - StrataSwitch unit # 1042 * old_bucket - Previous bucket contents (SOC_L2X_BUCKET_SIZE entries) 1043 * new_bucket - New bucket contents (SOC_L2X_BUCKET_SIZE entries) 1044 * shadow_hit_bits - perform the notify callback even if 1045 * only hit bits change. 1046 * bucket_index - start index of bucket within chunk 1047 * p_chunk_del_map - pointer to chunk's delete map 1048 * Notes: 1049 * It's necessary to process deletions first, then insertions. 1050 * If an entry moves within the hash bucket, or some field such 1051 * as PORT changes, the application first receives a callback 1052 * for a deletion, and then for an insertion. 1053 */ 1054 1055 STATIC void 1056 _soc_l2x_sync_bucket(int unit, 1057 uint32 *old_bucket, 1058 uint32 *new_bucket, 1059 uint8 shadow_hit_bits, int bucket_index, 1060 SHR_BITDCL *p_chunk_del_map, 1061 SHR_BITDCL *p_chunk_cb_map, 1062 SHR_BITDCL *p_bucket_sr_map) 1063 { 1064 int io, in, delete_marked, callback_suppress; 1065 soc_field_t valid_bit = VALIDf; 1066 uint32 *old_p, *new_p; 1067 #if defined(BCM_TRIDENT2_SUPPORT) 1068 int rv = SOC_E_NONE; 1069 int mac_moved = 0; 1070 #endif 1071 #if defined(BCM_TRIDENT2PLUS_SUPPORT) || defined(BCM_APACHE_SUPPORT) 1072 int key_type = 0; 1073 int remote_trunk = 0; 1074 int trunk = 0; 1075 #endif 1076 #ifdef BCM_TSN_SUPPORT 1077 int sr_ref_updated = 0; 1078 #endif 1079 1080 /* 1081 * Process deletions, which will be any entry that was in the bucket 1082 * before but whose key is no longer found anywhere in the bucket, 1083 * and changes, which will be any entry whose key is still found but 1084 * whose associated data (port, etc.) has changed. 1085 */ 1086 1087 if (soc_feature(unit, soc_feature_base_valid)) { 1088 valid_bit = BASE_VALIDf; 1089 } 1090 1091 old_p = old_bucket; 1092 for (io = 0; io < SOC_L2X_BUCKET_SIZE; io++, 1093 old_p += l2x_data[unit].entry_words) { 1094 1095 /* 1096 * No deletion or change is needed for invalid entries. 1097 */ 1098 if (!soc_mem_field32_get(unit, l2x_data[unit].l2mem, 1099 old_p, valid_bit)) { 1100 continue; 1101 } 1102 1103 /* 1104 * Check if the entry has been deleted and is marked for 1105 * delete. 1106 */ 1107 delete_marked = SHR_BITGET(p_chunk_del_map, bucket_index + io); 1108 callback_suppress = SHR_BITGET(p_chunk_cb_map, bucket_index + io); 1109 #ifdef BCM_TSN_SUPPORT 1110 if (p_bucket_sr_map != NULL) { 1111 sr_ref_updated = SHR_BITGET(p_bucket_sr_map, io); 1112 } 1113 #endif /* BCM_TSN_SUPPORT */ 1114 /* 1115 * Quick check: if the entry is identical, it does not need any 1116 * deletion or change processing. It may be valid or not. 1117 */ 1118 1119 new_p = new_bucket + (io * l2x_data[unit].entry_words); 1120 if (L2X_ENTRY_EQL(unit, old_p, new_p, 1121 WORDS2BYTES(l2x_data[unit].entry_words), shadow_hit_bits)) { 1122 if (delete_marked) { 1123 /* Entry got learnt again after delete */ 1124 if (callback_suppress) { 1125 /* Indicate re-learn */ 1126 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *) new_p); 1127 } else { 1128 /* Indicate delete and re-learn */ 1129 soc_l2x_callback(unit, 0, (l2x_entry_t *) old_p, NULL); 1130 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *) new_p); 1131 } 1132 } 1133 continue; 1134 } 1135 1136 /* 1137 * See if the old key still exists anywhere within the bucket. 1138 */ 1139 1140 new_p = new_bucket; 1141 for (in = 0; in < SOC_L2X_BUCKET_SIZE; in++) { 1142 if (soc_mem_field32_get(unit, l2x_data[unit].l2mem, 1143 new_p, valid_bit) && 1144 soc_mem_compare_key(unit, l2x_data[unit].l2mem, 1145 old_p, new_p) == 0) { 1146 break; 1147 } 1148 new_p += l2x_data[unit].entry_words; 1149 } 1150 1151 if (in == SOC_L2X_BUCKET_SIZE) { 1152 /* It doesn't, so issue a delete. */ 1153 if (!callback_suppress) { 1154 #if defined(BCM_TSN_SUPPORT) 1155 /* Decrease TSN/SR flowset reference count for L2 deletion */ 1156 if (soc_feature(unit, soc_feature_tsn)) { 1157 if (!sr_ref_updated) { 1158 soc_l2x_callback(unit, 1159 SOC_L2X_ENTRY_TSN_SR_FLOWSET_REF_CNT, 1160 (l2x_entry_t *)old_p, NULL); 1161 if (p_bucket_sr_map != NULL) { 1162 SHR_BITCLR(p_bucket_sr_map, io); 1163 } 1164 } 1165 } 1166 #endif /* BCM_TSN_SUPPORT */ 1167 #ifdef BCM_XGS_SWITCH_SUPPORT 1168 #if defined(BCM_TOMAHAWK3_SUPPORT) 1169 if (SOC_IS_TOMAHAWK3(unit)) { 1170 int rv1 = SOC_E_NONE; 1171 int idx = -1; 1172 1173 /* Entry not found in this bucket. Search in L2 table to see 1174 * if it was shuffled. It it was shuffled, the same entry 1175 * will be present at some other location. If the search 1176 * did not find it, it means the entry was deleted, so we 1177 * can issue a 'delete' callback. This code is required 1178 * because TH3 does not have mod fifo; in legacy devices, 1179 * mod fifo is configured to supress callbacks due to 1180 * internal (within SDK) shuffling of L2 entries 1181 */ 1182 rv1 = soc_mem_generic_lookup(unit, L2Xm, MEM_BLOCK_ANY, 0, 1183 old_p, NULL, &idx); 1184 if (rv1 == SOC_E_NOT_FOUND) { 1185 soc_l2x_callback(unit, 0, (l2x_entry_t *) old_p, NULL); 1186 } 1187 1188 } else 1189 #endif /* BCM_TOMAHAWK3_SUPPORT */ 1190 #endif /* BCM_XGS_SWITCH_SUPPORT */ 1191 { 1192 soc_l2x_callback(unit, 0, (l2x_entry_t *) old_p, NULL); 1193 } 1194 } 1195 continue; 1196 } 1197 1198 /* 1199 * Check if everything at the new location is the same (ignoring 1200 * hit bit). If so, there is no delete or change processing. 1201 */ 1202 1203 if (!(shadow_hit_bits & L2X_SHADOW_HIT_BITS)) { 1204 if (!(shadow_hit_bits & L2X_SHADOW_HIT_DST)) { 1205 soc_L2Xm_field32_set(unit, old_p, HITDAf, 1206 soc_L2Xm_field32_get(unit, new_p, 1207 HITDAf)); 1208 } 1209 if (!(shadow_hit_bits & L2X_SHADOW_HIT_SRC)) { 1210 soc_L2Xm_field32_set(unit, old_p, HITSAf, 1211 soc_L2Xm_field32_get(unit, new_p, 1212 HITSAf)); 1213 } 1214 } 1215 1216 if (L2X_ENTRY_EQL(unit, old_p, new_p, 1217 WORDS2BYTES(l2x_data[unit].entry_words), shadow_hit_bits)) { 1218 #if defined(BCM_TRIDENT2PLUS_SUPPORT) || defined(BCM_APACHE_SUPPORT) 1219 if (soc_feature(unit, soc_feature_no_l2_remote_trunk)) { 1220 key_type = soc_L2Xm_field32_get(unit, new_p, KEY_TYPEf); 1221 trunk = soc_L2Xm_field32_get(unit, new_p, Tf); 1222 remote_trunk = soc_L2Xm_field32_get(unit, new_p, REMOTE_TRUNKf); 1223 1224 if (remote_trunk && trunk && (key_type == 0)) { 1225 /* Clear REMOTE_TRUNK in S/W new chunk */ 1226 soc_L2Xm_field32_set(unit, new_p, REMOTE_TRUNKf, 0); 1227 1228 /* Clear REMOTE_TRUNK in H/W table */ 1229 soc_mem_generic_insert(unit, L2Xm, MEM_BLOCK_ANY, 1230 0, new_p, NULL, NULL); 1231 remote_trunk = 0; 1232 trunk = 0; 1233 } 1234 } 1235 #endif 1236 if (delete_marked) { 1237 /* Entry got learnt again after delete */ 1238 if (callback_suppress) { 1239 /* Indicate re-learn */ 1240 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *) new_p); 1241 } else { 1242 /* Indicate delete and re-learn */ 1243 soc_l2x_callback(unit, 0, (l2x_entry_t *) old_p, NULL); 1244 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *) new_p); 1245 } 1246 } 1247 continue; 1248 } 1249 1250 #if defined(BCM_TRIDENT2PLUS_SUPPORT) || defined(BCM_APACHE_SUPPORT) 1251 if (soc_feature(unit, soc_feature_no_l2_remote_trunk)) { 1252 key_type = soc_L2Xm_field32_get(unit, new_p, KEY_TYPEf); 1253 trunk = soc_L2Xm_field32_get(unit, new_p, Tf); 1254 remote_trunk = soc_L2Xm_field32_get(unit, new_p, REMOTE_TRUNKf); 1255 1256 if (remote_trunk && trunk && (key_type == 0)) { 1257 /* Clear REMOTE_TRUNK in S/W new chunk */ 1258 soc_L2Xm_field32_set(unit, new_p, REMOTE_TRUNKf, 0); 1259 1260 /* Clear REMOTE_TRUNK in H/W table */ 1261 soc_mem_generic_insert(unit, L2Xm, MEM_BLOCK_ANY, 1262 0, new_p, NULL, NULL); 1263 remote_trunk = 0; 1264 trunk = 0; 1265 } 1266 } 1267 #endif 1268 if (delete_marked) { 1269 /* Entry got learnt again at another port after delete */ 1270 if (callback_suppress) { 1271 /* Indicate re-learn */ 1272 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *) new_p); 1273 } else { 1274 /* Indicate delete and re-learn */ 1275 soc_l2x_callback(unit, 0, (l2x_entry_t *) old_p, NULL); 1276 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *) new_p); 1277 } 1278 } else { 1279 /* Issue change (delete & insert) */ 1280 if (!callback_suppress) { 1281 #if defined(BCM_TSN_SUPPORT) 1282 /* Update TSN/SR flowset reference count for L2 changes */ 1283 if (soc_feature(unit, soc_feature_tsn)) { 1284 if (!sr_ref_updated) { 1285 soc_l2x_callback(unit, 1286 SOC_L2X_ENTRY_TSN_SR_FLOWSET_REF_CNT, 1287 (l2x_entry_t *)old_p, 1288 (l2x_entry_t *)new_p); 1289 if (p_bucket_sr_map != NULL) { 1290 SHR_BITCLR(p_bucket_sr_map, io); 1291 } 1292 } 1293 } 1294 #endif /* BCM_TSN_SUPPORT */ 1295 soc_l2x_callback(unit, 0, (l2x_entry_t *) old_p, 1296 (l2x_entry_t *) new_p); 1297 } 1298 } 1299 } 1300 1301 /* 1302 * Process insertions, which will be any entry whose key was not in 1303 * the bucket before, but is now. 1304 */ 1305 1306 new_p = new_bucket; 1307 for (in = 0; in < SOC_L2X_BUCKET_SIZE; in++, 1308 new_p += l2x_data[unit].entry_words) { 1309 1310 #ifdef BCM_TSN_SUPPORT 1311 if (p_bucket_sr_map != NULL) { 1312 sr_ref_updated = SHR_BITGET(p_bucket_sr_map, in); 1313 } 1314 #endif 1315 /* 1316 * Quick check: if the entry is identical, it does not need any 1317 * insertion processing. It may be valid or not. 1318 */ 1319 old_p = old_bucket + (in * l2x_data[unit].entry_words); 1320 if (L2X_ENTRY_EQL(unit, old_p, new_p, 1321 WORDS2BYTES(l2x_data[unit].entry_words), 0)) { 1322 continue; 1323 } 1324 1325 /* 1326 * No insert needed for invalid entries. 1327 */ 1328 1329 if (!soc_mem_field32_get(unit, l2x_data[unit].l2mem, 1330 new_p, valid_bit)) { 1331 continue; 1332 } 1333 1334 /* 1335 * If the same key already existed elsewhere within the bucket, 1336 * it has already been taken care of by the change processing above. 1337 */ 1338 1339 old_p = old_bucket; 1340 for (io = 0; io < SOC_L2X_BUCKET_SIZE; io++) { 1341 if (soc_mem_field32_get(unit, l2x_data[unit].l2mem, 1342 old_p, valid_bit) && 1343 soc_mem_compare_key(unit, l2x_data[unit].l2mem, 1344 new_p, old_p) == 0) { 1345 break; 1346 } 1347 old_p += l2x_data[unit].entry_words; 1348 } 1349 1350 if (io < SOC_L2X_BUCKET_SIZE) { 1351 continue; 1352 } 1353 1354 if (soc_feature(unit, soc_feature_ppa_bypass)) { 1355 if (SOC_CONTROL(unit)->l2x_ppa_bypass == FALSE && 1356 soc_mem_field32_get(unit, L2Xm, new_p, KEY_TYPEf) != 1357 TR_L2_HASH_KEY_TYPE_BRIDGE) { 1358 SOC_CONTROL(unit)->l2x_ppa_bypass = TRUE; 1359 } 1360 } 1361 1362 #if defined(BCM_TRIDENT2_SUPPORT) 1363 /* Check whether this entry in the new bucket existed in 1364 * other buckets in another banks previously. 1365 */ 1366 if (SOC_IS_TD2_TT2(unit) && 1367 soc_feature(unit, soc_feature_shared_hash_mem)) { 1368 rv = _soc_trident2_l2x_sync_multi_buckets(unit, new_p, 1369 shadow_hit_bits, 1370 &mac_moved); 1371 if (SOC_SUCCESS(rv)) { 1372 if (mac_moved == TRUE) { 1373 continue; 1374 } 1375 } 1376 } 1377 #endif 1378 1379 #if defined(BCM_TRIDENT2PLUS_SUPPORT) || defined(BCM_APACHE_SUPPORT) 1380 if (soc_feature(unit, soc_feature_no_l2_remote_trunk)) { 1381 key_type = soc_L2Xm_field32_get(unit, new_p, KEY_TYPEf); 1382 trunk = soc_L2Xm_field32_get(unit, new_p, Tf); 1383 remote_trunk = soc_L2Xm_field32_get(unit, new_p, REMOTE_TRUNKf); 1384 1385 if (remote_trunk && trunk && (key_type == 0)) { 1386 /* Clear REMOTE_TRUNK in S/W new chunk */ 1387 soc_L2Xm_field32_set(unit, new_p, REMOTE_TRUNKf, 0); 1388 1389 /* Clear REMOTE_TRUNK in H/W table */ 1390 soc_mem_generic_insert(unit, L2Xm, MEM_BLOCK_ANY, 1391 0, new_p, NULL, NULL); 1392 remote_trunk = 0; 1393 trunk = 0; 1394 } 1395 } 1396 #endif 1397 #if defined(BCM_TSN_SUPPORT) 1398 /* Increase TSN/SR flowset reference count for L2 insertions */ 1399 if (soc_feature(unit, soc_feature_tsn)) { 1400 if (!sr_ref_updated) { 1401 soc_l2x_callback(unit, 1402 SOC_L2X_ENTRY_TSN_SR_FLOWSET_REF_CNT, 1403 NULL, (l2x_entry_t *)new_p); 1404 if (p_bucket_sr_map != NULL) { 1405 SHR_BITCLR(p_bucket_sr_map, in); 1406 } 1407 } 1408 } 1409 #endif /* BCM_TSN_SUPPORT */ 1410 soc_l2x_callback(unit, 0, NULL, (l2x_entry_t *) new_p); 1411 } 1412 1413 /* Sync shadow copy */ 1414 1415 sal_memcpy(old_bucket, new_bucket, 1416 SOC_L2X_BUCKET_SIZE * l2x_data[unit].entry_words * 4); 1417 } 1418 1419 /* 1420 * Function: 1421 * _soc_l2x_thread 1422 * Purpose: 1423 * Thread control for L2 shadow table maintenance 1424 * Parameters: 1425 * unit_vp - StrataSwitch unit # (as a void *). 1426 * Returns: 1427 * Nothing 1428 * Notes: 1429 * Exits when l2x_rate is set to zero and semaphore is given. The 1430 * table is processed one chunk at a time (spn_L2XMSG_CHUNKS chunks 1431 * total) in order to reduce memory requirements for the temporary 1432 * DMA buffer and to give the CPU a break from bursts of activity. 1433 */ 1434 1435 STATIC void 1436 _soc_l2x_thread(void *unit_vp) 1437 { 1438 int unit = PTR_TO_INT(unit_vp); 1439 soc_control_t *soc = SOC_CONTROL(unit); 1440 int rv; 1441 uint32 *shadow_tab = NULL, *chunk_buf = NULL; 1442 SHR_BITDCL *delete_map = NULL; 1443 SHR_BITDCL *chunk_delete_map = NULL; 1444 SHR_BITDCL *callback_map = NULL; 1445 SHR_BITDCL *chunk_callback_map = NULL; 1446 #ifdef BCM_TSN_SUPPORT 1447 SHR_BITDCL *sr_ref_map = NULL; 1448 SHR_BITDCL *bucket_sr_ref_map = NULL; 1449 int is_tsn_or_sr = FALSE; /* TSN or SR support */ 1450 #endif 1451 int index_count; 1452 int chunk_count, chunk_size; 1453 int chunk_index, bucket_index; 1454 uint32 *tab_p, *buf_p; 1455 int interval; 1456 sal_usecs_t stime, etime; 1457 1458 l2x_data[unit].l2mem = L2Xm; 1459 l2x_data[unit].entry_bytes = soc_mem_entry_bytes(unit, l2x_data[unit].l2mem); 1460 l2x_data[unit].entry_words = soc_mem_entry_words(unit, l2x_data[unit].l2mem); 1461 1462 assert(soc_mem_index_min(unit, l2x_data[unit].l2mem) == 0); 1463 index_count = soc_mem_index_count(unit, l2x_data[unit].l2mem); 1464 if (index_count <= 0) { 1465 LOG_ERROR(BSL_LS_SOC_L2, 1466 (BSL_META_U(unit, 1467 "soc_l2x_thread: table size is 0 \n"))); 1468 soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR, 1469 SOC_SWITCH_EVENT_THREAD_L2X, __LINE__, SOC_E_EMPTY); 1470 goto cleanup_exit; 1471 } 1472 1473 chunk_count = soc_property_get(unit, spn_L2XMSG_CHUNKS, 8); 1474 chunk_size = index_count / chunk_count; 1475 1476 assert(chunk_size > 0); 1477 assert(chunk_size % SOC_L2X_BUCKET_SIZE == 0); 1478 1479 shadow_tab = sal_alloc(index_count * l2x_data[unit].entry_words * 4, 1480 "l2x_old"); 1481 1482 chunk_buf = soc_cm_salloc(unit, 1483 chunk_size * l2x_data[unit].entry_words * 4, 1484 "l2x_new"); 1485 1486 delete_map = sal_alloc(SHR_BITALLOCSIZE(index_count), "l2x_delete_map"); 1487 callback_map = sal_alloc(SHR_BITALLOCSIZE(index_count), "l2x_callback_map"); 1488 1489 chunk_delete_map = 1490 sal_alloc(SHR_BITALLOCSIZE(chunk_size), "l2x_chunk_delete_map"); 1491 chunk_callback_map = 1492 sal_alloc(SHR_BITALLOCSIZE(chunk_size), "l2x_chunk_callback_map"); 1493 #ifdef BCM_TSN_SUPPORT 1494 if (soc_feature(unit, soc_feature_tsn)) { 1495 sr_ref_map = 1496 sal_alloc(SHR_BITALLOCSIZE(index_count), "l2x_sr_ref_map"); 1497 bucket_sr_ref_map = 1498 sal_alloc(SHR_BITALLOCSIZE(SOC_L2X_BUCKET_SIZE), 1499 "l2x_bucket_sr_ref_map"); 1500 is_tsn_or_sr = TRUE; 1501 } 1502 #endif 1503 1504 if (shadow_tab == NULL || chunk_buf == NULL || 1505 delete_map == NULL || chunk_delete_map == NULL || 1506 #ifdef BCM_TSN_SUPPORT 1507 ((is_tsn_or_sr) && 1508 (sr_ref_map == NULL || bucket_sr_ref_map == NULL)) || 1509 #endif 1510 callback_map == NULL || chunk_callback_map == NULL) { 1511 LOG_ERROR(BSL_LS_SOC_L2, 1512 (BSL_META_U(unit, 1513 "AbnormalThreadExit:soc_l2x_thread: not enough memory \n"))); 1514 soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR, 1515 SOC_SWITCH_EVENT_THREAD_L2X, __LINE__, SOC_E_MEMORY); 1516 goto cleanup_exit; 1517 } 1518 1519 #ifdef BCM_HURRICANE3_SUPPORT 1520 /* Prepare for sync L2 overflow table */ 1521 if ((soc_feature(unit, soc_feature_l2_overflow_bucket)) && 1522 (soc->l2_overflow_bucket_enable)) { 1523 if (soc_hr3_l2_overflow_sync_init(unit) < 0) { 1524 LOG_ERROR(BSL_LS_SOC_L2, 1525 (BSL_META_U(unit, 1526 "Error:soc_hr3_l2_overflow_sync_init: not enough memory \n"))); 1527 soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR, 1528 SOC_SWITCH_EVENT_THREAD_L2X, __LINE__, SOC_E_MEMORY); 1529 goto cleanup_exit; 1530 } 1531 } 1532 #endif /* BCM_HURRICANE3_SUPPORT */ 1533 1534 /* 1535 * Start with initially empty shadow table. 1536 */ 1537 1538 sal_memset(shadow_tab, 0, index_count * l2x_data[unit].entry_words * 4); 1539 1540 /* 1541 * Clear the delete and callback map. 1542 */ 1543 SHR_BITCLR_RANGE(delete_map, 0, index_count); 1544 SHR_BITCLR_RANGE(callback_map, 0, index_count); 1545 1546 l2x_data[unit].shadow_tab = shadow_tab; 1547 l2x_data[unit].chunk_buf = chunk_buf; 1548 l2x_data[unit].delete_map = delete_map; 1549 l2x_data[unit].callback_map = callback_map; 1550 #ifdef BCM_TSN_SUPPORT 1551 if (is_tsn_or_sr) { 1552 SHR_BITCLR_RANGE(sr_ref_map, 0, index_count); 1553 l2x_data[unit].sr_ref_map = sr_ref_map; 1554 } 1555 #endif /* BCM_TSN_SUPPORT */ 1556 chunk_index = 0; 1557 tab_p = shadow_tab; 1558 1559 while ((interval = soc->l2x_interval) != 0) { 1560 /* 1561 * Read the next chunk of the L2 table using Table DMA. 1562 */ 1563 1564 LOG_VERBOSE(BSL_LS_SOC_ARL, 1565 (BSL_META_U(unit, 1566 "soc_l2x_thread: Process %d-%d\n"), 1567 chunk_index, chunk_index + chunk_size - 1)); 1568 1569 stime = sal_time_usecs(); 1570 1571 soc_mem_lock(unit, l2x_data[unit].l2mem); 1572 if (SOC_L2_DEL_SYNC_LOCK(soc) < 0) { 1573 LOG_ERROR(BSL_LS_SOC_L2, 1574 (BSL_META_U(unit, 1575 "soc_l2x_thread: unable to take mutex\n"))); 1576 soc_mem_unlock(unit, l2x_data[unit].l2mem); 1577 soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR, 1578 SOC_SWITCH_EVENT_THREAD_L2X, __LINE__, 1579 SOC_E_RESOURCE); 1580 goto cleanup_exit; 1581 } 1582 1583 if ((rv = soc_mem_read_range(unit, l2x_data[unit].l2mem, 1584 MEM_BLOCK_ANY,chunk_index, 1585 chunk_index + chunk_size - 1, 1586 chunk_buf)) < 0) { 1587 SOC_L2_DEL_SYNC_UNLOCK(soc); 1588 soc_mem_unlock(unit, l2x_data[unit].l2mem); 1589 LOG_ERROR(BSL_LS_SOC_L2, 1590 (BSL_META_U(unit, 1591 "soc_l2x_thread: DMA failed: %s\n"), 1592 soc_errmsg(rv))); 1593 1594 soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR, 1595 SOC_SWITCH_EVENT_THREAD_L2X, __LINE__, rv); 1596 1597 goto cleanup_exit; 1598 } else { 1599 SHR_BITCOPY_RANGE(chunk_delete_map, 0, delete_map, chunk_index, 1600 chunk_size); 1601 SHR_BITCOPY_RANGE(chunk_callback_map, 0, callback_map, chunk_index, 1602 chunk_size); 1603 1604 SHR_BITCLR_RANGE(delete_map, chunk_index, chunk_size); 1605 SHR_BITCLR_RANGE(callback_map, chunk_index, chunk_size); 1606 1607 SOC_L2_DEL_SYNC_UNLOCK(soc); 1608 soc_mem_unlock(unit, l2x_data[unit].l2mem); 1609 1610 /* 1611 * Scan, compare, and sync the old and new chunks one bucket 1612 * at a time. 1613 */ 1614 1615 buf_p = chunk_buf; 1616 for (bucket_index = 0; bucket_index < chunk_size; 1617 bucket_index += SOC_L2X_BUCKET_SIZE) { 1618 #ifdef BCM_TSN_SUPPORT 1619 if (is_tsn_or_sr) { 1620 soc_mem_lock(unit, l2x_data[unit].l2mem); 1621 SOC_L2_SR_LOCK(unit); 1622 SHR_BITCOPY_RANGE(bucket_sr_ref_map, 0, sr_ref_map, 1623 chunk_index + bucket_index, 1624 SOC_L2X_BUCKET_SIZE); 1625 1626 _soc_l2x_sync_bucket(unit, tab_p, buf_p, 1627 soc->l2x_shadow_hit_bits, 1628 bucket_index, chunk_delete_map, 1629 chunk_callback_map, 1630 bucket_sr_ref_map); 1631 SHR_BITCOPY_RANGE(sr_ref_map, chunk_index + bucket_index, 1632 bucket_sr_ref_map, 1633 0, 1634 SOC_L2X_BUCKET_SIZE); 1635 SOC_L2_SR_UNLOCK(unit); 1636 soc_mem_unlock(unit, l2x_data[unit].l2mem); 1637 } else 1638 #endif 1639 { 1640 _soc_l2x_sync_bucket(unit, tab_p, buf_p, 1641 soc->l2x_shadow_hit_bits, 1642 bucket_index, chunk_delete_map, 1643 chunk_callback_map, NULL); 1644 } 1645 1646 tab_p += l2x_data[unit].entry_words * SOC_L2X_BUCKET_SIZE; 1647 buf_p += l2x_data[unit].entry_words * SOC_L2X_BUCKET_SIZE; 1648 } 1649 1650 if ((chunk_index += chunk_size) >= index_count) { 1651 chunk_index = 0; 1652 tab_p = shadow_tab; 1653 } 1654 } 1655 1656 etime = sal_time_usecs(); 1657 1658 /* 1659 * Implement the sleep using a semaphore timeout so if the task 1660 * is requested to exit, it can do so immediately. 1661 */ 1662 1663 LOG_VERBOSE(BSL_LS_SOC_ARL, 1664 (BSL_META_U(unit, 1665 "soc_l2x_thread: unit=%d: done in %d usec\n"), 1666 unit, 1667 SAL_USECS_SUB(etime, stime))); 1668 #if defined(BCM_HURRICANE3_SUPPORT) 1669 /* Sync the L2 overflow table */ 1670 if ((chunk_index == 0) && 1671 (soc_feature(unit, soc_feature_l2_overflow_bucket)) && 1672 (soc->l2_overflow_bucket_enable)) { 1673 soc_hr3_l2_overflow_sync(unit); 1674 1675 } 1676 1677 #endif /* BCM_HURRICANE3_SUPPORT */ 1678 1679 sal_sem_take(soc->l2x_notify, interval / chunk_count); 1680 } 1681 1682 cleanup_exit: 1683 1684 (void)sal_sem_take(soc->l2x_lock, sal_sem_FOREVER); 1685 1686 if (chunk_buf != NULL) { 1687 soc_cm_sfree(unit, chunk_buf); 1688 l2x_data[unit].chunk_buf = NULL; 1689 } 1690 1691 if (shadow_tab != NULL) { 1692 sal_free(shadow_tab); 1693 l2x_data[unit].shadow_tab = NULL; 1694 } 1695 1696 if (delete_map != NULL) { 1697 sal_free(delete_map); 1698 l2x_data[unit].delete_map = NULL; 1699 } 1700 1701 if (chunk_delete_map != NULL) { 1702 sal_free(chunk_delete_map); 1703 } 1704 1705 if (callback_map != NULL) { 1706 sal_free(callback_map); 1707 l2x_data[unit].callback_map = NULL; 1708 } 1709 1710 if (chunk_callback_map != NULL) { 1711 sal_free(chunk_callback_map); 1712 } 1713 1714 #ifdef BCM_TSN_SUPPORT 1715 if (sr_ref_map != NULL) { 1716 sal_free(sr_ref_map); 1717 l2x_data[unit].sr_ref_map = NULL; 1718 } 1719 if (bucket_sr_ref_map != NULL) { 1720 sal_free(bucket_sr_ref_map); 1721 } 1722 #endif 1723 1724 #ifdef BCM_HURRICANE3_SUPPORT 1725 /* Prepare for sync L2 overflow table */ 1726 if ((soc_feature(unit, soc_feature_l2_overflow_bucket)) && 1727 (soc->l2_overflow_bucket_enable)) { 1728 soc_hr3_l2_overflow_sync_cleanup(unit); 1729 } 1730 #endif /* BCM_HURRICANE3_SUPPORT */ 1731 1732 (void)sal_sem_give(soc->l2x_lock); 1733 1734 LOG_INFO(BSL_LS_SOC_ARL, 1735 (BSL_META_U(unit, 1736 "soc_l2x_thread: exiting\n"))); 1737 1738 soc->l2x_pid = SAL_THREAD_ERROR; 1739 sal_thread_exit(0); 1740 } 1741 1742 #if defined(BCM_TSN_SUPPORT) 1743 /* 1744 * Function: 1745 * soc_l2x_sr_ref_sync 1746 * Purpose: 1747 * Sync the SW shadow copy for SR flow reference count 1748 * Parameters: 1749 * unit - unit number. 1750 * index - L2 entry index. 1751 * Returns: 1752 * SOC_E_XXX 1753 * Notes: 1754 */ 1755 int soc_l2x_sr_ref_sync(int unit, int l2_index) 1756 { 1757 soc_control_t *soc = SOC_CONTROL(unit); 1758 int max_index; 1759 1760 LOG_VERBOSE(BSL_LS_SOC_ARL, 1761 (BSL_META_U(unit, 1762 "soc_l2x_sr_ref_sync: unit=%d index=%d\n"), 1763 unit, l2_index)); 1764 1765 if (soc->l2x_pid == SAL_THREAD_ERROR) { 1766 /* thread not running */ 1767 return SOC_E_NONE; 1768 } 1769 1770 if (l2x_data[unit].l2mem == INVALIDm) { 1771 return SOC_E_NONE; 1772 } 1773 1774 max_index = soc_mem_index_max(unit, l2x_data[unit].l2mem); 1775 if (l2_index > max_index) { 1776 #ifdef BCM_HURRICANE3_SUPPORT 1777 if ((SOC_IS_HURRICANE3(unit) || SOC_IS_GREYHOUND2(unit)) && 1778 (l2_index & SOC_L2X_GEN_RESP_INDEX_L2_OVERFLOW(unit))) { 1779 /* Entry learnt in L2_ENTRY_OVERFLOW */ 1780 return SOC_E_NONE; 1781 } 1782 #endif /* BCM_HURRICANE3_SUPPORT */ 1783 return SOC_E_PARAM; 1784 } 1785 if (l2x_data[unit].shadow_tab == NULL) { 1786 LOG_WARN(BSL_LS_SOC_ARL, 1787 (BSL_META_U(unit, 1788 "soc_l2x_sr_ref_sync: " 1789 "l2x_data[%d].shadow_tab is NULL!!!\n"), 1790 unit)); 1791 return SOC_E_NONE; 1792 } 1793 1794 SHR_BITSET(l2x_data[unit].sr_ref_map, l2_index); 1795 1796 return SOC_E_NONE; 1797 } 1798 1799 #endif /* BCM_TSN_SUPPORT */ 1800 1801 #endif /* BCM_XGS_SWITCH_SUPPORT */