l3x.c (54408B)
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 * XGS L3 Table Manipulation API routines. 8 * 9 * The L3Xm memory is an aggregate structure supported by hardware. 10 * When an entry is read from it, it is constructed from the L3X_BASEm, 11 * L3X_VALIDm, and L3X_HITm tables. NOTE: the L3X table is read-only. 12 */ 13 14 #include <shared/bsl.h> 15 16 #include <sal/core/libc.h> 17 #include <shared/bsl.h> 18 #include <soc/drv.h> 19 #include <soc/l3x.h> 20 #include <soc/mem.h> 21 #include <soc/hash.h> 22 #include <soc/util.h> 23 #include <soc/debug.h> 24 #ifdef BCM_TRIDENT2_SUPPORT 25 #include <soc/trident2.h> 26 #endif 27 28 /* 29 * Function: 30 * soc_esw_l3_lock 31 * Purpose: 32 * Lock L3 module - if module was not initialized NOOP 33 * 34 * Parameters: 35 * unit - (IN) Unit number. 36 * Returns: 37 * SOC_E_XXX 38 * Notes: 39 */ 40 int 41 soc_esw_l3_lock(int unit) 42 { 43 #if defined(INCLUDE_L3) 44 if (NULL != SOC_CONTROL(unit)->l3x_lock) { 45 return sal_mutex_take(SOC_CONTROL(unit)->l3x_lock, 46 sal_mutex_FOREVER); 47 } 48 #endif /* INCLUDE_L3 */ 49 return (SOC_E_NONE); 50 } 51 52 /* 53 * Function: 54 * soc_esw_l3_unlock 55 * Purpose: 56 * Unlock L3 module - if module was not initialized NOOP 57 * 58 * Parameters: 59 * unit - (IN) Unit number. 60 * Returns: 61 * SOC_E_XXX 62 * Notes: 63 */ 64 int 65 soc_esw_l3_unlock(int unit) 66 { 67 #if defined(INCLUDE_L3) 68 if (NULL != SOC_CONTROL(unit)->l3x_lock) { 69 return sal_mutex_give(SOC_CONTROL(unit)->l3x_lock); 70 } 71 #endif /* INCLUDE_L3 */ 72 return (SOC_E_NONE); 73 } 74 75 #ifdef INCLUDE_L3 76 77 #ifdef BCM_XGS_SWITCH_SUPPORT 78 79 /* 80 * Function: 81 * soc_l3x_init 82 * Purpose: 83 * Initialize L3 table subsystem. 84 * Parameters: 85 * unit - StrataSwitch unit number. 86 * Returns: 87 * SOC_E_xxx 88 */ 89 int 90 soc_l3x_init(int unit) 91 { 92 return SOC_E_NONE; 93 } 94 95 /* 96 * Function: 97 * soc_l3x_lock 98 * Purpose: 99 * Lock all of the L3 host tables 100 * Parameters: 101 * unit - StrataSwitch PCI device unit number 102 * Returns: 103 * SOC_E_XXX 104 */ 105 106 int 107 soc_l3x_lock(int unit) 108 { 109 #ifdef BCM_FIREBOLT_SUPPORT 110 if (SOC_IS_FBX(unit)) { 111 soc_mem_lock(unit, L3_ENTRY_ONLYm); 112 } 113 #endif 114 return SOC_E_NONE; 115 } 116 117 /* 118 * Function: 119 * soc_l3x_unlock 120 * Purpose: 121 * Unlock all of the L3 host tables 122 * Parameters: 123 * unit - StrataSwitch PCI device unit number 124 * Returns: 125 * SOC_E_XXX 126 */ 127 128 int 129 soc_l3x_unlock(int unit) 130 { 131 #ifdef BCM_FIREBOLT_SUPPORT 132 if (SOC_IS_XGS3_SWITCH(unit)) { 133 soc_mem_unlock(unit, L3_ENTRY_ONLYm); 134 } 135 #endif 136 return SOC_E_NONE; 137 } 138 139 /* 140 * Function: 141 * _soc_l3x_entry_mem_view_get 142 * Purpose: 143 * Given base table entry & memory extract view name 144 * & number of base slots entry occupies. 145 * Parameters: 146 * unit - (IN) SOC device number. 147 * base_mem - (IN) Base memory name. 148 * base_entry - (IN) Base memory entry pointer. 149 * entry_mem - (OUT)Entry memory. 150 * entry_size - (OUT)Number of base entry slots entry occupies. 151 * Returns: 152 * SOC_E_XXX 153 */ 154 155 STATIC int 156 _soc_l3x_entry_mem_view_get(int unit, 157 soc_mem_t base_mem, 158 uint32 *base_entry, 159 soc_mem_t *entry_mem, 160 int *entry_size) 161 { 162 int key_type; /* Entry type. */ 163 soc_mem_t mem = INVALIDm; /* Resolved entry memory.*/ 164 int v6 = 0; /* IPv6 entry flag. */ 165 int ipmc = 0; /* IPMC entry flag. */ 166 167 168 /* Input parameters check. */ 169 if ((NULL == entry_mem) || (NULL == entry_size) || 170 (!SOC_MEM_IS_VALID(unit, base_mem))) { 171 return (SOC_E_PARAM); 172 } 173 174 /* Check valid bit. */ 175 if (!SOC_MEM_FIELD_VALID(unit, base_mem, VALIDf)) { 176 return (SOC_E_UNAVAIL); 177 } 178 #if defined(METROLITE_AV_DEBUG) 179 /*As requested by the AV team removing this check for the AV release only. 180 * This will help the AV team to use the existing cases without modification */ 181 else if (!soc_mem_field32_get(unit, base_mem, base_entry, VALIDf)) { 182 *entry_mem = INVALIDm; 183 *entry_size = 1; 184 return (SOC_E_NONE); 185 } 186 #endif 187 /* We start by reading one minimal size L3 entry and 188 * identifying the actual entry type. */ 189 if (SOC_MEM_FIELD_VALID(unit, base_mem, KEY_TYPEf)) { 190 key_type = soc_mem_field32_get(unit, base_mem, base_entry, KEY_TYPEf); 191 #if defined(BCM_TRIDENT2_SUPPORT) 192 if (SOC_IS_TRIDENT2X(unit) || SOC_IS_TOMAHAWKX(unit)) { 193 switch (key_type) { 194 case TD2_L3_HASH_KEY_TYPE_V4UC: 195 case TD2_L3_HASH_KEY_TYPE_TRILL: 196 case TD2_L3_HASH_KEY_TYPE_FCOE_DOMAIN: 197 case TD2_L3_HASH_KEY_TYPE_FCOE_HOST: 198 case TD2_L3_HASH_KEY_TYPE_FCOE_SRC_MAP: 199 mem = L3_ENTRY_IPV4_UNICASTm; 200 break; 201 case TD2_L3_HASH_KEY_TYPE_V4UC_EXT: 202 case TD2_L3_HASH_KEY_TYPE_V4MC: 203 case TD2_L3_HASH_KEY_TYPE_V4L2MC: 204 case TD2_L3_HASH_KEY_TYPE_V4L2VPMC: 205 case TD2_L3_HASH_KEY_TYPE_FCOE_DOMAIN_EXT: 206 case TD2_L3_HASH_KEY_TYPE_FCOE_HOST_EXT: 207 case TD2_L3_HASH_KEY_TYPE_FCOE_SRC_MAP_EXT: 208 case TD2_L3_HASH_KEY_TYPE_DST_NAT: 209 case TD2_L3_HASH_KEY_TYPE_DST_NAPT: 210 mem = L3_ENTRY_IPV4_MULTICASTm; 211 break; 212 case TD2_L3_HASH_KEY_TYPE_V6UC: 213 mem = L3_ENTRY_IPV6_UNICASTm; 214 break; 215 case TD2_L3_HASH_KEY_TYPE_V6UC_EXT: 216 case TD2_L3_HASH_KEY_TYPE_V6MC: 217 case TD2_L3_HASH_KEY_TYPE_V6L2MC: 218 case TD2_L3_HASH_KEY_TYPE_V6L2VPMC: 219 mem = L3_ENTRY_IPV6_MULTICASTm; 220 break; 221 default: 222 return SOC_E_PARAM; 223 } 224 } else 225 #endif /* BCM_TRIDENT2_SUPPORT */ 226 { 227 switch (key_type) { 228 case TR_L3_HASH_KEY_TYPE_V4UC: 229 mem = L3_ENTRY_IPV4_UNICASTm; 230 break; 231 case TR_L3_HASH_KEY_TYPE_V4MC: 232 mem = L3_ENTRY_IPV4_MULTICASTm; 233 break; 234 case TR_L3_HASH_KEY_TYPE_V6UC: 235 mem = L3_ENTRY_IPV6_UNICASTm; 236 break; 237 case TR_L3_HASH_KEY_TYPE_V6MC: 238 mem = L3_ENTRY_IPV6_MULTICASTm; 239 break; 240 case TR_L3_HASH_KEY_TYPE_LMEP: 241 case TR_L3_HASH_KEY_TYPE_RMEP: 242 case TR_L3_HASH_KEY_TYPE_TRILL: 243 mem = L3_ENTRY_IPV4_UNICASTm; 244 break; 245 default: 246 return SOC_E_PARAM; 247 } 248 } 249 } else if ((SOC_MEM_FIELD_VALID(unit, base_mem, V6f)) && 250 (SOC_MEM_FIELD_VALID(unit, base_mem, IPMCf))) { 251 v6 = soc_mem_field32_get(unit, base_mem, base_entry, V6f); 252 ipmc = soc_mem_field32_get(unit, base_mem, base_entry, IPMCf); 253 if (v6 && ipmc) { 254 mem = L3_ENTRY_IPV6_MULTICASTm; 255 } else if (v6) { 256 mem = L3_ENTRY_IPV6_UNICASTm; 257 } else if (ipmc) { 258 mem = L3_ENTRY_IPV4_MULTICASTm; 259 } else { 260 mem = L3_ENTRY_IPV4_UNICASTm; 261 } 262 } else { 263 return (SOC_E_UNAVAIL); 264 } 265 266 *entry_size = soc_mem_index_count(unit, base_mem) / soc_mem_index_count(unit, mem); 267 *entry_mem = mem; 268 return (SOC_E_NONE); 269 } 270 271 #ifdef BCM_FIREBOLT_SUPPORT 272 /* 273 * Function: 274 * soc_fb_l3x_bank_lookup 275 * Purpose: 276 * Send an L3 lookup message over the S-Channel and receive the 277 * response. 278 * Parameters: 279 * unit - StrataSwitch unit # 280 * banks - For dual hashing, which halves are selected (inverted) 281 * key - L3X entry to look up; only MAC+VLAN fields are relevant 282 * result - L3X entry to receive entire found entry 283 * index_ptr (OUT) - If found, receives table index where found 284 * Returns: 285 * SOC_E_INTERNAL if retries exceeded or other internal error 286 * SOC_E_NOT_FOUND if the entry is not found. 287 * SOC_E_NONE (0) on success (entry found): 288 * Notes: 289 * The S-Channel response contains either a matching L3 entry, 290 * or an entry with a key of all f's if not found. It is okay 291 * if the result pointer is the same as the key pointer. 292 * Retries the lookup in cases where the particular chip requires it. 293 */ 294 295 int 296 soc_fb_l3x_bank_lookup(int unit, uint8 banks, 297 l3_entry_ipv6_multicast_entry_t *key, 298 l3_entry_ipv6_multicast_entry_t *result, 299 int *index_ptr) 300 { 301 schan_msg_t schan_msg; 302 int i; 303 int entry_dw; 304 soc_mem_t mem; 305 int nbits; 306 int rv; 307 int entry_size; 308 int dst_blk, src_blk, data_byte_len; 309 uint32 bank_ignore_mask; 310 int opcode, nack; 311 312 SOC_IF_ERROR_RETURN(_soc_l3x_entry_mem_view_get(unit, 313 L3_ENTRY_IPV4_UNICASTm, (uint32 *) key, &mem, &entry_size)); 314 315 if (INVALIDm == mem) { 316 return SOC_E_PARAM; 317 } 318 319 nbits = soc_mem_entry_bits(unit, mem) % 32; 320 if (nbits == 0) { 321 nbits = 32; 322 } 323 entry_dw = soc_mem_entry_words(unit, mem); 324 schan_msg_clear(&schan_msg); 325 src_blk = SOC_BLOCK2SCH(unit, CMIC_BLOCK(unit)); 326 dst_blk = SOC_BLOCK2SCH(unit, IPIPE_BLOCK(unit)); 327 bank_ignore_mask = banks & 0x3; 328 data_byte_len = entry_dw * 4; 329 soc_schan_header_cmd_set(unit, &schan_msg.header, L3X2_LOOKUP_CMD_MSG, 330 dst_blk, src_blk, 0, data_byte_len, 0, 331 bank_ignore_mask); 332 333 /* Fill in entry data */ 334 335 sal_memcpy(schan_msg.l3x2.data, key, entry_dw * 4); 336 337 /* 338 * Write onto S-Channel "L3 lookup" command packet consisting of 339 * header word + 4/4/7/13 words of L3 key, and read back header 340 * word + 5/5/8/14 words of lookup result. 341 * (index of the extry + contents of the entry) 342 */ 343 344 rv = soc_schan_op(unit, &schan_msg, entry_dw + 1, entry_dw + 2, 1); 345 346 /* Check result */ 347 soc_schan_header_status_get(unit, &schan_msg.header, &opcode, NULL, NULL, 348 NULL, NULL, &nack); 349 if (opcode != L3X2_LOOKUP_ACK_MSG) { 350 LOG_ERROR(BSL_LS_SOC_L3, 351 (BSL_META_U(unit, 352 "soc_fb_l3x_lookup: invalid S-Channel reply, " 353 "expected L3X2_LOOKUP_ACK_MSG:\n"))); 354 soc_schan_dump(unit, &schan_msg, entry_dw + 2); 355 return SOC_E_INTERNAL; 356 } 357 358 /* 359 * Fill in result entry from read data. 360 * 361 * Format of S-Channel response: 362 * readresp.header : L3 lookup Ack S-channel header 363 * readresp.data[0-n]: entry contents 364 * readresp.data[n]: index of the entry in the L3_ENTRY_XXXm Table 365 * ======================================= 366 * | PERR_PBM | Index | L3x entry data | 367 * ======================================= 368 */ 369 370 if ((nack != 0) || (rv == SOC_E_FAIL)) { 371 *index_ptr = -1; 372 if (soc_feature(unit, soc_feature_l3x_parity)) { 373 int perr_pbm_pos; 374 int perr_index; 375 uint32 perr_pbm; 376 if (SOC_IS_RAVEN(unit) && mem == L3_ENTRY_IPV6_MULTICASTm) { 377 perr_index = entry_dw; 378 perr_pbm_pos = _shr_popcount(SOC_MEM_INFO(unit, mem).index_max) - 379 soc_mem_entry_bits(unit, mem) / 32; 380 } else { 381 perr_index = entry_dw - 1; 382 perr_pbm_pos = SOC_L3X_PERR_PBM_POS(unit, mem); 383 } 384 perr_pbm = ((schan_msg.readresp.data[perr_index] >> 385 perr_pbm_pos) & (SOC_L3X_BUCKET_SIZE(unit) - 1)); 386 if (perr_pbm) { 387 uint32 index = (schan_msg.readresp.data[perr_index] >> nbits) & 388 ((1 << (32 - nbits)) - 1); 389 index |= (schan_msg.readresp.data[perr_index + 1] << (32 - nbits)) & 390 soc_mem_index_max(unit, mem); /* Assume size of table 2^N */ 391 LOG_ERROR(BSL_LS_SOC_L3, 392 (BSL_META_U(unit, 393 "Lookup table[L3_ENTRY_XXX]: Parity Error Index %d Bucket Bitmap 0x%08x\n"), 394 index, 395 (schan_msg.readresp.data[perr_index] >> perr_pbm_pos) & 396 (SOC_L3X_BUCKET_SIZE(unit) - 1) )); 397 return SOC_E_INTERNAL; 398 } 399 } 400 return SOC_E_NOT_FOUND; 401 } 402 403 404 for(i = 0; i < entry_dw - 1; i++) { 405 result->entry_data[i] = schan_msg.readresp.data[i]; 406 } 407 result->entry_data[i] = schan_msg.readresp.data[i] & ((1 << nbits) - 1); 408 *index_ptr = (schan_msg.readresp.data[i] >> nbits) & 409 ((1 << (32 - nbits)) - 1); 410 *index_ptr |= (schan_msg.readresp.data[i + 1] << (32 - nbits)) & 411 soc_mem_index_max(unit, mem); 412 413 if (bsl_check(bslLayerSoc, bslSourceSocmem, bslSeverityNormal, unit)) { 414 LOG_INFO(BSL_LS_SOC_SOCMEM, 415 (BSL_META_U(unit, 416 "L3 entry lookup: "))); 417 soc_mem_entry_dump(unit, mem, result, BSL_INFO|BSL_LS_SOC_SOCMEM); 418 LOG_INFO(BSL_LS_SOC_SOCMEM, 419 (BSL_META_U(unit, 420 " (index=%d)\n"), *index_ptr)); 421 } 422 423 return SOC_E_NONE; 424 } 425 426 /* 427 * Function: 428 * soc_fb_l3x_bank_insert 429 * Purpose: 430 * Insert an entry into the L3X hash table. 431 * Parameters: 432 * unit - StrataSwitch unit # 433 * banks - For dual hashing, which halves are selected (inverted) 434 * entry - L3X entry to insert 435 * Returns: 436 * SOC_E_NONE - success 437 * SOC_E_FULL - hash bucket full 438 * Notes: 439 * Uses hardware insertion; sends an L3 INSERT message over the 440 * S-Channel. The hardware needs the L3_ENTRY_XXX entry type. 441 * This affects the L3_ENTRY_ONLYm table, L3_VALID_ONLYm table, L3_STATICm 442 * table, and the L3_HITm table. 443 */ 444 445 int 446 soc_fb_l3x_bank_insert(int unit, uint8 banks, 447 l3_entry_ipv6_multicast_entry_t *entry) 448 { 449 schan_msg_t schan_msg; 450 int rv; 451 int entry_dw; 452 soc_mem_t mem; 453 int entry_size; 454 int dst_blk, src_blk, data_byte_len; 455 uint32 bank_ignore_mask; 456 int opcode, nack; 457 458 SOC_IF_ERROR_RETURN(_soc_l3x_entry_mem_view_get(unit, 459 L3_ENTRY_IPV4_UNICASTm, (uint32 *) entry, &mem, &entry_size)); 460 if (INVALIDm == mem) { 461 return SOC_E_PARAM; 462 } 463 464 if (soc_feature(unit, soc_feature_generic_table_ops)) { 465 return soc_mem_generic_insert(unit, mem, MEM_BLOCK_ANY, banks, 466 (void *)entry, NULL, 0); 467 } 468 469 entry_dw = soc_mem_entry_words(unit, mem); 470 schan_msg_clear(&schan_msg); 471 src_blk = SOC_BLOCK2SCH(unit, CMIC_BLOCK(unit)); 472 dst_blk = SOC_BLOCK2SCH(unit, IPIPE_BLOCK(unit)); 473 bank_ignore_mask = banks & 0x3; 474 data_byte_len = entry_dw * 4; 475 soc_schan_header_cmd_set(unit, &schan_msg.header, L3_INSERT_CMD_MSG, 476 dst_blk, src_blk, 0, data_byte_len, 0, 477 bank_ignore_mask); 478 479 /* Fill in entry data */ 480 sal_memcpy(schan_msg.l3x2.data, entry, entry_dw * 4); 481 482 /* Execute S-Channel operation */ 483 rv = soc_schan_op(unit, &schan_msg, entry_dw + 1, entry_dw + 2, 1); 484 485 soc_schan_header_status_get(unit, &schan_msg.header, &opcode, NULL, NULL, 486 NULL, NULL, &nack); 487 if (opcode != L3_INSERT_DONE_MSG) { 488 LOG_ERROR(BSL_LS_SOC_L3, 489 (BSL_META_U(unit, 490 "soc_fb_l3x_insert: invalid S-Channel reply, " 491 "expected L3_INSERT_DONE_MSG:\n"))); 492 soc_schan_dump(unit, &schan_msg, 1); 493 return SOC_E_INTERNAL; 494 } 495 496 if ((nack != 0) || (rv == SOC_E_FAIL)) { 497 if (soc_feature(unit, soc_feature_l3x_parity)) { 498 int perr_pbm_pos; 499 int perr_index; 500 uint32 perr_pbm; 501 if (SOC_IS_RAVEN(unit) && mem == L3_ENTRY_IPV6_MULTICASTm) { 502 perr_index = entry_dw; 503 perr_pbm_pos = _shr_popcount(SOC_MEM_INFO(unit, mem).index_max) - 504 soc_mem_entry_bits(unit, mem) / 32; 505 } else { 506 perr_index = entry_dw - 1; 507 perr_pbm_pos = SOC_L3X_PERR_PBM_POS(unit, mem); 508 } 509 perr_pbm = ((schan_msg.readresp.data[perr_index] >> 510 perr_pbm_pos) & (SOC_L3X_BUCKET_SIZE(unit) - 1)); 511 if (perr_pbm) { 512 uint32 index; 513 int nbits; 514 515 nbits = soc_mem_entry_bits(unit, mem) % 32; 516 if (nbits == 0) { 517 nbits = 32; 518 } 519 index = (schan_msg.readresp.data[perr_index] >> nbits) & 520 ((1 << (32 - nbits)) - 1); 521 index |= (schan_msg.readresp.data[perr_index + 1] << (32 - nbits)) & 522 soc_mem_index_max(unit, mem); /* Assume size of table 2^N */ 523 LOG_ERROR(BSL_LS_SOC_L3, 524 (BSL_META_U(unit, 525 "Insert table[L3_ENTRY_XXX]: Parity Error Index %d Bucket Bitmap 0x%08x\n"), 526 index, 527 (schan_msg.readresp.data[perr_index] >> perr_pbm_pos) & 528 (SOC_L3X_BUCKET_SIZE(unit) - 1))); 529 return SOC_E_INTERNAL; 530 } 531 } 532 LOG_INFO(BSL_LS_SOC_SOCMEM, 533 (BSL_META_U(unit, 534 "Insert table[L3_ENTRY_XXX]: hash bucket full\n"))); 535 rv = SOC_E_FULL; 536 } 537 538 return rv; 539 } 540 541 /* 542 * Function: 543 * soc_fb_l3x_bank_delete 544 * Purpose: 545 * Delete an entry from the L3X hash table. 546 * Parameters: 547 * unit - StrataSwitch unit # 548 * banks - For dual hashing, which halves are selected (inverted) 549 * entry - L3X entry to delete 550 * Returns: 551 * SOC_E_NONE - success 552 * Notes: 553 * Uses hardware deletion; sends an L3 DELETE message over the 554 * S-Channel. The hardware needs the L3_ENTRY_XXX entry type. 555 */ 556 557 int 558 soc_fb_l3x_bank_delete(int unit, uint8 banks, 559 l3_entry_ipv6_multicast_entry_t *entry) 560 { 561 schan_msg_t schan_msg; 562 int rv; 563 int entry_dw; 564 soc_mem_t mem; 565 int entry_size; 566 int dst_blk, src_blk, data_byte_len; 567 uint32 bank_ignore_mask; 568 int opcode, nack; 569 570 SOC_IF_ERROR_RETURN(_soc_l3x_entry_mem_view_get(unit, 571 L3_ENTRY_IPV4_UNICASTm, (uint32 *) entry, &mem, &entry_size)); 572 if (INVALIDm == mem) { 573 return SOC_E_PARAM; 574 } 575 576 entry_dw = soc_mem_entry_words(unit, mem); 577 578 if (bsl_check(bslLayerSoc, bslSourceSocmem, bslSeverityNormal, unit)) { 579 LOG_INFO(BSL_LS_SOC_SOCMEM, 580 (BSL_META_U(unit, 581 "Delete table[L3_ENTRY_XXXm]: "))); 582 soc_mem_entry_dump(unit, mem, entry, BSL_INFO|BSL_LS_SOC_SOCMEM); 583 LOG_INFO(BSL_LS_SOC_SOCMEM, 584 (BSL_META_U(unit, 585 "\n"))); 586 } 587 588 schan_msg_clear(&schan_msg); 589 src_blk = SOC_BLOCK2SCH(unit, CMIC_BLOCK(unit)); 590 dst_blk = SOC_BLOCK2SCH(unit, IPIPE_BLOCK(unit)); 591 bank_ignore_mask = banks & 0x3; 592 data_byte_len = entry_dw * 4; 593 soc_schan_header_cmd_set(unit, &schan_msg.header, L3_DELETE_CMD_MSG, 594 dst_blk, src_blk, 0, data_byte_len, 0, 595 bank_ignore_mask); 596 597 /* Fill in packet data */ 598 sal_memcpy(schan_msg.l3x2.data, entry, entry_dw * 4); 599 600 /* Execute S-Channel operation */ 601 rv = soc_schan_op(unit, &schan_msg, entry_dw + 1, entry_dw + 2, 1); 602 603 soc_schan_header_status_get(unit, &schan_msg.header, &opcode, NULL, NULL, 604 NULL, NULL, &nack); 605 if (opcode != L3_DELETE_DONE_MSG) { 606 LOG_ERROR(BSL_LS_SOC_L3, 607 (BSL_META_U(unit, 608 "soc_fb_l3x_delete: invalid S-Channel reply, " 609 "expected L3_DELETE_DONE_MSG:\n"))); 610 soc_schan_dump(unit, &schan_msg, 1); 611 return SOC_E_INTERNAL; 612 } 613 614 if ((nack != 0) || (rv == SOC_E_FAIL)) { 615 if (soc_feature(unit, soc_feature_l3x_parity)) { 616 int perr_pbm_pos; 617 int perr_index; 618 uint32 perr_pbm; 619 if (SOC_IS_RAVEN(unit) && mem == L3_ENTRY_IPV6_MULTICASTm) { 620 perr_index = entry_dw; 621 perr_pbm_pos = _shr_popcount(SOC_MEM_INFO(unit, mem).index_max) - 622 soc_mem_entry_bits(unit, mem) / 32; 623 } else { 624 perr_index = entry_dw - 1; 625 perr_pbm_pos = SOC_L3X_PERR_PBM_POS(unit, mem); 626 } 627 perr_pbm = ((schan_msg.readresp.data[perr_index] >> 628 perr_pbm_pos) & (SOC_L3X_BUCKET_SIZE(unit) - 1)); 629 if (perr_pbm) { 630 uint32 index; 631 int nbits; 632 633 nbits = soc_mem_entry_bits(unit, mem) % 32; 634 if (nbits == 0) { 635 nbits = 32; 636 } 637 index = (schan_msg.readresp.data[perr_index] >> nbits) & 638 ((1 << (32 - nbits)) - 1); 639 index |= (schan_msg.readresp.data[perr_index + 1] << (32 - nbits)) & 640 soc_mem_index_max(unit, mem); /* Assume size of table 2^N */ 641 LOG_ERROR(BSL_LS_SOC_L3, 642 (BSL_META_U(unit, 643 "Delete table[L3_ENTRYm]: Parity Error Index %d Bucket Bitmap 0x%08x\n"), 644 index, 645 (schan_msg.readresp.data[perr_index] >> perr_pbm_pos) & 646 (SOC_L3X_BUCKET_SIZE(unit) - 1) )); 647 rv = SOC_E_INTERNAL; 648 } 649 } 650 LOG_INFO(BSL_LS_SOC_SOCMEM, 651 (BSL_META_U(unit, 652 "Delete table[L3_ENTRYm]: Not found\n"))); 653 } 654 655 return rv; 656 } 657 658 /* 659 * Function: 660 * _soc_l3x_compare_size 661 * Purpose: 662 * Compare two entries in l3x mem bucket by number of 663 * base slots they occupy. 664 * Parameters: 665 * b - (IN)first compared entry. 666 * a - (IN)second compared entry. 667 * Returns: 668 * a<=>b 669 */ 670 static INLINE int 671 _soc_l3x_compare_size(void *a, void *b) 672 { 673 soc_mem_l3x_entry_info_t *first; /* First compared entry. */ 674 soc_mem_l3x_entry_info_t *second; /* Second compared entry. */ 675 676 /* Cast group info pointers. */ 677 first = (soc_mem_l3x_entry_info_t *)a; 678 second = (soc_mem_l3x_entry_info_t *)b; 679 680 if (first->size > second->size) { 681 return -1; 682 } else if (first->size < second->size) { 683 return 1; 684 } 685 return 0; 686 } 687 688 /* 689 * Function: 690 * _soc_l3x_compare_index 691 * Purpose: 692 * Compare two entries in l3x mem bucket by index 693 * in the bucket 694 * Parameters: 695 * b - (IN)first compared entry. 696 * a - (IN)second compared entry. 697 * Returns: 698 * a<=>b 699 */ 700 static INLINE int 701 _soc_l3x_compare_index(void *a, void *b) 702 { 703 soc_mem_l3x_entry_info_t *first; /* First compared entry. */ 704 soc_mem_l3x_entry_info_t *second; /* Second compared entry. */ 705 706 /* Cast group info pointers. */ 707 first = (soc_mem_l3x_entry_info_t *)a; 708 second = (soc_mem_l3x_entry_info_t *)b; 709 710 if (first->index > second->index) { 711 return 1; 712 } else if (first->index < second->index) { 713 return -1; 714 } 715 return 0; 716 } 717 718 719 720 721 722 /* 723 * Function: 724 * _soc_l3x_mem_bucket_map_entry_shift 725 * Purpose: 726 * Shift entry in the bucket map to a new index. 727 * Parameters: 728 * unit - (IN) SOC device number. 729 * bkt_map - (IN) Bucket L3x Entry map. 730 * idx_src - (IN) Source entry index. 731 * idx_dst - (IN) Destination entry index. 732 * Returns: 733 * SOC_E_XXX 734 */ 735 736 STATIC int 737 _soc_l3x_mem_bucket_map_entry_shift(int unit, soc_mem_l3x_bucket_map_t *bkt_map, 738 int idx_src, int idx_dst) 739 { 740 soc_mem_l3x_entry_info_t bmap_entry; 741 int map_idx; 742 743 /* Input parameters check. */ 744 if (NULL == bkt_map) { 745 return (SOC_E_PARAM); 746 } 747 748 /* Index sanity. */ 749 if((0 > idx_src) || (idx_src > bkt_map->size) || (0 > idx_dst) || 750 (idx_dst > bkt_map->size) || (NULL == bkt_map)) { 751 return (SOC_E_PARAM); 752 } 753 754 if (idx_src == idx_dst) { 755 return (SOC_E_NONE); 756 } 757 758 /* Preserve shifted entry. */ 759 sal_memcpy(&bmap_entry, bkt_map->entry_arr + idx_src, 760 sizeof(soc_mem_l3x_entry_info_t)); 761 762 /* Shift bucket map 1 entry down. */ 763 for (map_idx = idx_src; (map_idx - 1) >= idx_dst; map_idx--) { 764 sal_memcpy(bkt_map->entry_arr + map_idx, 765 bkt_map->entry_arr + map_idx - 1, 766 sizeof(soc_mem_l3x_entry_info_t)); 767 } 768 769 /* Reinsert entry to a new location. */ 770 sal_memcpy(bkt_map->entry_arr + idx_dst, &bmap_entry, 771 sizeof(soc_mem_l3x_entry_info_t)); 772 773 return (SOC_E_NONE); 774 } 775 776 777 /* 778 * Function: 779 * _soc_l3x_mem_bucket_entry_shift 780 * Purpose: 781 * Shift entry in bucket to a different index in the bucket. 782 * Parameters: 783 * unit - (IN) SOC device number. 784 * mem - (IN) Entry memory. 785 * idx_src - (IN) Source entry index. 786 * idx_dst - (IN) Destination entry index. 787 * Returns: 788 * SOC_E_XXX 789 */ 790 791 STATIC int 792 _soc_l3x_mem_bucket_entry_shift(int unit, soc_mem_t mem, 793 int idx_src, int idx_dst) 794 { 795 uint32 entry[SOC_MAX_MEM_WORDS]; /* Hw entry buffer. */ 796 int rv; /* Operation return status. */ 797 798 soc_mem_lock(unit, mem); 799 800 rv = soc_mem_read(unit, mem, MEM_BLOCK_ANY, idx_src, entry); 801 if (SOC_FAILURE(rv)) { 802 soc_mem_unlock(unit, mem); 803 return rv; 804 } 805 806 rv = soc_mem_write(unit, mem, MEM_BLOCK_ALL, idx_dst, entry); 807 if (SOC_FAILURE(rv)) { 808 soc_mem_unlock(unit, mem); 809 return rv; 810 } 811 812 rv = soc_mem_write(unit, mem, MEM_BLOCK_ALL, idx_src, 813 soc_mem_entry_null(unit, mem)); 814 soc_mem_unlock(unit, mem); 815 return rv; 816 } 817 818 /* 819 * Function: 820 * _soc_l3x_mem_bucket_pack 821 * Purpose: 822 * Pack l3x table bucket, to allow wide entry insertion. 823 * Parameters: 824 * unit - (IN) SOC device number. 825 * buffer - (IN) Bucket L3x entries. 826 * bkt_map - (IN) Bucket L3x Entry map inside the buffer. 827 * num_entries - (IN) Number of entries caller attempts to insert. 828 * Returns: 829 * SOC_E_XXX 830 */ 831 832 STATIC int 833 _soc_l3x_mem_bucket_pack(int unit, uint32 *buffer, 834 soc_mem_l3x_bucket_map_t *bkt_map, int num_entries) 835 { 836 soc_mem_l3x_entry_info_t *entry; /* Bucket map entry. */ 837 int bucket_idx; /* Bucket iteration index. */ 838 int map_idx; /* Installed entries iteration index. */ 839 int lkup_idx; /* Gap fill entry lookup index. */ 840 int free_slot_size; /* Number of free entries in base memory. */ 841 int idx_src; /* Shifted entry source. */ 842 int idx_dst; /* Shifted entry destination. */ 843 int packed; /* At least 1 entry was moved to an empty slot. */ 844 int pack_candidate; /* At least 1 entry fits to an empty slot. */ 845 846 847 /* Input parameters check. */ 848 if ((NULL == buffer) || (NULL == bkt_map)) { 849 return (SOC_E_PARAM); 850 } 851 852 /* Sort entries by index in the bucket. */ 853 _shr_sort(bkt_map->entry_arr, bkt_map->size, 854 sizeof(soc_mem_l3x_entry_info_t), _soc_l3x_compare_index); 855 856 /* Iterate over base entries in the bucket. */ 857 /* Stop 1) at the end of the bucket. 2) No more installed entries. */ 858 for (bucket_idx = 0, map_idx = 0; 859 ((bucket_idx < bkt_map->total_count) && (map_idx < bkt_map->size));) { 860 861 entry = bkt_map->entry_arr + map_idx; 862 863 /* Get free slot size. */ 864 free_slot_size = entry->index - bucket_idx; 865 866 /* No gap move to the next entry. */ 867 if (0 == free_slot_size) { 868 bucket_idx += entry->size; 869 map_idx++; 870 continue; 871 } 872 873 /* Bucket has enough room for the inserted entry.*/ 874 if (free_slot_size >= num_entries) { 875 if ((!bucket_idx) || ((bucket_idx >= num_entries) && 876 (0 == (bucket_idx % num_entries)))) { 877 return (SOC_E_NONE); 878 } 879 } 880 881 /* Fill the gap. */ 882 while (free_slot_size) { 883 pack_candidate = FALSE; 884 packed = FALSE; 885 /* Search for entry fitting into the free slot. */ 886 for (lkup_idx = bkt_map->size - 1; lkup_idx >= map_idx; lkup_idx--) { 887 entry = bkt_map->entry_arr + lkup_idx; 888 889 /* Check if candidate fits into the free slot. */ 890 if(entry->size <= free_slot_size) { 891 pack_candidate = TRUE; 892 /* Check candidate allignment restrictions. */ 893 if (((!bucket_idx) || ((bucket_idx >= entry->size) && 894 (0 == (bucket_idx % entry->size))))) { 895 896 idx_src = (bkt_map->base_idx + entry->index)/entry->size; 897 idx_dst = (bkt_map->base_idx + bucket_idx)/entry->size; 898 899 /* Shift hw entry. */ 900 SOC_IF_ERROR_RETURN 901 (_soc_l3x_mem_bucket_entry_shift(unit, entry->mem, 902 idx_src, idx_dst)); 903 904 /* Map entry new index is free slot start index. */ 905 entry->index = bucket_idx; 906 907 /* Free slot start index moved down by entry size. */ 908 bucket_idx += entry->size; 909 910 /* Free slot size is decreased by entry size. */ 911 free_slot_size -= entry->size; 912 913 /* Swap entries in backet map to keep order. */ 914 SOC_IF_ERROR_RETURN 915 (_soc_l3x_mem_bucket_map_entry_shift(unit, bkt_map, 916 lkup_idx, map_idx)); 917 /* Move to the next entry in the bucket. */ 918 map_idx++; 919 packed = TRUE; 920 break; 921 } 922 } 923 } 924 if (FALSE == packed) { 925 /* There is no entry fitting into the slot move on. */ 926 bucket_idx += (pack_candidate) ? 1 : free_slot_size; 927 free_slot_size -= (pack_candidate) ? 1 : free_slot_size; 928 } 929 } 930 } 931 return (SOC_E_NONE); 932 } 933 /* 934 * Function: 935 * _soc_l3x_mem_range_read 936 * Purpose: 937 * Read l3x table into allocated buffer. 938 * Parameters: 939 * unit - (IN) SOC device number. 940 * base_mem - (IN) Base memory name. 941 * base_index - (IN) Bucket start index. 942 * ent_count - (IN) Number of entries to read. 943 * buffer - (OUT) Allocated pointer filled with entries. 944 * bucket_map - (OUT) Entry map inside the buffer. 945 * Returns: 946 * SOC_E_XXX 947 */ 948 949 STATIC int 950 _soc_l3x_mem_range_read (int unit, 951 soc_mem_t base_mem, 952 int base_index, 953 uint8 ent_count, 954 uint32 *buffer, 955 soc_mem_l3x_bucket_map_t *bucket_map) 956 { 957 uint32 *entry_ptr; /* Entry read pointer. */ 958 int entry_size; /* Resolved entry size. */ 959 soc_mem_t entry_mem; /* Resolved entry memory. */ 960 int counter; /* Bucket map entries counter. */ 961 int idx_max; /* Last index to read. */ 962 int idx; /* Iteration index. */ 963 964 /* Input parameters check. */ 965 if ((NULL == buffer) || (!SOC_MEM_IS_VALID(unit, base_mem)) || 966 (!ent_count) || (NULL == bucket_map)) { 967 return (SOC_E_PARAM); 968 } 969 970 /* Start/End indexes sanity. */ 971 idx_max = base_index + ent_count - 1; 972 if ((base_index < soc_mem_index_min(unit, base_mem)) || 973 (idx_max > soc_mem_index_max(unit, base_mem))) { 974 return (SOC_E_PARAM); 975 } 976 977 /* Read entries into the buffer. */ 978 SOC_IF_ERROR_RETURN (soc_mem_read_range(unit, base_mem, MEM_BLOCK_ANY, 979 base_index, idx_max, buffer)); 980 981 /* Stack variables initialization. */ 982 entry_size = 0; 983 counter = 0; 984 985 /* Parse the buffer. */ 986 for (idx = 0; idx < ent_count; idx += entry_size) { 987 entry_ptr = soc_mem_table_idx_to_pointer(unit, base_mem, uint32 *, \ 988 buffer, idx); 989 990 /* Get entry memory view & relative to base memory size. */ 991 SOC_IF_ERROR_RETURN(_soc_l3x_entry_mem_view_get(unit, base_mem, 992 entry_ptr, &entry_mem, 993 &entry_size)); 994 995 /* Skip invalid entries. */ 996 if (INVALIDm == entry_mem) { 997 continue; 998 } 999 1000 /* Initializes bucket_map of valid entry. */ 1001 bucket_map->entry_arr[counter].size = entry_size; 1002 bucket_map->entry_arr[counter].mem = entry_mem; 1003 bucket_map->entry_arr[counter].flags |= SOC_MEM_L3X_ENTRY_VALID; 1004 /* Set entry absolute index in the base view. */ 1005 bucket_map->entry_arr[counter].index = idx; 1006 /* Set number of valid entries in bucket. */ 1007 bucket_map->valid_count += entry_size; 1008 /* Increment map size. */ 1009 counter++; 1010 } 1011 1012 /* Set map size. */ 1013 bucket_map->size = counter; 1014 bucket_map->base_idx = base_index; 1015 bucket_map->base_mem = base_mem; 1016 bucket_map->total_count = ent_count; 1017 1018 return (SOC_E_NONE); 1019 } 1020 1021 1022 /* 1023 * Function: 1024 * _soc_l3x_mem_bucket_pack_insert 1025 * Purpose: 1026 * Pack l3x entries bucket & reattempt insert operation. 1027 * 1028 * If dual hash is disabled/not supported we still 1029 * might be able to insert wide entries after bucket 1030 * packing. 1031 * Parameters: 1032 * uint - (IN) BCM device number. 1033 * entry_data - (IN) Inserted entry data. 1034 * Return: 1035 * SOC_E_XXX 1036 */ 1037 1038 STATIC int 1039 _soc_l3x_mem_bucket_pack_insert (int unit, void *entry_data) 1040 { 1041 soc_mem_l3x_bucket_map_t bkt_map; /* L3X bucket entries map. */ 1042 uint32 *buffer; /* L3X table bucket snapshot. */ 1043 int alloc_size; /* Allocation buffer size. */ 1044 int bucket_size; /* Number of entries in bucket. */ 1045 int bucket_index; /* Entry index in base L3X memory. */ 1046 soc_mem_t mem; /* Entry view memory. */ 1047 int num_entries; /* Number of base slots entry needs.*/ 1048 int free_slots = 0; /* Number of free entries. */ 1049 int rv; /* Operation return status. */ 1050 1051 SOC_IF_ERROR_RETURN 1052 (_soc_l3x_entry_mem_view_get(unit, L3_ENTRY_IPV4_UNICASTm, 1053 entry_data, &mem, &num_entries)); 1054 1055 if (INVALIDm == mem) { 1056 return (SOC_E_INTERNAL); 1057 } 1058 1059 /* There is no reason to pack if entry needs a single slot. */ 1060 if (num_entries == 1) { 1061 return (SOC_E_FULL); 1062 } 1063 1064 /* Stack variales initialization & memory allocations.*/ 1065 sal_memset(&bkt_map, 0, sizeof (soc_mem_l3x_bucket_map_t)); 1066 bucket_size = SOC_L3X_BUCKET_SIZE(unit); 1067 1068 /* Calcualte memory required to create bucket snapshot. */ 1069 alloc_size = bucket_size * 1070 WORDS2BYTES(soc_mem_entry_words(unit, L3_ENTRY_IPV4_UNICASTm)); 1071 1072 /* Allocate buffer for snapshot. */ 1073 buffer = soc_cm_salloc(unit, alloc_size, "L3X bucket image"); 1074 if (NULL == buffer) { 1075 return (SOC_E_MEMORY); 1076 } 1077 /* Reset allocated buffer. */ 1078 sal_memset(buffer, 0, alloc_size); 1079 1080 1081 /* Allocate buffer for valid entries map. */ 1082 alloc_size = bucket_size * sizeof(soc_mem_l3x_entry_info_t); 1083 bkt_map.entry_arr = sal_alloc(alloc_size, "L3X Entries Info"); 1084 if (NULL == bkt_map.entry_arr) { 1085 soc_cm_sfree(unit, buffer); 1086 return (SOC_E_MEMORY); 1087 } 1088 sal_memset(bkt_map.entry_arr, 0, alloc_size); 1089 1090 /* Calculate entry hash value. */ 1091 bucket_index = bucket_size * 1092 soc_fb_l3x2_entry_hash(unit, (uint32 *)entry_data); 1093 1094 /* Read all entries in the bucket. */ 1095 rv = _soc_l3x_mem_range_read(unit, L3_ENTRY_IPV4_UNICASTm, 1096 bucket_index, bucket_size, 1097 buffer, &bkt_map); 1098 if (SOC_FAILURE(rv)) { 1099 soc_cm_sfree(unit, buffer); 1100 sal_free(bkt_map.entry_arr); 1101 return rv; 1102 } 1103 free_slots = bucket_size - bkt_map.valid_count; 1104 /* Return E_FULL if failed to free the space. */ 1105 if (free_slots < num_entries) { 1106 soc_cm_sfree(unit, buffer); 1107 sal_free(bkt_map.entry_arr); 1108 return (SOC_E_FULL); 1109 } 1110 1111 /* Pack entries in the bucket & reinsert original one.*/ 1112 rv = _soc_l3x_mem_bucket_pack(unit, buffer, &bkt_map, num_entries); 1113 if (SOC_FAILURE(rv)) { 1114 soc_cm_sfree(unit, buffer); 1115 sal_free(bkt_map.entry_arr); 1116 return rv; 1117 } 1118 1119 /* Insert orignal entry. . */ 1120 rv = soc_fb_l3x_bank_insert(unit, 0, entry_data); 1121 1122 /* Free allocated resources. */ 1123 soc_cm_sfree(unit, buffer); 1124 sal_free(bkt_map.entry_arr); 1125 return rv; 1126 } 1127 1128 #if defined(BCM_FIREBOLT2_SUPPORT) || defined(BCM_TRX_SUPPORT) || \ 1129 defined(BCM_RAVEN_SUPPORT) 1130 /* 1131 * Function: 1132 * _soc_l3x_mem_moved_entry_flush 1133 * Purpose: 1134 * Remove moved entries from the bucket 1135 * Parameters: 1136 * unit - (IN) SOC device number. 1137 * bucket_map - (IN) Bucket L3x Entry map inside the buffer. 1138 * Returns: 1139 * SOC_E_XXX 1140 */ 1141 1142 STATIC int 1143 _soc_l3x_mem_moved_entry_flush(int unit, soc_mem_l3x_bucket_map_t *bucket_map) 1144 { 1145 soc_mem_l3x_entry_info_t *entry; /* Bucket map entry. */ 1146 int entry_index; /* Entry index in a view. */ 1147 int idx; /* Bucket iteration index. */ 1148 int map_idx; /* Bucket map iteration index. */ 1149 1150 /* Input parameters check. */ 1151 if (NULL == bucket_map) { 1152 return (SOC_E_PARAM); 1153 } 1154 1155 for (idx = 0; idx < bucket_map->size; idx++) { 1156 entry = bucket_map->entry_arr + idx; 1157 if (entry->flags & SOC_MEM_L3X_ENTRY_MOVED) { 1158 entry_index = (entry->index + bucket_map->base_idx) / entry->size; 1159 1160 SOC_IF_ERROR_RETURN 1161 (soc_mem_write(unit, entry->mem, SOC_BLOCK_ALL, entry_index, 1162 soc_mem_entry_null(unit, entry->mem))); 1163 1164 /* Pack bucket map. */ 1165 for (map_idx = idx; (map_idx + 1) < bucket_map->size; map_idx++) { 1166 sal_memcpy(bucket_map->entry_arr + map_idx, 1167 bucket_map->entry_arr + map_idx + 1, 1168 sizeof(soc_mem_l3x_entry_info_t)); 1169 } 1170 1171 bucket_map->size--; 1172 bucket_map->valid_count -= entry->size; 1173 /* Reset last entry in the map. */ 1174 sal_memset(bucket_map->entry_arr + map_idx, 0, 1175 sizeof(soc_mem_l3x_entry_info_t)); 1176 idx--; 1177 } 1178 } 1179 return (SOC_E_NONE); 1180 } 1181 1182 /* 1183 * Function: 1184 * _soc_l3x_mem_dual_hash_move 1185 * Purpose: 1186 * Recursive move routine for dual hash auto-move inserts 1187 * Assumes feature already checked, mem locks taken. 1188 * Parameters: 1189 * uint - (IN) BCM device number. 1190 * mem - (IN) Inserted entry memory. 1191 * banks - (IN) Destination banks to insert the entry. 1192 * entry_data - (IN) Inserted entry data. 1193 * hash_info - (IN) Hash select information. 1194 * num_entries - (IN) Inserted entry width. 1195 * bucket_trace - (IN) Trace of buckets affected by recursion. 1196 * recurse - (IN) Be in recursion or not. 1197 * recurse_depth - (IN) Maximum recursion depth. 1198 * Returns: 1199 * SOC_E_XXX 1200 * Notes: 1201 * The ugliness here is the XGS3 L3 table. It contains 4 different 1202 * L3 entry types in one hash structure. A given logical entry 1203 * may take up 1, 2, or 4 memory lines. Since a different type 1204 * might be blocking the entry to insert, we need to account 1205 * for the complexity of moving multiple entries in that case. 1206 * See further comments inline. 1207 */ 1208 1209 STATIC int 1210 _soc_l3x_mem_dual_hash_move(int unit, 1211 soc_mem_t mem, /* Assumes memory locked */ 1212 uint8 banks, 1213 void *entry_data, 1214 dual_hash_info_t *hash_info, 1215 int num_entries, 1216 SHR_BITDCL *bucket_trace, 1217 int recurse, 1218 int recurse_depth) 1219 { 1220 soc_mem_l3x_entry_info_t *entry_arr; /* Bucket map entry array. */ 1221 soc_mem_l3x_entry_info_t *entry; /* Bucket map entry. */ 1222 soc_mem_l3x_bucket_map_t bkt_map; /* L3X bucket entries map. */ 1223 uint32 move_entry[SOC_MAX_MEM_WORDS];/* Entry moved to another bank. */ 1224 int that_bank_only; /* Destination bank. */ 1225 int this_bank_bit; /* Current bank id indicator */ 1226 int half_bucket; /* Number of entries in half bucket.*/ 1227 int total_moved; /* Number of moved entries. */ 1228 int free_slots = 0; /* Number of free entries. */ 1229 int bucket_index = 0; /* Entry index in base L3X memory. */ 1230 uint32 *buffer; /* L3X table bucket snapshot. */ 1231 int alloc_size; /* Allocation buffer size. */ 1232 int this_hash; /* Hash selection in current bank. */ 1233 int that_hash; /* Hash selection in other bank. */ 1234 int hash_base; /* Inserted entry hash value. */ 1235 int dest_hash_base; /* Moved entry hash value. */ 1236 int dest_bucket_index; /* Moved entry index in base L3X. */ 1237 int bix; /* Bank iterator. */ 1238 int idx; /* Bucket iteration index. */ 1239 int trace_size; /* Recursion trace array size. */ 1240 SHR_BITDCL *trace; /* Buckets involved in recursion. */ 1241 int rv = SOC_E_NONE; /* Operation return status. */ 1242 1243 1244 /* Maximum recursion depth reached check. */ 1245 if (recurse_depth < 0) { 1246 return (SOC_E_FULL); 1247 } 1248 1249 /* Stack variales initialization & memory allocations.*/ 1250 half_bucket = hash_info->bucket_size / 2; 1251 1252 /* Calcualte memory required to create bucket snapshot. */ 1253 alloc_size = half_bucket * 1254 WORDS2BYTES(soc_mem_entry_words(unit, hash_info->base_mem)); 1255 1256 /* Allocate buffer for snapshot. */ 1257 buffer = soc_cm_salloc(unit, alloc_size, "L3X bucket image"); 1258 if (NULL == buffer) { 1259 return (SOC_E_MEMORY); 1260 } 1261 /* Reset allocated buffer. */ 1262 sal_memset(buffer, 0, alloc_size); 1263 1264 1265 /* Allocate buffer for valid entries map. */ 1266 alloc_size = half_bucket * sizeof(soc_mem_l3x_entry_info_t); 1267 entry_arr = sal_alloc(alloc_size, "L3X Entries Info"); 1268 if (NULL == entry_arr) { 1269 soc_cm_sfree(unit, buffer); 1270 return (SOC_E_MEMORY); 1271 } 1272 1273 /* Keep back trace of all buckets affected by recursion. */ 1274 trace_size = 1275 SHR_BITALLOCSIZE(soc_mem_index_count(unit, hash_info->base_mem)); 1276 if (NULL == bucket_trace) { 1277 trace = sal_alloc(trace_size, "Dual hash"); 1278 if (NULL == trace) { 1279 sal_free(entry_arr); 1280 soc_cm_sfree(unit, buffer); 1281 return (SOC_E_MEMORY); 1282 } 1283 } else { 1284 trace = bucket_trace; 1285 } 1286 1287 /* Iterate over memory banks trying to free space for inserted entry.*/ 1288 for (bix = 0; bix < 2; bix++) { 1289 1290 if (bix == 0) { 1291 this_bank_bit = SOC_MEM_HASH_BANK0_BIT; 1292 that_bank_only = SOC_MEM_HASH_BANK1_ONLY; 1293 this_hash = hash_info->hash_sel0; 1294 that_hash = hash_info->hash_sel1; 1295 } else { 1296 this_bank_bit = SOC_MEM_HASH_BANK1_BIT; 1297 that_bank_only = SOC_MEM_HASH_BANK0_ONLY; 1298 this_hash = hash_info->hash_sel1; 1299 that_hash = hash_info->hash_sel0; 1300 } 1301 1302 if (banks & this_bank_bit) { 1303 /* Not this bank */ 1304 continue; 1305 } 1306 1307 /* Calculate entry hash value. */ 1308 hash_base = soc_fb_l3x_entry_hash(unit, this_hash, entry_data); 1309 bucket_index = hash_base * hash_info->bucket_size + bix * half_bucket; 1310 1311 /* Recursion trace initialization. */ 1312 if (NULL == bucket_trace) { 1313 sal_memset(trace, 0, trace_size); 1314 } 1315 SHR_BITSET(trace, bucket_index); 1316 1317 /* Reset bucket entries map. */ 1318 /* Allocation size is preserved across the loop. */ 1319 total_moved = 0; 1320 sal_memset(entry_arr, 0, alloc_size); 1321 sal_memset(&bkt_map, 0, sizeof (soc_mem_l3x_bucket_map_t)); 1322 bkt_map.entry_arr = entry_arr; 1323 /* Read all entries in the bucket. */ 1324 rv = _soc_l3x_mem_range_read(unit, hash_info->base_mem, bucket_index, 1325 half_bucket, buffer, &bkt_map); 1326 if (SOC_FAILURE(rv)) { 1327 break; 1328 } 1329 1330 free_slots = half_bucket - bkt_map.valid_count; 1331 /* Check if bucket packing is sufficient. */ 1332 if (free_slots >= num_entries) { 1333 break; 1334 } 1335 1336 /* 1337 * Sort entries by size. There is a greater chance to 1338 * shift small entries vs large ones. 1339 */ 1340 _shr_sort(bkt_map.entry_arr, bkt_map.size, 1341 sizeof(soc_mem_l3x_entry_info_t), _soc_l3x_compare_size); 1342 1343 1344 /* Move entries from shorted to longest. */ 1345 /* There is a higher chance to move shorter entry. */ 1346 for (idx = bkt_map.size - 1 ; idx >= 0; idx--) { 1347 1348 entry = entry_arr + idx; 1349 rv = soc_mem_read(unit, entry->mem, MEM_BLOCK_ANY, 1350 (entry->index + bkt_map.base_idx) / entry->size, 1351 move_entry); 1352 if (SOC_FAILURE(rv)) { 1353 break; 1354 } 1355 /* Are we already in recursion or will we be in recursion */ 1356 if (recurse || recurse_depth) { 1357 /* Calculate destination entry hash value. */ 1358 dest_hash_base = soc_fb_l3x_entry_hash(unit, that_hash, move_entry); 1359 dest_bucket_index = 1360 dest_hash_base * hash_info->bucket_size + (!bix) * half_bucket; 1361 1362 /* Make sure we are not touching buckets in bucket trace. */ 1363 if(SHR_BITGET(trace, dest_bucket_index)) { 1364 continue; 1365 } 1366 } 1367 1368 /* Move entry to the other bank. */ 1369 rv = soc_fb_l3x_bank_insert(unit, that_bank_only, 1370 (void *)move_entry); 1371 1372 if (SOC_FAILURE(rv)) { 1373 if (rv != SOC_E_FULL) { 1374 break; 1375 } 1376 /* Recursive call. */ 1377 rv = _soc_l3x_mem_dual_hash_move(unit, entry->mem, 1378 that_bank_only, 1379 move_entry, hash_info, 1380 entry->size, trace, 1381 TRUE, recurse_depth - 1); 1382 } 1383 1384 if (SOC_SUCCESS(rv)) { 1385 total_moved++; 1386 free_slots += entry->size; 1387 entry->flags |= SOC_MEM_L3X_ENTRY_MOVED; 1388 entry->flags &= ~SOC_MEM_L3X_ENTRY_VALID; 1389 1390 if (free_slots >= num_entries) { 1391 break; 1392 } 1393 } 1394 } 1395 1396 /* Remove moved entries. */ 1397 if (total_moved) { 1398 rv = _soc_l3x_mem_moved_entry_flush(unit, &bkt_map); 1399 if (SOC_FAILURE(rv)) { 1400 soc_cm_sfree(unit, buffer); 1401 sal_free(entry_arr); 1402 if (NULL == bucket_trace) sal_free(trace); 1403 return rv; 1404 } 1405 } 1406 1407 /* Sufficient space was freed to accomodate the entry. */ 1408 if (free_slots >= num_entries) { 1409 break; 1410 } 1411 1412 /* If any error happened stop. */ 1413 if (SOC_FAILURE(rv) && (rv != SOC_E_FULL)) { 1414 break; 1415 } 1416 1417 } /* Loop over the banks. */ 1418 1419 /* Return if any error occured */ 1420 if (SOC_FAILURE(rv)) { 1421 soc_cm_sfree(unit, buffer); 1422 sal_free(entry_arr); 1423 if (NULL == bucket_trace) sal_free(trace); 1424 return rv; 1425 } 1426 1427 /* Return E_FULL if failed to free the space. */ 1428 if (free_slots < num_entries) { 1429 soc_cm_sfree(unit, buffer); 1430 sal_free(entry_arr); 1431 if (NULL == bucket_trace) sal_free(trace); 1432 return (SOC_E_FULL); 1433 } 1434 1435 /* If inserted entry size is > 1 pack the bucket. */ 1436 if (num_entries > 1) { 1437 /* Pack entries in the bucket & reinsert original one.*/ 1438 rv = _soc_l3x_mem_bucket_pack(unit, buffer, &bkt_map, num_entries); 1439 if (SOC_FAILURE(rv)) { 1440 soc_cm_sfree(unit, buffer); 1441 sal_free(entry_arr); 1442 if (NULL == bucket_trace) sal_free(trace); 1443 return rv; 1444 } 1445 } 1446 1447 /* Insert orignal entry. . */ 1448 rv = soc_fb_l3x_bank_insert(unit, banks, 1449 (l3_entry_ipv6_multicast_entry_t *)entry_data); 1450 /* Free allocated resources. */ 1451 soc_cm_sfree(unit, buffer); 1452 sal_free(entry_arr); 1453 if (NULL == bucket_trace) sal_free(trace); 1454 return rv; 1455 } 1456 1457 /* 1458 * Function: 1459 * _soc_mem_l3x_dual_hash_insert 1460 * Purpose: 1461 * Dual hash auto-move inserts 1462 * Parameters: 1463 * uint - (IN) BCM device number. 1464 * entry_data - (IN) Inserted entry data. 1465 * recurse_depth - (IN) Maximum recursion depth. 1466 * Returns: 1467 * SOC_E_XXX 1468 */ 1469 1470 STATIC int 1471 _soc_mem_l3x_dual_hash_insert (int unit, 1472 void *entry_data, 1473 int recurse_depth) 1474 { 1475 dual_hash_info_t hash_info; /* Hash selections. */ 1476 soc_mem_t mem; /* Entry memory. */ 1477 int rv; /* Operation return status. */ 1478 int num_entries = 0; /* Entry width. */ 1479 1480 SOC_IF_ERROR_RETURN 1481 (_soc_l3x_entry_mem_view_get(unit, L3_ENTRY_IPV4_UNICASTm, 1482 entry_data, &mem, &num_entries)); 1483 1484 if (INVALIDm == mem) { 1485 return (SOC_E_INTERNAL); 1486 } 1487 1488 rv = soc_fb_l3x_bank_insert(unit, 0, 1489 (l3_entry_ipv6_multicast_entry_t *)entry_data); 1490 if (rv != SOC_E_FULL || recurse_depth == 0) { 1491 return rv; 1492 } 1493 1494 SOC_IF_ERROR_RETURN 1495 (soc_fb_l3x_entry_bank_hash_sel_get(unit, 0, 1496 &(hash_info.hash_sel0))); 1497 SOC_IF_ERROR_RETURN 1498 (soc_fb_l3x_entry_bank_hash_sel_get(unit, 1, 1499 &(hash_info.hash_sel1))); 1500 if (hash_info.hash_sel0 == hash_info.hash_sel1) { 1501 /* Can't juggle the entries */ 1502 return _soc_l3x_mem_bucket_pack_insert(unit, entry_data); 1503 } 1504 1505 hash_info.bucket_size = SOC_L3X_BUCKET_SIZE(unit); 1506 hash_info.base_mem = L3_ENTRY_IPV4_UNICASTm; 1507 1508 /* Time to shuffle the entries */ 1509 1510 rv = _soc_l3x_mem_dual_hash_move(unit, mem, SOC_MEM_HASH_BANK_BOTH, 1511 entry_data, &hash_info, num_entries, 1512 NULL, FALSE, recurse_depth - 1); 1513 return (rv); 1514 } 1515 #endif /* BCM_FIREBOLT2_SUPPORT || BCM_RAVEN_SUPPORT || BCM_TRX_SUPPORT */ 1516 1517 /* 1518 */ 1519 1520 /* 1521 * Function: 1522 * soc_fb_l3x_insert 1523 * Purpose: 1524 * Original non-bank versions of the FB L3X table op functions 1525 * Parameters: 1526 * uint - (IN) BCM device number. 1527 * entry - (IN) Inserted entry data. 1528 * Returns: 1529 * SOC_E_XXX 1530 */ 1531 int 1532 soc_fb_l3x_insert(int unit, l3_entry_ipv6_multicast_entry_t *entry) 1533 { 1534 int rv; /* Operation return status. */ 1535 1536 /* COVERITY : Intentional , Stack use of 5064 bytes */ 1537 /* coverity[stack_use_callee_max : FALSE] */ 1538 /* coverity[stack_use_overflow : FALSE] */ 1539 /* soc_fb_l3x_insert uses 4064 bytes of stack space which is intentional */ 1540 SOC_IF_ERROR_RETURN(soc_l3x_lock(unit)); 1541 #if defined(BCM_FIREBOLT2_SUPPORT) || defined(BCM_TRX_SUPPORT) || \ 1542 defined(BCM_RAVEN_SUPPORT) 1543 if (soc_feature(unit, soc_feature_dual_hash)) { 1544 rv = _soc_mem_l3x_dual_hash_insert(unit, (void *)entry, 1545 SOC_DUAL_HASH_MOVE_MAX_L3X(unit)); 1546 } else 1547 #endif /* BCM_FIREBOLT2_SUPPORT || BCM_RAVEN_SUPPORT || BCM_TRX_SUPPORT */ 1548 { 1549 rv = soc_fb_l3x_bank_insert(unit, 0, entry); 1550 if (SOC_FAILURE(rv) && (SOC_E_FULL == rv)) { 1551 rv = _soc_l3x_mem_bucket_pack_insert (unit, entry); 1552 } 1553 } 1554 SOC_IF_ERROR_RETURN(soc_l3x_unlock(unit)); 1555 return (rv); 1556 } 1557 1558 int 1559 soc_fb_l3x_delete(int unit, l3_entry_ipv6_multicast_entry_t *entry) 1560 { 1561 return soc_fb_l3x_bank_delete(unit, 0, entry); 1562 } 1563 1564 int 1565 soc_fb_l3x_lookup(int unit, l3_entry_ipv6_multicast_entry_t *key, 1566 l3_entry_ipv6_multicast_entry_t *result, int *index_ptr) 1567 { 1568 return soc_fb_l3x_bank_lookup(unit, 0, key, result, index_ptr); 1569 } 1570 #endif /* BCM_FIREBOLT_SUPPORT */ 1571 1572 1573 #endif /* BCM_XGS_SWITCH_SUPPORT */ 1574 #endif /* INCLUDE_L3 */