rh_cmn.c (108026B)
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: rh.c 8 * Purpose: Tomahawk / Trident3 Resilient Hashing function implementations 9 */ 10 11 #include <shared/bsl.h> 12 13 #include <soc/defs.h> 14 #if defined(INCLUDE_L3) && defined(BCM_TOMAHAWK_SUPPORT) 15 16 #include <soc/drv.h> 17 #include <soc/scache.h> 18 #include <bcm/error.h> 19 #include <bcm/debug.h> 20 #include <soc/tomahawk.h> 21 #include <bcm_int/esw/tomahawk.h> 22 #include <bcm_int/esw/firebolt.h> 23 #if defined(BCM_TOMAHAWK3_SUPPORT) 24 #include <bcm_int/esw/tomahawk3.h> 25 #endif /* BCM_TOMAHAWK3_SUPPORT */ 26 27 /* RH info per ecmp group */ 28 typedef struct _th_ecmp_rh_group_s { 29 bcm_if_t *rh_intf_arr; /* ECMP members */ 30 uint16 data_hash; /* data hash of sorted ECMP member list */ 31 int rh_intf_count; /* Number of ECMP members */ 32 uint16 max_paths; /* Number of ECMP members */ 33 uint8 enable; 34 } _ecmp_rh_group_t; 35 36 /* Bookkeeping info for ECMP resilient hashing */ 37 typedef struct _th_ecmp_rh_info_s { 38 uint32 ecmp_rh_rand_seed; /* The seed for pseudo-random number generator */ 39 _ecmp_rh_group_t *rhg; 40 } _opt_ecmp_rh_info_t; 41 42 STATIC _opt_ecmp_rh_info_t *_opt_ecmp_rh_info[BCM_MAX_NUM_UNITS]; 43 44 #if defined(BCM_TOMAHAWK_SUPPORT) 45 extern int ecmp_mode_hierarchical; 46 #endif 47 48 /* RH member */ 49 typedef struct _ecmp_rh_member_s { 50 int nh_index; /* Next hop index of the member */ 51 int member_id; /* Member ID */ 52 int num_replica; /* Number of members with the same next hop index as 53 this member. Valid only for the first replica. */ 54 int replica_id; /* Index among members with the same next hop index as 55 this member. Valid for every replica. */ 56 int next_replica_id; /* Index of the next replica to be assigned to 57 a rh set entry containing the next hop index 58 shared by replicas. Valid only for the first 59 replica. */ 60 } _ecmp_rh_member_t; 61 62 /*----- STATIC FUNCS ----- */ 63 STATIC int 64 _bcm_opt_rh_ecmp_grp_hash_calc(int unit, void *buf, uint16 *hash) 65 { 66 67 if ((NULL == buf) || (NULL == hash)) { 68 return (BCM_E_PARAM); 69 } 70 71 /* Calculate hash of next hop indexes, which are members in the group. */ 72 *hash = _shr_crc16(0, (uint8 *) buf, 73 BCM_XGS3_L3_ECMP_MAX_PATHS(unit) * sizeof(int)); 74 75 return (BCM_E_NONE); 76 } 77 78 /* 79 * Compare function used to sort ECMP members 80 */ 81 static INLINE int 82 _opt_rh_cmp_int(void *a, void *b) 83 { 84 int first; /* First compared integer. */ 85 int second; /* Second compared integer. */ 86 87 first = *(int *)a; 88 second = *(int *)b; 89 90 if (first < second) { 91 return (BCM_L3_CMP_LESS); 92 } else if (first > second) { 93 return (BCM_L3_CMP_GREATER); 94 } 95 return (BCM_L3_CMP_EQUAL); 96 } 97 98 /* 99 * Function: 100 * bcm_opt_ecmp_rh_deinit 101 * Purpose: 102 * Deallocate ECMP resilient hashing internal data structures 103 * Parameters: 104 * unit - (IN) SOC unit number. 105 * Returns: 106 * BCM_E_xxx 107 */ 108 int 109 bcm_opt_ecmp_rh_deinit(int unit) 110 { 111 int i; 112 bcm_if_t **rh_intf_arr_ptr; 113 if (_opt_ecmp_rh_info[unit]) { 114 if (_opt_ecmp_rh_info[unit]->rhg) { 115 for (i = 0; i < BCM_XGS3_L3_ECMP_MAX_GROUPS(unit); i++) { 116 /* Free ECMP member array */ 117 rh_intf_arr_ptr = &(_opt_ecmp_rh_info[unit]->rhg[i].rh_intf_arr); 118 if (*rh_intf_arr_ptr) { 119 sal_free(*rh_intf_arr_ptr); 120 *rh_intf_arr_ptr = NULL; 121 } 122 } 123 sal_free(_opt_ecmp_rh_info[unit]->rhg); 124 } 125 sal_free(_opt_ecmp_rh_info[unit]); 126 _opt_ecmp_rh_info[unit] = NULL; 127 } 128 return BCM_E_NONE; 129 } 130 131 /* 132 * Function: 133 * bcm_opt_ecmp_rh_init 134 * Purpose: 135 * Initialize ECMP resilient hashing internal data structures 136 * Parameters: 137 * unit - (IN) SOC unit number. 138 * Returns: 139 * BCM_E_xxx 140 */ 141 int 142 bcm_opt_ecmp_rh_init(int unit) 143 { 144 int rv = BCM_E_NONE; 145 146 bcm_opt_ecmp_rh_deinit(unit); 147 148 _opt_ecmp_rh_info[unit] = sal_alloc(sizeof(_opt_ecmp_rh_info_t), 149 "_opt_ecmp_rh_info"); 150 if (_opt_ecmp_rh_info[unit] == NULL) { 151 return BCM_E_MEMORY; 152 } 153 sal_memset(_opt_ecmp_rh_info[unit], 0, sizeof(_opt_ecmp_rh_info_t)); 154 155 _opt_ecmp_rh_info[unit]->rhg = sal_alloc( 156 sizeof(_ecmp_rh_group_t) * BCM_XGS3_L3_ECMP_MAX_GROUPS(unit), 157 "_opt_ecmp_rh_info"); 158 if (_opt_ecmp_rh_info[unit]->rhg == NULL) { 159 return BCM_E_MEMORY; 160 } 161 sal_memset(_opt_ecmp_rh_info[unit]->rhg, 0, 162 sizeof(_ecmp_rh_group_t) * BCM_XGS3_L3_ECMP_MAX_GROUPS(unit)); 163 164 /* Set the seed for the pseudo-random number generator */ 165 _opt_ecmp_rh_info[unit]->ecmp_rh_rand_seed = sal_time_usecs(); 166 167 return rv; 168 } 169 170 /* 171 * Function: 172 * bcm_opt_ecmp_rh_set_intf_arr 173 * Purpose: 174 * Store ECMP members in the rh info data structure 175 * Parameters: 176 * unit - (IN) SOC unit number. 177 * intf_count - (IN) ECMP member count. 178 * intf_array - (IN) ECMP member array. 179 * ecmp_group_idx - (IN) ECMP group ID. 180 * group_size - (IN) RH ECMP group size. 181 * Returns: 182 * BCM_E_xxx 183 */ 184 int 185 bcm_opt_ecmp_rh_set_intf_arr(int unit, 186 int intf_count, 187 bcm_if_t *intf_array, 188 int ecmp_group_idx, 189 int group_size, 190 int max_paths) 191 { 192 bcm_if_t **rh_intf_arr_ptr = NULL; 193 uint16 hash; 194 bcm_if_t *hash_intf_array = NULL; 195 int alloc_size; 196 197 if (intf_array == NULL) { 198 return(BCM_E_INTERNAL); 199 } 200 201 if (BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, ecmp_group_idx + 202 BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit))) { 203 rh_intf_arr_ptr = &(_opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].rh_intf_arr); 204 if (*rh_intf_arr_ptr) { 205 sal_free(*rh_intf_arr_ptr); 206 *rh_intf_arr_ptr = NULL; 207 } 208 *rh_intf_arr_ptr = sal_alloc(sizeof(bcm_if_t) * intf_count, 209 "ECMP RH entry count array"); 210 if (NULL == *rh_intf_arr_ptr) { 211 return(BCM_E_MEMORY); 212 } 213 sal_memset(*rh_intf_arr_ptr, 0, (sizeof(bcm_if_t) * intf_count)); 214 /* Copy ECMP member count */ 215 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].rh_intf_count = intf_count; 216 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].max_paths = max_paths; 217 218 alloc_size = sizeof(bcm_if_t) * BCM_XGS3_L3_ECMP_MAX(unit); 219 hash_intf_array = sal_alloc(alloc_size, "RH intf array"); 220 if (NULL == hash_intf_array) { 221 return(BCM_E_MEMORY); 222 } 223 sal_memset(hash_intf_array, 0, sizeof(bcm_if_t) * BCM_XGS3_L3_ECMP_MAX(unit)); 224 sal_memcpy(hash_intf_array, intf_array, sizeof(bcm_if_t) * intf_count); 225 /* Sort ECMP member array */ 226 _shr_sort(hash_intf_array, intf_count, sizeof(int), _opt_rh_cmp_int); 227 /* Copy sorted ECMP member array */ 228 sal_memcpy(*rh_intf_arr_ptr, hash_intf_array, 229 (sizeof(bcm_if_t) * intf_count)); 230 /* Compute member hash */ 231 _bcm_opt_rh_ecmp_grp_hash_calc(unit, hash_intf_array, &hash); 232 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].data_hash = hash; 233 sal_free(hash_intf_array); 234 } 235 return (BCM_E_NONE); 236 } 237 238 /* 239 * Function: 240 * bcm_th_ecmp_group_rh_set 241 * Purpose: 242 * Set/reset the given ECMP group as a RH group 243 * Parameters: 244 * unit - (IN) SOC unit number. 245 * ecmp_group_idx - (IN) ECMP group ID. 246 * enable - (IN) Enable 247 * Returns: 248 * TRUE or FALSE 249 */ 250 void 251 bcm_th_ecmp_group_rh_set(int unit, int ecmp_group_idx, int enable) 252 { 253 if (ecmp_group_idx > BCM_XGS3_L3_ECMP_MAX_GROUPS(unit)) { 254 LOG_ERROR(BSL_LS_BCM_L3, 255 (BSL_META_U(unit, 256 "Group index %d out of bound\n"), ecmp_group_idx)); 257 return; 258 } 259 260 /* coverity[overrun-local : FALSE] */ 261 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].enable = enable ? 1 : 0; 262 return; 263 } 264 265 /* 266 * Function: 267 * bcm_opt_ecmp_group_is_rh 268 * Purpose: 269 * Check whether the given ECMP group is a RH group 270 * Parameters: 271 * unit - (IN) SOC unit number. 272 * ecmp_group_idx - (IN) ECMP group ID. 273 * Returns: 274 * TRUE or FALSE 275 */ 276 int 277 bcm_opt_ecmp_group_is_rh(int unit, int ecmp_group_idx) 278 { 279 if (!soc_feature(unit, soc_feature_ecmp_resilient_hash_optimized)) { 280 return FALSE; 281 } 282 if (ecmp_group_idx > BCM_XGS3_L3_ECMP_MAX_GROUPS(unit)) { 283 LOG_ERROR(BSL_LS_BCM_L3, 284 (BSL_META_U(unit, 285 "Group index %d out of bound\n"), ecmp_group_idx)); 286 return FALSE; 287 } 288 289 /* coverity[overrun-local : FALSE] */ 290 return _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].enable ? TRUE : FALSE; 291 } 292 293 /* 294 * Function: 295 * bcm_opt_ecmp_rh_multipath_get 296 * Purpose: 297 * Get ECMP members and member count for the group 298 * Parameters: 299 * unit - (IN) SOC unit number. 300 * ecmp_group_idx - (IN) ECMP group ID. 301 * intf_arr - (OUT) ECMP member array. 302 * intf_count - (OUT) ECMP member count. 303 * max_paths - (OUT) ECMP max paths. 304 * Returns: 305 * BCM_E_xxx 306 */ 307 int 308 bcm_opt_ecmp_rh_multipath_get(int unit, int ecmp_group_idx, int intf_size, 309 bcm_if_t *intf_arr, int *intf_count, int *max_paths) 310 { 311 bcm_if_t *intf_arr_ptr; 312 313 if (intf_size && intf_count == NULL) { 314 return BCM_E_PARAM; 315 } 316 317 intf_arr_ptr = _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].rh_intf_arr; 318 if (intf_arr_ptr) { 319 if (intf_count) { 320 *intf_count = _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].rh_intf_count; 321 } 322 if (max_paths) { 323 *max_paths = _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].max_paths; 324 } 325 if (intf_size == 0) { 326 return (BCM_E_NONE); 327 } else if (intf_size < *intf_count) { 328 *intf_count = intf_size; 329 } 330 if (intf_arr) { 331 sal_memcpy(intf_arr, intf_arr_ptr, 332 (sizeof(bcm_if_t) * (*intf_count))); 333 } 334 } else { 335 return (BCM_E_INTERNAL); 336 } 337 338 return (BCM_E_NONE); 339 } 340 341 /* 342 * Function: 343 * _bcm_opt_ecmp_rh_free_resource 344 * Purpose: 345 * Free resources for an ECMP resilient hashing group. 346 * Parameters: 347 * unit - (IN) SOC unit number. 348 * ecmp_group_idx - (IN) ECMP group ID. 349 * Returns: 350 * BCM_E_xxx 351 */ 352 STATIC int 353 _bcm_opt_ecmp_rh_free_resource(int unit, int ecmp_group_idx) 354 { 355 ecmp_count_entry_t ecmp_count_entry; 356 initial_l3_ecmp_group_entry_t initial_l3_ecmp_group_entry; 357 bcm_if_t **intf_arr_ptr; 358 soc_mem_t mem = L3_ECMP_COUNTm; 359 360 if (!BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, ecmp_group_idx + 361 BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit))) { 362 return(BCM_E_PARAM); 363 } 364 365 #ifdef BCM_TOMAHAWK3_SUPPORT 366 if (soc_feature(unit, soc_feature_l3_ecmp_hier_tbl) && 367 (BCM_XGS3_L3_TBL(unit, ecmp_info).ecmp_mode == 368 ecmp_mode_hierarchical) && 369 (ecmp_group_idx < (BCM_XGS3_L3_ECMP_MAX_GROUPS(unit) / 2))) { 370 mem = ECMP_GROUP_HIERARCHICALm; 371 } 372 #endif 373 374 /* Read group entry from hw to check if load balancing mode is RH */ 375 BCM_IF_ERROR_RETURN(soc_mem_read(unit, mem, MEM_BLOCK_ANY, 376 ecmp_group_idx, &ecmp_count_entry)); 377 if (BCM_TH_L3_ECMP_LB_MODE_RH != soc_mem_field32_get(unit, mem, 378 &ecmp_count_entry, LB_MODEf)) { 379 /* Resilient hashing is not enabled on this ECMP group. */ 380 return BCM_E_NONE; 381 } 382 /* Clear resilient hashing lb mode field */ 383 soc_mem_field32_set(unit, mem, &ecmp_count_entry, 384 LB_MODEf, 0); 385 BCM_IF_ERROR_RETURN(soc_mem_write(unit, mem, MEM_BLOCK_ALL, 386 ecmp_group_idx, &ecmp_count_entry)); 387 388 if (SOC_MEM_IS_VALID(unit, INITIAL_L3_ECMP_GROUPm)) { 389 BCM_IF_ERROR_RETURN(soc_mem_read(unit, INITIAL_L3_ECMP_GROUPm, MEM_BLOCK_ANY, 390 ecmp_group_idx, &initial_l3_ecmp_group_entry)); 391 soc_mem_field32_set(unit, INITIAL_L3_ECMP_GROUPm, 392 &initial_l3_ecmp_group_entry, LB_MODEf, 0); 393 BCM_IF_ERROR_RETURN(soc_mem_write(unit, INITIAL_L3_ECMP_GROUPm, 394 MEM_BLOCK_ALL, ecmp_group_idx, &initial_l3_ecmp_group_entry)); 395 } 396 397 /* Free ECMP member array */ 398 intf_arr_ptr = &(_opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].rh_intf_arr); 399 if (*intf_arr_ptr) { 400 sal_free(*intf_arr_ptr); 401 *intf_arr_ptr = NULL; 402 } 403 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].data_hash = 0; 404 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_idx].rh_intf_count = 0; 405 406 return BCM_E_NONE; 407 } 408 409 /* 410 * Function: 411 * _bcm_opt_ecmp_rh_rand_get 412 * Purpose: 413 * Get a random number between 0 and the given max value. 414 * Parameters: 415 * unit - (IN) SOC unit number. 416 * rand_max - (IN) Maximum random number. 417 * rand_num - (OUT) Random number. 418 * Returns: 419 * BCM_E_xxx 420 * Notes: 421 * This procedure uses the pseudo-random number generator algorithm 422 * suggested by the C standard. 423 */ 424 STATIC int 425 _bcm_opt_ecmp_rh_rand_get(int unit, int rand_max, int *rand_num) 426 { 427 int modulus; 428 int rand_seed_shift; 429 430 if (rand_max < 0) { 431 return BCM_E_PARAM; 432 } 433 434 if (NULL == rand_num) { 435 return BCM_E_PARAM; 436 } 437 438 /* Make sure the modulus does not exceed limit. For instance, 439 * if the 32-bit rand_seed is shifted to the right by 16 bits before 440 * the modulo operation, the modulus should not exceed 1 << 16. 441 */ 442 modulus = rand_max + 1; 443 rand_seed_shift = 16; 444 if (modulus > (1 << (32 - rand_seed_shift))) { 445 return BCM_E_PARAM; 446 } 447 448 _opt_ecmp_rh_info[unit]->ecmp_rh_rand_seed = 449 _opt_ecmp_rh_info[unit]->ecmp_rh_rand_seed * 1103515245 + 12345; 450 451 *rand_num = (_opt_ecmp_rh_info[unit]->ecmp_rh_rand_seed >> rand_seed_shift) % 452 modulus; 453 454 return BCM_E_NONE; 455 } 456 457 /* 458 * Function: 459 * bcm_opt_l3_egress_rh_ecmp_find 460 * Purpose: 461 * Find resilient hashing group given its members. 462 * Parameters: 463 * unit - (IN) SOC unit number. 464 * intf_count - (IN) ECMP member count. 465 * intf_array - (IN) ECMP member array. 466 * mpintf - (OUT) ECMP multipath group ID. 467 * Returns: 468 * BCM_E_xxx 469 */ 470 int 471 bcm_opt_l3_egress_rh_ecmp_find(int unit, int intf_count, 472 bcm_if_t *intf_array, bcm_if_t *mpintf) 473 { 474 uint16 hash; 475 int rv = BCM_E_NONE; 476 bcm_if_t *hash_intf_array = NULL; 477 int alloc_size; 478 int i; 479 480 /* Allocate member array */ 481 alloc_size = sizeof(bcm_if_t) * BCM_XGS3_L3_ECMP_MAX(unit); 482 hash_intf_array = sal_alloc(alloc_size, "RH intf array"); 483 if (NULL == hash_intf_array) { 484 return(BCM_E_MEMORY); 485 } 486 sal_memset(hash_intf_array, 0, sizeof(bcm_if_t) * BCM_XGS3_L3_ECMP_MAX(unit)); 487 /* Copy ECMP members */ 488 sal_memcpy(hash_intf_array, intf_array, sizeof(bcm_if_t) * intf_count); 489 /* Sort ECMP members in the member array */ 490 _shr_sort(hash_intf_array, intf_count, sizeof(int), _opt_rh_cmp_int); 491 /* Compute hash on the sorted member array */ 492 _bcm_opt_rh_ecmp_grp_hash_calc(unit, hash_intf_array, &hash); 493 i = 0; 494 if (_opt_ecmp_rh_info[unit]) { 495 for (i = 0; i < BCM_XGS3_L3_ECMP_MAX_GROUPS(unit); i++) { 496 if (_opt_ecmp_rh_info[unit]->rhg[i].rh_intf_arr) { 497 if ((hash == _opt_ecmp_rh_info[unit]->rhg[i].data_hash) && 498 (intf_count == _opt_ecmp_rh_info[unit]->rhg[i].rh_intf_count)) { 499 /* Compare ecmp groups. */ 500 if (sal_memcmp(_opt_ecmp_rh_info[unit]->rhg[i].rh_intf_arr, 501 hash_intf_array, (intf_count * sizeof(int))) == 0) { 502 *mpintf = i + BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 503 break; 504 } 505 } 506 } 507 } 508 } 509 sal_free(hash_intf_array); 510 if (i == BCM_XGS3_L3_ECMP_MAX_GROUPS(unit)) { 511 rv = BCM_E_NOT_FOUND; 512 } 513 return rv; 514 } 515 516 /* 517 * Function: 518 * _bcm_opt_ecmp_rh_member_choose 519 * Purpose: 520 * Choose a member of the ECMP group. 521 * Parameters: 522 * unit - (IN) SOC unit number. 523 * num_members - (IN) Number of members to choose from. 524 * entry_count_arr - (IN/OUT) An array keeping track of how many 525 * rh set entries have been assigned 526 * to each member. 527 * max_entry_count - (IN/OUT) The maximum number of rh set entries 528 * that can be assigned to a member. 529 * chosen_index - (OUT) The index of the chosen member. 530 * Returns: 531 * BCM_E_xxx 532 */ 533 STATIC int 534 _bcm_opt_ecmp_rh_member_choose(int unit, int num_members, int *entry_count_arr, 535 int *max_entry_count, int *chosen_index) 536 { 537 int member_index; 538 int next_index; 539 540 *chosen_index = 0; 541 542 /* Choose a random member index */ 543 BCM_IF_ERROR_RETURN(_bcm_opt_ecmp_rh_rand_get(unit, num_members - 1, 544 &member_index)); 545 546 if (entry_count_arr[member_index] < *max_entry_count) { 547 entry_count_arr[member_index]++; 548 *chosen_index = member_index; 549 } else { 550 /* The randomly chosen member has reached the maximum 551 * rh set entry count. Choose the next member that 552 * has not reached the maximum entry count. 553 */ 554 next_index = (member_index + 1) % num_members; 555 while (next_index != member_index) { 556 if (entry_count_arr[next_index] < *max_entry_count) { 557 entry_count_arr[next_index]++; 558 *chosen_index = next_index; 559 break; 560 } else { 561 next_index = (next_index + 1) % num_members; 562 } 563 } 564 if (next_index == member_index) { 565 /* All members have reached the maximum rh set entry 566 * count. This scenario occurs when dividing the number of 567 * rh set entries by the number of members results 568 * in a non-zero remainder. The remainder rh set entries 569 * will be distributed among members, at most 1 remainder 570 * entry per member. 571 */ 572 (*max_entry_count)++; 573 if (entry_count_arr[member_index] < *max_entry_count) { 574 entry_count_arr[member_index]++; 575 *chosen_index = member_index; 576 } else { 577 /* It's possible that the member's entry count equals 578 * to the incremented value of max_entry_count, when 579 * this procedure is invoked by 580 * _bcm_opt_ecmp_rh_populate_empty_entries. 581 */ 582 next_index = (member_index + 1) % num_members; 583 while (next_index != member_index) { 584 if (entry_count_arr[next_index] < *max_entry_count) { 585 entry_count_arr[next_index]++; 586 *chosen_index = next_index; 587 break; 588 } else { 589 next_index = (next_index + 1) % num_members; 590 } 591 } 592 if (next_index == member_index) { 593 return BCM_E_INTERNAL; 594 } 595 } 596 } 597 } 598 599 return BCM_E_NONE; 600 } 601 602 /* 603 * Function: 604 * _bcm_opt_ecmp_rh_member_replica_find 605 * Purpose: 606 * For each member in the member_array, find others members 607 * who share the same next hop index. 608 * Parameters: 609 * unit - (IN) SOC unit number. 610 * num_members - (IN) Number of members. 611 * member_array - (IN/OUT) Array of members. 612 * Returns: 613 * BCM_E_XXX 614 */ 615 STATIC int 616 _bcm_opt_ecmp_rh_member_replica_find(int unit, int num_members, 617 _ecmp_rh_member_t *member_array) 618 { 619 int i, k; 620 621 if (num_members < 1) { 622 return BCM_E_PARAM; 623 } 624 if (NULL == member_array) { 625 return BCM_E_PARAM; 626 } 627 628 for (i = 0; i < num_members - 1; i++) { 629 if (member_array[i].replica_id == 0) { 630 /* replica_id == 0 indicates first replica. 631 * Search for other replicas in subsequent members. 632 */ 633 for (k = i + 1; k < num_members; k++) { 634 if (member_array[k].nh_index == member_array[i].nh_index) { 635 /* Replica found. Assign replica id. */ 636 member_array[k].replica_id = member_array[i].num_replica; 637 638 /* For non-first replicas, set non-applicable fields to -1. */ 639 member_array[k].num_replica = -1; 640 member_array[k].next_replica_id = -1; 641 642 /* Increment the num_replica field of the first replica */ 643 member_array[i].num_replica++; 644 } 645 } 646 } 647 } 648 649 return BCM_E_NONE; 650 } 651 652 /* 653 * Function: 654 * _bcm_opt_ecmp_rh_add_rebalance 655 * Purpose: 656 * Re-balance rh set entries from existing members to the new member. 657 * For example, if the number of rh set entries is 64, and the number of 658 * existing members is 6, then each existing member has between 10 and 11 659 * entries. The entries should be re-assigned from the 6 existing members 660 * to the new member such that each member will end up with between 9 and 661 * 10 entries. 662 * Parameters: 663 * unit - (IN) SOC unit number. 664 * num_entries - (IN) Number of rh set entries in buffer. 665 * rh_intf_array - (IN/OUT) intf rh set array entry buffer. 666 * member_id_buf - (IN/OUT) Member ID buffer. 667 * num_members - (IN) Number of members in member_array. 668 * member_array - (IN) Array of existing members. 669 * entry_count_array - (IN/OUT) Array of entry counts of existing members. 670 * new_member - (IN) The member to be added. 671 * entry_count - (OUT) Entry count of the new member. 672 * Returns: 673 * BCM_E_XXX 674 */ 675 STATIC int 676 _bcm_opt_ecmp_rh_add_rebalance(int unit, 677 int num_entries, int overlay, bcm_if_t *rh_intf_array, 678 int *member_id_buf, int num_members, 679 _ecmp_rh_member_t *member_array, 680 int *entry_count_array, 681 _ecmp_rh_member_t *new_member, 682 int *entry_count) 683 { 684 int lower_bound, upper_bound; 685 int threshold; 686 int entry_index, next_entry_index; 687 int member_id; 688 int is_new_member; 689 int member_index = 0; 690 691 if (num_entries < 1) { 692 return BCM_E_PARAM; 693 } 694 if (NULL == member_id_buf) { 695 return BCM_E_PARAM; 696 } 697 if (num_members < 1) { 698 return BCM_E_PARAM; 699 } 700 if (NULL == member_array) { 701 return BCM_E_PARAM; 702 } 703 if (NULL == entry_count_array) { 704 return BCM_E_PARAM; 705 } 706 if (NULL == new_member) { 707 return BCM_E_PARAM; 708 } 709 if (NULL == entry_count) { 710 return BCM_E_PARAM; 711 } 712 713 lower_bound = num_entries / (num_members + 1); 714 upper_bound = (num_entries % (num_members + 1)) ? 715 (lower_bound + 1) : lower_bound; 716 threshold = upper_bound; 717 *entry_count = 0; 718 while (*entry_count < lower_bound) { 719 /* Pick a random entry in the rh set entry buffer */ 720 BCM_IF_ERROR_RETURN 721 (_bcm_opt_ecmp_rh_rand_get(unit, num_entries - 1, &entry_index)); 722 member_id = member_id_buf[entry_index]; 723 724 /* Determine if the randomly picked entry contains the new member. 725 * If not, determine the existing member index. 726 */ 727 if (member_id == new_member->member_id) { 728 is_new_member = TRUE; 729 } else { 730 is_new_member = FALSE; 731 732 /* In the member_array, it's assumed that each array element's 733 * member_id matches the element's array index. 734 */ 735 member_index = member_id; 736 } 737 if (!is_new_member && (entry_count_array[member_index] > threshold)) { 738 if (overlay) { 739 rh_intf_array[entry_index] = new_member->nh_index + BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 740 } else { 741 rh_intf_array[entry_index] = new_member->nh_index + BCM_XGS3_EGRESS_IDX_MIN(unit); 742 } 743 member_id_buf[entry_index] = new_member->member_id; 744 entry_count_array[member_index]--; 745 (*entry_count)++; 746 } else { 747 /* Either the member of the randomly chosen entry is 748 * the same as the new member, or the member is an existing 749 * member and its entry count has decreased to threshold. 750 * In both cases, find the next entry that contains a 751 * member that's not the new member and whose entry count 752 * has not decreased to threshold. 753 */ 754 next_entry_index = (entry_index + 1) % num_entries; 755 while (next_entry_index != entry_index) { 756 member_id = member_id_buf[next_entry_index]; 757 758 /* Determine if the entry contains the new member. 759 * If not, determine the existing member index. 760 */ 761 if (member_id == new_member->member_id) { 762 is_new_member = TRUE; 763 } else { 764 is_new_member = FALSE; 765 766 /* In the member_array, it's assumed that each array element's 767 * member_id matches the array index of the element. 768 */ 769 member_index = member_id; 770 } 771 772 if (!is_new_member && 773 (entry_count_array[member_index] > threshold)) { 774 if (overlay) { 775 rh_intf_array[next_entry_index] = new_member->nh_index + BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 776 } else { 777 rh_intf_array[next_entry_index] = new_member->nh_index + BCM_XGS3_EGRESS_IDX_MIN(unit); 778 } 779 member_id_buf[next_entry_index] = new_member->member_id; 780 entry_count_array[member_index]--; 781 (*entry_count)++; 782 break; 783 } else { 784 next_entry_index = (next_entry_index + 1) % num_entries; 785 } 786 } 787 if (next_entry_index == entry_index) { 788 /* The entry count of all existing members has decreased 789 * to threshold. The entry count of the new member has 790 * not yet increased to lower_bound. Lower the threshold. 791 */ 792 threshold--; 793 } 794 } 795 } 796 797 return BCM_E_NONE; 798 } 799 800 /* 801 * Function: 802 * _bcm_opt_ecmp_rh_member_id_buf_assign 803 * Purpose: 804 * For each entry in the rh set entry buffer, match its next hop 805 * index against the next hop index of a member, then assign the 806 * matching member's member ID to the entry. 807 * Parameters: 808 * unit - (IN) SOC unit number. 809 * num_members - (IN) Number of members. 810 * member_array - (IN/OUT) Array of members. 811 * num_entries - (IN) Number of rh set entries in buffer. 812 * rh_intf_array - (IN) intf rh set array entry buffer. 813 * member_id_buf - (OUT) Member ID buffer. 814 * Returns: 815 * BCM_E_XXX 816 */ 817 STATIC int 818 _bcm_opt_ecmp_rh_member_id_buf_assign(int unit, int num_members, 819 _ecmp_rh_member_t *member_array, 820 int num_entries, 821 bcm_if_t *rh_intf_array, 822 int *member_id_buf) 823 { 824 int i, k, m; 825 int next_hop_index; 826 int offset; 827 828 for (i = 0; i < num_entries; i++) { 829 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, rh_intf_array[i])) { 830 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 831 } 832 #ifdef BCM_TOMAHAWK3_SUPPORT 833 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 834 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 835 rh_intf_array[i])) { 836 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 837 } 838 #endif 839 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, rh_intf_array[i])) { 840 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 841 } else { 842 return(BCM_E_INTERNAL); 843 } 844 next_hop_index = rh_intf_array[i] - offset; 845 /* Search the member_array for matching next hop index */ 846 for (k = 0; k < num_members; k++) { 847 if (member_array[k].nh_index == next_hop_index) { 848 if (member_array[k].num_replica == 1) { 849 /* The next hop index is not shared by other members. */ 850 member_id_buf[i] = member_array[k].member_id; 851 break; 852 } 853 854 /* Else there are more than one members sharing the next 855 * hop index. Assign the member ID of the replica 856 * indicated by next_replica_id. 857 */ 858 for (m = k; m < num_members; m++) { 859 if ((member_array[m].nh_index == next_hop_index) && 860 (member_array[m].replica_id == member_array[k].next_replica_id)) { 861 member_id_buf[i] = member_array[m].member_id; 862 break; 863 } 864 } 865 if (m == num_members) { 866 /* Cannot find a matching replica */ 867 return BCM_E_INTERNAL; 868 } 869 870 /* Increment the next_replica_id field of the first replica */ 871 member_array[k].next_replica_id = (member_array[k].next_replica_id + 1) % 872 member_array[k].num_replica; 873 874 break; 875 } 876 } 877 if (k == num_members) { 878 /* Cannot find a member with matching next hop index */ 879 return BCM_E_INTERNAL; 880 } 881 } 882 883 return BCM_E_NONE; 884 } 885 886 /* 887 * Function: 888 * _bcm_opt_ecmp_rh_set 889 * Purpose: 890 * Configure an ECMP resilient hashing group. 891 * Parameters: 892 * unit - (IN) SOC unit number. 893 * ecmp - (IN) ECMP group info. 894 * intf_count - (IN) Number of elements in intf_array. 895 * intf_array - (IN) Array of Egress forwarding objects. 896 * new_intf_array - (OUT) intf rh set array entry buffer. 897 * Returns: 898 * BCM_E_xxx 899 */ 900 STATIC int 901 _bcm_opt_ecmp_rh_set(int unit, 902 bcm_l3_egress_ecmp_t *ecmp, 903 int intf_count, 904 bcm_if_t *intf_array, 905 bcm_if_t *new_intf_array) 906 { 907 int rv = BCM_E_NONE; 908 int max_entry_count; 909 int chosen_index; 910 int *entry_count_arr = NULL; 911 int i; 912 913 if (ecmp == NULL || 914 ecmp->dynamic_mode != BCM_L3_ECMP_DYNAMIC_MODE_RESILIENT) { 915 return BCM_E_PARAM; 916 } 917 918 if (intf_count > 0 && intf_array == NULL) { 919 return BCM_E_PARAM; 920 } 921 922 if (intf_count == 0) { 923 return BCM_E_NONE; 924 } 925 926 entry_count_arr = sal_alloc(sizeof(int) * intf_count, 927 "ECMP RH entry count array"); 928 if (NULL == entry_count_arr) { 929 return BCM_E_MEMORY; 930 } 931 sal_memset(entry_count_arr, 0, sizeof(int) * intf_count); 932 max_entry_count = ecmp->dynamic_size / intf_count; 933 934 for (i = 0; i < ecmp->dynamic_size; i++) { 935 /* Choose a member of the ECMP */ 936 rv = _bcm_opt_ecmp_rh_member_choose(unit, intf_count, 937 entry_count_arr, &max_entry_count, &chosen_index); 938 if (BCM_FAILURE(rv)) { 939 sal_free(entry_count_arr); 940 return rv; 941 } 942 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, 943 intf_array[chosen_index])) { 944 new_intf_array[i] = intf_array[chosen_index]; 945 } 946 #ifdef BCM_TOMAHAWK3_SUPPORT 947 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 948 (BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 949 intf_array[chosen_index]))) { 950 new_intf_array[i] = intf_array[chosen_index]; 951 } 952 #endif 953 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, 954 intf_array[chosen_index])) { 955 new_intf_array[i] = intf_array[chosen_index]; 956 } else { 957 sal_free(entry_count_arr); 958 return BCM_E_PARAM; 959 } 960 } 961 sal_free(entry_count_arr); 962 963 return BCM_E_NONE; 964 } 965 966 /* 967 * Function: 968 * _bcm_opt_ecmp_rh_add 969 * Purpose: 970 * Add a member to an ECMP resilient hashing group. 971 * Parameters: 972 * unit - (IN) SOC unit number. 973 * ecmp - (IN) ECMP group info. 974 * intf_count - (IN) Number of elements in intf_array. 975 * intf_array - (IN) Array of resilient hashing eligible members, 976 * including the member to be added. 977 * new_intf - (IN) New member to be added. 978 * rh_intf_array - (IN/OUT) intf rh set array entry buffer. 979 * Returns: 980 * BCM_E_xxx 981 */ 982 STATIC int 983 _bcm_opt_ecmp_rh_add(int unit, 984 bcm_l3_egress_ecmp_t *ecmp, 985 int intf_count, 986 bcm_if_t *intf_array, 987 bcm_if_t new_intf, 988 bcm_if_t *rh_intf_array) 989 { 990 int rv = BCM_E_NONE; 991 int offset; 992 int new_next_hop_index; 993 int num_existing_members; 994 int alloc_size; 995 int num_entries; 996 _ecmp_rh_member_t *existing_member_arr = NULL; 997 int i; 998 int *member_id_buf = NULL; 999 int *entry_count_arr = NULL; 1000 int member_id; 1001 _ecmp_rh_member_t new_member; 1002 int new_member_entry_count; 1003 int overlay = 0; 1004 1005 if (ecmp == NULL || 1006 ecmp->dynamic_mode != BCM_L3_ECMP_DYNAMIC_MODE_RESILIENT) { 1007 return BCM_E_PARAM; 1008 } 1009 1010 if (intf_count == 0 || intf_array == NULL) { 1011 return BCM_E_PARAM; 1012 } 1013 1014 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, new_intf)) { 1015 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 1016 } 1017 #ifdef BCM_TOMAHAWK3_SUPPORT 1018 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 1019 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 1020 new_intf)) { 1021 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 1022 } 1023 #endif 1024 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, new_intf)) { 1025 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 1026 } else { 1027 return BCM_E_PARAM; 1028 } 1029 new_next_hop_index = new_intf - offset; 1030 1031 /* Check that the new member is the last element of the array of 1032 * resilient hashing eligible members. 1033 */ 1034 if (new_intf != intf_array[intf_count - 1]) { 1035 return BCM_E_PARAM; 1036 } 1037 num_existing_members = intf_count - 1; 1038 1039 if (intf_count == 1) { 1040 /* Adding the first member is the same as setting one member */ 1041 return _bcm_opt_ecmp_rh_set(unit, ecmp, intf_count, intf_array, 1042 rh_intf_array); 1043 } 1044 num_entries = ecmp->dynamic_size; 1045 /* Construct an array of existing members */ 1046 alloc_size = num_existing_members * sizeof(_ecmp_rh_member_t); 1047 existing_member_arr = sal_alloc(alloc_size, "ECMP RH member array"); 1048 if (NULL == existing_member_arr) { 1049 rv = BCM_E_MEMORY; 1050 goto cleanup; 1051 } 1052 sal_memset(existing_member_arr, 0, alloc_size); 1053 for (i = 0; i < num_existing_members; i++) { 1054 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, intf_array[i])) { 1055 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 1056 } 1057 #ifdef BCM_TOMAHAWK3_SUPPORT 1058 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 1059 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 1060 intf_array[i])) { 1061 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 1062 } 1063 #endif 1064 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, intf_array[i])) { 1065 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 1066 } else { 1067 rv = BCM_E_PARAM; 1068 goto cleanup; 1069 } 1070 existing_member_arr[i].nh_index = intf_array[i] - offset; 1071 existing_member_arr[i].member_id = i; 1072 existing_member_arr[i].num_replica = 1; 1073 existing_member_arr[i].replica_id = 0; 1074 existing_member_arr[i].next_replica_id = 0; 1075 } 1076 /* Find members that share the same next hop index, and 1077 * update the replica information in existing_member_arr. 1078 */ 1079 rv = _bcm_opt_ecmp_rh_member_replica_find(unit, num_existing_members, 1080 existing_member_arr); 1081 if (BCM_FAILURE(rv)) { 1082 goto cleanup; 1083 } 1084 1085 /* Derive a buffer of member IDs by assigning a member ID to each 1086 * entry of the rh set entry buffer. 1087 */ 1088 alloc_size = num_entries * sizeof(int); 1089 member_id_buf = sal_alloc(alloc_size, "ECMP RH member ID buffer"); 1090 if (NULL == member_id_buf) { 1091 rv = BCM_E_MEMORY; 1092 goto cleanup; 1093 } 1094 sal_memset(member_id_buf, 0, alloc_size); 1095 rv = _bcm_opt_ecmp_rh_member_id_buf_assign(unit, num_existing_members, 1096 existing_member_arr, num_entries, rh_intf_array, member_id_buf); 1097 if (BCM_FAILURE(rv)) { 1098 goto cleanup; 1099 } 1100 1101 /* Compute the number of entries currently assigned to each 1102 * existing member. 1103 */ 1104 alloc_size = intf_count * sizeof(int); 1105 entry_count_arr = sal_alloc(alloc_size, "ECMP RH entry count array"); 1106 if (NULL == entry_count_arr) { 1107 rv = BCM_E_MEMORY; 1108 goto cleanup; 1109 } 1110 sal_memset(entry_count_arr, 0, alloc_size); 1111 for (i = 0; i < num_entries; i++) { 1112 member_id = member_id_buf[i]; 1113 1114 /* In this procedure, the member_id happens to be the same as 1115 * the index into the existing_member_arr. 1116 */ 1117 entry_count_arr[member_id]++; 1118 } 1119 1120 /* Check that the distribution of rh set entries among existing members 1121 * is balanced. For instance, if the number of rh set entries is 64, and 1122 * the number of existing members is 6, then every member should have 1123 * between 10 and 11 entries. 1124 */ 1125 1126 #ifdef BCM_TOMAHAWK3_SUPPORT 1127 if (soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) { 1128 overlay = (ecmp->ecmp_group_flags & BCM_L3_ECMP_OVERLAY); 1129 } 1130 #endif 1131 1132 /* Re-balance rh set entries from existing members to the new member */ 1133 sal_memset(&new_member, 0, sizeof(_ecmp_rh_member_t)); 1134 new_member.nh_index = new_next_hop_index; 1135 new_member.member_id = num_existing_members; 1136 rv = _bcm_opt_ecmp_rh_add_rebalance(unit, 1137 num_entries, overlay, rh_intf_array, 1138 member_id_buf, num_existing_members, existing_member_arr, 1139 entry_count_arr, &new_member, &new_member_entry_count); 1140 if (BCM_FAILURE(rv)) { 1141 goto cleanup; 1142 } 1143 1144 cleanup: 1145 if (member_id_buf) { 1146 sal_free(member_id_buf); 1147 } 1148 if (existing_member_arr) { 1149 sal_free(existing_member_arr); 1150 } 1151 if (entry_count_arr) { 1152 sal_free(entry_count_arr); 1153 } 1154 1155 return rv; 1156 } 1157 1158 /* 1159 * Function: 1160 * _bcm_opt_ecmp_rh_delete 1161 * Purpose: 1162 * Delete a member from an ECMP resilient hashing group. 1163 * Parameters: 1164 * unit - (IN) SOC unit number. 1165 * ecmp - (IN) ECMP group info. 1166 * intf_count - (IN) Number of elements in intf_array. 1167 * intf_array - (IN) Array of resilient hashing eligible members, 1168 * except the member to be deleted. 1169 * leaving_intf - (IN) Member to delete. 1170 * rh_intf_array - (IN/OUT) intf rh set array entry buffer. 1171 * Returns: 1172 * BCM_E_xxx 1173 */ 1174 STATIC int 1175 _bcm_opt_ecmp_rh_delete(int unit, 1176 bcm_l3_egress_ecmp_t *ecmp, 1177 int intf_count, 1178 bcm_if_t *intf_array, 1179 bcm_if_t leaving_intf, 1180 bcm_if_t *rh_intf_array) 1181 { 1182 int rv = BCM_E_NONE; 1183 int ecmp_group; 1184 int offset; 1185 int leaving_next_hop_index; 1186 int num_entries; 1187 int alloc_size; 1188 int num_existing_members; 1189 _ecmp_rh_member_t *existing_member_arr = NULL; 1190 int i; 1191 int *member_id_buf = NULL; 1192 int *entry_count_arr = NULL; 1193 int lower_bound, upper_bound; 1194 int num_remaining_members; 1195 int threshold; 1196 int leaving_member_id; 1197 int member_id; 1198 int chosen_index; 1199 #ifdef BCM_TOMAHAWK3_SUPPORT 1200 int overlay_group = 0; 1201 #endif 1202 1203 if (ecmp == NULL || 1204 ecmp->dynamic_mode != BCM_L3_ECMP_DYNAMIC_MODE_RESILIENT) { 1205 return BCM_E_PARAM; 1206 } 1207 if (BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, ecmp->ecmp_intf)) { 1208 ecmp_group = ecmp->ecmp_intf - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 1209 } else { 1210 return BCM_E_PARAM; 1211 } 1212 1213 if (intf_count > 0 && intf_array == NULL) { 1214 return BCM_E_PARAM; 1215 } 1216 1217 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, leaving_intf)) { 1218 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 1219 } 1220 #ifdef BCM_TOMAHAWK3_SUPPORT 1221 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 1222 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 1223 leaving_intf)) { 1224 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 1225 overlay_group = 1; 1226 } 1227 #endif 1228 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, leaving_intf)) { 1229 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 1230 } else { 1231 return BCM_E_PARAM; 1232 } 1233 leaving_next_hop_index = leaving_intf - offset; 1234 num_entries = ecmp->dynamic_size; 1235 1236 if (intf_count == 0) { 1237 /* Deleting the last member is the same as freeing all resources */ 1238 BCM_IF_ERROR_RETURN(_bcm_opt_ecmp_rh_free_resource(unit, ecmp_group)); 1239 1240 return BCM_E_NONE; 1241 } 1242 1243 /* Construct an array of existing members */ 1244 num_existing_members = intf_count + 1; 1245 alloc_size = num_existing_members * sizeof(_ecmp_rh_member_t); 1246 existing_member_arr = sal_alloc(alloc_size, "ECMP RH member array"); 1247 if (NULL == existing_member_arr) { 1248 rv = BCM_E_MEMORY; 1249 goto cleanup; 1250 } 1251 sal_memset(existing_member_arr, 0, alloc_size); 1252 for (i = 0; i < intf_count; i++) { 1253 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, intf_array[i])) { 1254 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 1255 } 1256 #ifdef BCM_TOMAHAWK3_SUPPORT 1257 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 1258 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 1259 intf_array[i])) { 1260 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 1261 } 1262 #endif 1263 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, intf_array[i])) { 1264 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 1265 } else { 1266 rv = BCM_E_PARAM; 1267 goto cleanup; 1268 } 1269 existing_member_arr[i].nh_index = intf_array[i] - offset; 1270 existing_member_arr[i].member_id = i; 1271 existing_member_arr[i].num_replica = 1; 1272 existing_member_arr[i].replica_id = 0; 1273 existing_member_arr[i].next_replica_id = 0; 1274 } 1275 existing_member_arr[intf_count].nh_index = leaving_next_hop_index; 1276 existing_member_arr[intf_count].member_id = intf_count; 1277 existing_member_arr[intf_count].num_replica = 1; 1278 existing_member_arr[intf_count].replica_id = 0; 1279 existing_member_arr[intf_count].next_replica_id = 0; 1280 1281 /* Find members that share the same next hop index, and 1282 * update the replica information in existing_member_arr. 1283 */ 1284 rv = _bcm_opt_ecmp_rh_member_replica_find(unit, num_existing_members, 1285 existing_member_arr); 1286 if (BCM_FAILURE(rv)) { 1287 goto cleanup; 1288 } 1289 1290 /* Derive a buffer of member IDs by assigning a member ID to each 1291 * entry of the rh set entry buffer. 1292 */ 1293 alloc_size = num_entries * sizeof(int); 1294 member_id_buf = sal_alloc(alloc_size, "ECMP RH member ID buffer"); 1295 if (NULL == member_id_buf) { 1296 rv = BCM_E_MEMORY; 1297 goto cleanup; 1298 } 1299 sal_memset(member_id_buf, 0, alloc_size); 1300 rv = _bcm_opt_ecmp_rh_member_id_buf_assign(unit, num_existing_members, 1301 existing_member_arr, num_entries, rh_intf_array, member_id_buf); 1302 if (BCM_FAILURE(rv)) { 1303 goto cleanup; 1304 } 1305 1306 /* Compute the number of entries currently assigned to each 1307 * existing member. 1308 */ 1309 alloc_size = num_existing_members * sizeof(int); 1310 entry_count_arr = sal_alloc(alloc_size, "ECMP RH entry count array"); 1311 if (NULL == entry_count_arr) { 1312 rv = BCM_E_MEMORY; 1313 goto cleanup; 1314 } 1315 sal_memset(entry_count_arr, 0, alloc_size); 1316 for (i = 0; i < num_entries; i++) { 1317 member_id = member_id_buf[i]; 1318 1319 /* In the existing_member_arr, each element's member_id is the 1320 * same as its array index. 1321 */ 1322 entry_count_arr[member_id]++; 1323 } 1324 1325 /* Check that the distribution of rh set entries among all members 1326 * is balanced. For instance, if the number of rh set entries is 64, and 1327 * the number of members is 6, then every member should have 1328 * between 10 and 11 entries. 1329 */ 1330 lower_bound = num_entries / num_existing_members; 1331 upper_bound = (num_entries % num_existing_members) ? 1332 (lower_bound + 1) : lower_bound; 1333 for (i = 0; i < num_existing_members; i++) { 1334 if (entry_count_arr[i] < lower_bound || 1335 entry_count_arr[i] > upper_bound) { 1336 rv = BCM_E_INTERNAL; 1337 goto cleanup; 1338 } 1339 } 1340 1341 /* Re-balance rh set entries from the leaving member to the remaining 1342 * members. For example, if the number of rh set entries is 64, and 1343 * the number of members is 6, then each member has between 10 and 11 1344 * entries. The entries should be re-assigned from the leaving member to 1345 * the remaining 5 members such that each remaining member will end up 1346 * with between 12 and 13 entries. 1347 */ 1348 num_remaining_members = num_existing_members - 1; 1349 lower_bound = num_entries / num_remaining_members; 1350 threshold = lower_bound; 1351 leaving_member_id = existing_member_arr[intf_count].member_id; 1352 for (i = 0; i < num_entries; i++) { 1353 member_id = member_id_buf[i]; 1354 if (member_id != leaving_member_id) { 1355 continue; 1356 } 1357 1358 /* Randomly choose a member among the remaining members */ 1359 rv = _bcm_opt_ecmp_rh_member_choose(unit, num_remaining_members, 1360 entry_count_arr, &threshold, &chosen_index); 1361 if (BCM_FAILURE(rv)) { 1362 goto cleanup; 1363 } 1364 #ifdef BCM_TOMAHAWK3_SUPPORT 1365 if (overlay_group) { 1366 rh_intf_array[i] = existing_member_arr[chosen_index].nh_index + 1367 BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 1368 } else 1369 #endif 1370 { 1371 rh_intf_array[i] = existing_member_arr[chosen_index].nh_index + 1372 BCM_XGS3_EGRESS_IDX_MIN(unit); 1373 } 1374 member_id_buf[i] = existing_member_arr[chosen_index].member_id; 1375 } 1376 1377 cleanup: 1378 if (member_id_buf) { 1379 sal_free(member_id_buf); 1380 } 1381 if (existing_member_arr) { 1382 sal_free(existing_member_arr); 1383 } 1384 if (entry_count_arr) { 1385 sal_free(entry_count_arr); 1386 } 1387 1388 return rv; 1389 } 1390 1391 /* 1392 * Function: 1393 * _bcm_opt_ecmp_rh_clear_by_member_id_th 1394 * Purpose: 1395 * In the given rh set entry buffer, clear entries whose 1396 * member ID is greater than or equal to the given member ID threshold. 1397 * Also, keep track of entry count for each member ID smaller 1398 * than the given member ID threshold. 1399 * Parameters: 1400 * unit - (IN) SOC unit number. 1401 * num_entries - (IN) Number of rh set entries in buffer. 1402 * rh_intf_array - (IN/OUT) intf array entry buffer. 1403 * member_id_buf - (IN/OUT) Member ID buffer. 1404 * member_id_th - (IN) Member ID threshold. 1405 * array_size - (IN) Number of elements in entry_count_array. 1406 * entry_count_array - (OUT) Array of entry counts for member IDs not cleared. 1407 * Returns: 1408 * BCM_E_XXX 1409 */ 1410 STATIC int 1411 _bcm_opt_ecmp_rh_clear_by_member_id_th(int unit, 1412 int num_entries, bcm_if_t *rh_intf_aray, 1413 int *member_id_buf, 1414 int member_id_th, int array_size, 1415 int *entry_count_array) 1416 { 1417 int i; 1418 int member_id; 1419 1420 if (num_entries < 1) { 1421 return BCM_E_PARAM; 1422 } 1423 if (NULL == member_id_buf) { 1424 return BCM_E_PARAM; 1425 } 1426 if (member_id_th != array_size) { 1427 return BCM_E_PARAM; 1428 } 1429 if (NULL == entry_count_array) { 1430 return BCM_E_PARAM; 1431 } 1432 1433 for (i = 0; i < num_entries; i++) { 1434 member_id = member_id_buf[i]; 1435 1436 if (member_id >= member_id_th) { 1437 1438 /* Do not clear the next hop index, which is needed later by 1439 * _bcm_opt_ecmp_rh_populate_empty_entries. 1440 */ 1441 1442 /* Clear member ID */ 1443 member_id_buf[i] = -1; 1444 } else { 1445 entry_count_array[member_id]++; 1446 } 1447 } 1448 1449 return BCM_E_NONE; 1450 } 1451 1452 /* 1453 * Function: 1454 * _bcm_opt_ecmp_rh_populate_empty_entries 1455 * Purpose: 1456 * In the given rh set entry buffer, populate entries not 1457 * containing a member, such that maximal balance is achieve 1458 * among members in member_array. 1459 * Parameters: 1460 * unit - (IN) SOC unit number. 1461 * num_entries - (IN) Number of rh set entries in buffer. 1462 * rh_intf_array - (IN/OUT) intf array entry buffer. 1463 * member_id_buf - (IN/OUT) Member ID buffer. 1464 * num_members - (IN) Number of elements in member_array and 1465 * entry_count_array. 1466 * member_array - (IN) Array of members. 1467 * entry_count_array - (IN/OUT) Array of entry counts. 1468 * num_shared_members - (IN) Number of members in member_array that 1469 * are shared by the old and the new ECMP 1470 * groups. 1471 * Returns: 1472 * BCM_E_XXX 1473 */ 1474 STATIC int 1475 _bcm_opt_ecmp_rh_populate_empty_entries(int unit, 1476 int num_entries, 1477 int overlay, 1478 bcm_if_t *rh_intf_array, 1479 int *member_id_buf, 1480 int num_members, 1481 _ecmp_rh_member_t *member_array, 1482 int *entry_count_array, 1483 int num_shared_members) 1484 { 1485 int max_entry_count; 1486 int i, k; 1487 int next_hop_index; 1488 int chosen_index; 1489 int offset; 1490 1491 if (num_entries < 1) { 1492 return BCM_E_PARAM; 1493 } 1494 if (NULL == member_id_buf) { 1495 return BCM_E_PARAM; 1496 } 1497 if (0 == num_members) { 1498 return BCM_E_PARAM; 1499 } 1500 if (NULL == member_array) { 1501 return BCM_E_PARAM; 1502 } 1503 if (NULL == entry_count_array) { 1504 return BCM_E_PARAM; 1505 } 1506 if (num_shared_members > num_members) { 1507 return BCM_E_PARAM; 1508 } 1509 1510 max_entry_count = num_entries / num_members; 1511 for (i = 0; i < num_entries; i++) { 1512 if (member_id_buf[i] != -1) { 1513 /* Skip valid entries */ 1514 continue; 1515 } 1516 1517 /* First, try to find a shared member whose next hop index 1518 * matches the current entry and whose entry count has not reached 1519 * max_entry_count. Doing so minimizes flow-to-member reassignments. 1520 */ 1521 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, rh_intf_array[i])) { 1522 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 1523 } 1524 #ifdef BCM_TOMAHAWK3_SUPPORT 1525 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 1526 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 1527 rh_intf_array[i])) { 1528 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 1529 } 1530 #endif 1531 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, rh_intf_array[i])) { 1532 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 1533 } else { 1534 return BCM_E_PARAM; 1535 } 1536 next_hop_index = rh_intf_array[i] - offset; 1537 for (k = 0; k < num_shared_members; k++) { 1538 if (member_array[k].nh_index == next_hop_index) { 1539 if (entry_count_array[k] < max_entry_count) { 1540 entry_count_array[k]++; 1541 chosen_index = k; 1542 break; 1543 } 1544 } 1545 } 1546 if (k == num_shared_members) { 1547 /* Cannot find a shared member with matching next hop index and 1548 * with entry count < max_entry_count. Randomly choose a member. 1549 */ 1550 BCM_IF_ERROR_RETURN(_bcm_opt_ecmp_rh_member_choose(unit, num_members, 1551 entry_count_array, &max_entry_count, &chosen_index)); 1552 } 1553 /* Set rh set entry */ 1554 member_id_buf[i] = member_array[chosen_index].member_id; 1555 rh_intf_array[i] = member_array[chosen_index].nh_index + BCM_XGS3_EGRESS_IDX_MIN(unit); 1556 #ifdef BCM_TOMAHAWK3_SUPPORT 1557 if (overlay) { 1558 rh_intf_array[i] = member_array[chosen_index].nh_index + BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 1559 } 1560 #endif 1561 1562 } 1563 1564 return BCM_E_NONE; 1565 } 1566 1567 1568 /* 1569 * Function: 1570 * _bcm_opt_ecmp_rh_balance_with_min_shared_mod 1571 * Purpose: 1572 * Modify the given rh set entry buffer to achieve balance among 1573 * the shared members and members exclusive to the new ECMP group, 1574 * while keeping modification of entries containing the 1575 * shared members to a minimum. 1576 * Parameters: 1577 * unit - (IN) SOC unit number. 1578 * num_entries - (IN) Number of rh set entries in buffer. 1579 * rh_intf_array - (IN/OUT) intf array entry buffer. Initially, this buffer 1580 * contains the old ECMP group's rh set entries. 1581 * shared_intf_count - (IN) Number of members shared by old and new ECMP 1582 * groups. 1583 * shared_intf_array - (IN) Array of members shared by old and new ECMP 1584 * groups. 1585 * ex_old_intf_count - (IN) Number of members exclusive to old ECMP group. 1586 * ex_old_intf_array - (IN) Array of members exclusive to old ECMP group. 1587 * ex_new_intf_count - (IN) Number of members exclusive to new ECMP group. 1588 * ex_new_intf_array - (IN) Array of members exclusive to new ECMP group. 1589 * Returns: 1590 * BCM_E_XXX 1591 */ 1592 STATIC int 1593 _bcm_opt_ecmp_rh_balance_with_min_shared_mod(int unit, 1594 int num_entries, 1595 int overlay, 1596 bcm_if_t *rh_intf_array, 1597 int shared_intf_count, 1598 bcm_if_t *shared_intf_array, 1599 int ex_old_intf_count, 1600 bcm_if_t *ex_old_intf_array, 1601 int ex_new_intf_count, 1602 bcm_if_t *ex_new_intf_array) 1603 { 1604 int rv = BCM_E_NONE; 1605 int old_intf_count; 1606 int new_intf_count; 1607 int num_members; 1608 int alloc_size; 1609 _ecmp_rh_member_t *member_array = NULL; 1610 int *entry_count_array = NULL; 1611 int i; 1612 int ex_old_index, ex_new_index; 1613 int *member_id_buf = NULL; 1614 int intf_count; 1615 1616 if (num_entries < 1) { 1617 return BCM_E_PARAM; 1618 } 1619 if (0 == shared_intf_count) { 1620 return BCM_E_PARAM; 1621 } 1622 if (NULL == shared_intf_array) { 1623 return BCM_E_PARAM; 1624 } 1625 if ((ex_old_intf_count > 0) && (NULL == ex_old_intf_array)) { 1626 return BCM_E_PARAM; 1627 } 1628 if ((ex_new_intf_count > 0) && (NULL == ex_new_intf_array)) { 1629 return BCM_E_PARAM; 1630 } 1631 1632 /* Allocate an array of members and an array of entry counts */ 1633 old_intf_count = shared_intf_count + ex_old_intf_count; 1634 new_intf_count = shared_intf_count + ex_new_intf_count; 1635 num_members = (old_intf_count > new_intf_count) ? old_intf_count : new_intf_count; 1636 alloc_size = num_members * sizeof(_ecmp_rh_member_t); 1637 member_array = sal_alloc(alloc_size, "ECMP RH member array"); 1638 if (NULL == member_array) { 1639 rv = BCM_E_MEMORY; 1640 goto cleanup; 1641 } 1642 sal_memset(member_array, 0, alloc_size); 1643 1644 alloc_size = num_members * sizeof(int); 1645 entry_count_array = sal_alloc(alloc_size, "ECMP RH entry count array"); 1646 if (NULL == entry_count_array) { 1647 rv = BCM_E_MEMORY; 1648 goto cleanup; 1649 } 1650 sal_memset(entry_count_array, 0, alloc_size); 1651 1652 /* Initialize the member_array with shared members, followed by members 1653 * exclusive to the old ECMP group. 1654 */ 1655 for (i = 0; i < shared_intf_count; i++) { 1656 member_array[i].nh_index = shared_intf_array[i]; 1657 member_array[i].member_id = i; 1658 member_array[i].num_replica = 1; 1659 member_array[i].replica_id = 0; 1660 member_array[i].next_replica_id = 0; 1661 } 1662 for (i = shared_intf_count; i < old_intf_count; i++) { 1663 ex_old_index = i - shared_intf_count; 1664 member_array[i].nh_index = ex_old_intf_array[ex_old_index]; 1665 member_array[i].member_id = i; 1666 member_array[i].num_replica = 1; 1667 member_array[i].replica_id = 0; 1668 member_array[i].next_replica_id = 0; 1669 } 1670 /* Find members that share the same next hop index, and 1671 * update the replica information in member_array. 1672 */ 1673 rv = _bcm_opt_ecmp_rh_member_replica_find(unit, old_intf_count, 1674 member_array); 1675 if (BCM_FAILURE(rv)) { 1676 goto cleanup; 1677 } 1678 1679 /* Derive a buffer of member IDs by assigning a member ID to each 1680 * entry of the rh set entry buffer. 1681 */ 1682 alloc_size = num_entries * sizeof(int); 1683 member_id_buf = sal_alloc(alloc_size, "ECMP RH member ID buffer"); 1684 if (NULL == member_id_buf) { 1685 rv = BCM_E_MEMORY; 1686 goto cleanup; 1687 } 1688 sal_memset(member_id_buf, 0, alloc_size); 1689 rv = _bcm_opt_ecmp_rh_member_id_buf_assign(unit, old_intf_count, 1690 member_array, num_entries, rh_intf_array, member_id_buf); 1691 if (BCM_FAILURE(rv)) { 1692 goto cleanup; 1693 } 1694 1695 /* Traverse the rh set entry buffer to clear entries with member 1696 * IDs greater than the member IDs of the shared members. Also keep 1697 * track of each shared member's entry count. 1698 */ 1699 rv = _bcm_opt_ecmp_rh_clear_by_member_id_th(unit, num_entries, rh_intf_array, 1700 member_id_buf, shared_intf_count, shared_intf_count, 1701 entry_count_array); 1702 if (BCM_FAILURE(rv)) { 1703 goto cleanup; 1704 } 1705 1706 /* Clear from the member_array members exclusive to the old ECMP group. 1707 * Also need to reset share members' replica info. 1708 */ 1709 for (i = shared_intf_count; i < old_intf_count; i++) { 1710 sal_memset(&member_array[i], 0, sizeof(_ecmp_rh_member_t)); 1711 } 1712 for (i = 0; i < shared_intf_count; i++) { 1713 member_array[i].num_replica = 1; 1714 member_array[i].replica_id = 0; 1715 member_array[i].next_replica_id = 0; 1716 } 1717 1718 /* Add to member_array members exclusive to the new ECMP group */ 1719 for (i = shared_intf_count; i < new_intf_count; i++) { 1720 ex_new_index = i - shared_intf_count; 1721 member_array[i].nh_index = ex_new_intf_array[ex_new_index]; 1722 member_array[i].member_id = i; 1723 member_array[i].num_replica = 1; 1724 member_array[i].replica_id = 0; 1725 member_array[i].next_replica_id = 0; 1726 } 1727 1728 /* Find members that share the same next hop index, and 1729 * update the replica information in member_array. 1730 */ 1731 rv = _bcm_opt_ecmp_rh_member_replica_find(unit, new_intf_count, 1732 member_array); 1733 if (BCM_FAILURE(rv)) { 1734 goto cleanup; 1735 } 1736 /* Populate the cleared entries in the rh set entry buffer. 1737 * There're 2 cases: 1738 * 1739 * Case 1: 1740 * If the number of members in the old ECMP group is greater than or 1741 * equal to the number in the new ECMP group, populate the cleared 1742 * entries with shared members and members exclusive to the new ECMP 1743 * group, such that maximal balance is achieved among them. 1744 * 1745 * Case 2: 1746 * If the old ECMP group has fewer members than the new ECMP group, 1747 * populate the cleared entries with N members that are exclusive 1748 * to the new ECMP group, such that N + number of shared members equals 1749 * to the number of members in old ECMP group. 1750 * 1751 * In both cases, the rh set entries containing the shared members 1752 * are unmodified. 1753 */ 1754 if (old_intf_count >= new_intf_count) { 1755 intf_count = new_intf_count; 1756 } else { 1757 intf_count = old_intf_count; 1758 } 1759 rv = _bcm_opt_ecmp_rh_populate_empty_entries(unit, num_entries, overlay, rh_intf_array, 1760 member_id_buf, intf_count, member_array, entry_count_array, 1761 shared_intf_count); 1762 if (BCM_FAILURE(rv)) { 1763 goto cleanup; 1764 } 1765 1766 /* For any remaining unused members in member_array, move some 1767 * rh set entries from existing members to these remaining 1768 * members, such that maximal balance is achieved. 1769 */ 1770 if (new_intf_count > intf_count) { 1771 for (i = intf_count; i < new_intf_count; i++) { 1772 rv = _bcm_opt_ecmp_rh_add_rebalance(unit, num_entries, overlay, rh_intf_array, 1773 member_id_buf, i, member_array, entry_count_array, 1774 &member_array[i], &entry_count_array[i]); 1775 if (BCM_FAILURE(rv)) { 1776 goto cleanup; 1777 } 1778 } 1779 } 1780 1781 cleanup: 1782 if (member_array) { 1783 sal_free(member_array); 1784 } 1785 if (entry_count_array) { 1786 sal_free(entry_count_array); 1787 } 1788 if (member_id_buf) { 1789 sal_free(member_id_buf); 1790 } 1791 1792 return rv; 1793 } 1794 1795 /* 1796 * Function: 1797 * _bcm_opt_ecmp_rh_arrange_with_no_shared_entries 1798 * Purpose: 1799 * Arrange the given rh set entry buffer to achieve Maximization of the shared members, 1800 * while entries containing none of shared members. 1801 * Parameters: 1802 * unit - (IN) SOC unit number. 1803 * num_entries - (IN) Number of rh set entries in buffer. 1804 * rh_intf_array - (IN/OUT) intf array entry buffer. Initially, this buffer 1805 * contains the old ECMP group's rh set entries. 1806 * old_intf_count - (IN) Number of members exclusive to old ECMP group. 1807 * old_intf_array - (IN) Array of members exclusive to old ECMP group. 1808 * new_intf_count - (IN) Number of members exclusive to new ECMP group. 1809 * new_intf_array - (IN) Array of members exclusive to new ECMP group. 1810 * shared_intf_count - (OUT) Number of members shared by old and new ECMP 1811 * groups. 1812 * shared_intf_array - (OUT) Array of members shared by old and new ECMP 1813 * groups. 1814 * ex_old_intf_count - (OUT) Number of members exclusive to old ECMP group. 1815 * ex_old_intf_array - (OUT) Array of members exclusive to old ECMP group. 1816 * ex_new_intf_count - (OUT) Number of members exclusive to new ECMP group. 1817 * ex_new_intf_array - (OUT) Array of members exclusive to new ECMP group. 1818 * Returns: 1819 * BCM_E_XXX 1820 */ 1821 STATIC int 1822 _bcm_opt_ecmp_rh_arrange_with_no_shared_entries(int unit, 1823 int num_entries, 1824 bcm_if_t *rh_intf_array, 1825 int old_intf_count, 1826 bcm_if_t *old_intf_array, 1827 int new_intf_count, 1828 bcm_if_t *new_intf_array, 1829 int *shared_intf_count, 1830 bcm_if_t *shared_intf_array, 1831 int *ex_old_intf_count, 1832 bcm_if_t *ex_old_intf_array, 1833 int *ex_new_intf_count, 1834 bcm_if_t *ex_new_intf_array) 1835 { 1836 int rv = BCM_E_NONE; 1837 int num_members; 1838 int alloc_size; 1839 _ecmp_rh_member_t *member_array = NULL; 1840 int i; 1841 int *member_id_buf = NULL; 1842 int member_id; 1843 int max_shared; 1844 1845 if (num_entries < 1) { 1846 return BCM_E_PARAM; 1847 } 1848 if (old_intf_count > 0 && old_intf_array == NULL) { 1849 return BCM_E_PARAM; 1850 } 1851 if (new_intf_count > 0 && new_intf_array == NULL) { 1852 return BCM_E_PARAM; 1853 } 1854 if (NULL == shared_intf_array) { 1855 return BCM_E_PARAM; 1856 } 1857 if (NULL == ex_old_intf_array) { 1858 return BCM_E_PARAM; 1859 } 1860 if (NULL == ex_new_intf_array) { 1861 return BCM_E_PARAM; 1862 } 1863 1864 /* Allocate an array of members and an array of entry counts */ 1865 num_members = old_intf_count; 1866 alloc_size = num_members * sizeof(_ecmp_rh_member_t); 1867 member_array = sal_alloc(alloc_size, "ECMP RH member array"); 1868 if (NULL == member_array) { 1869 rv = BCM_E_MEMORY; 1870 goto cleanup; 1871 } 1872 sal_memset(member_array, 0, alloc_size); 1873 1874 /* Initialize the member_array with shared members, followed by members 1875 * exclusive to the old ECMP group. 1876 */ 1877 for (i = 0; i < num_members; i++) { 1878 member_array[i].nh_index = old_intf_array[i]; 1879 member_array[i].member_id = i; 1880 member_array[i].num_replica = 1; 1881 member_array[i].replica_id = 0; 1882 member_array[i].next_replica_id = 0; 1883 } 1884 1885 /* Find members that share the same next hop index, and 1886 * update the replica information in member_array. 1887 */ 1888 rv = _bcm_opt_ecmp_rh_member_replica_find(unit, old_intf_count, 1889 member_array); 1890 if (BCM_FAILURE(rv)) { 1891 goto cleanup; 1892 } 1893 1894 /* Derive a buffer of member IDs by assigning a member ID to each 1895 * entry of the rh set entry buffer. 1896 */ 1897 alloc_size = num_entries * sizeof(int); 1898 member_id_buf = sal_alloc(alloc_size, "ECMP RH member ID buffer"); 1899 if (NULL == member_id_buf) { 1900 rv = BCM_E_MEMORY; 1901 goto cleanup; 1902 } 1903 sal_memset(member_id_buf, 0, alloc_size); 1904 rv = _bcm_opt_ecmp_rh_member_id_buf_assign(unit, num_members, 1905 member_array, num_entries, rh_intf_array, member_id_buf); 1906 if (BCM_FAILURE(rv)) { 1907 goto cleanup; 1908 } 1909 1910 /* Determine members shared by old and new ECMP groups, and 1911 * members exclusive to the old and the new ECMP groups. 1912 */ 1913 max_shared = (old_intf_count > new_intf_count) ? new_intf_count : 1914 old_intf_count; 1915 1916 for (i = 0; i < num_entries; i++) { 1917 member_id = member_id_buf[i]; 1918 if (member_id >= max_shared) { 1919 continue; 1920 } 1921 rh_intf_array[i] = new_intf_array[member_id] + BCM_XGS3_EGRESS_IDX_MIN(unit); 1922 } 1923 1924 *shared_intf_count = max_shared; 1925 sal_memcpy(shared_intf_array, new_intf_array, 1926 *shared_intf_count * sizeof(bcm_if_t)); 1927 1928 if (old_intf_count > new_intf_count) { 1929 *ex_new_intf_count = 0; 1930 *ex_old_intf_count = old_intf_count - *shared_intf_count; 1931 sal_memcpy(ex_old_intf_array, &old_intf_array[*shared_intf_count], 1932 *ex_old_intf_count * sizeof(bcm_if_t)); 1933 } else { 1934 *ex_old_intf_count = 0; 1935 *ex_new_intf_count = new_intf_count - *shared_intf_count; 1936 sal_memcpy(ex_new_intf_array, &new_intf_array[*shared_intf_count], 1937 *ex_new_intf_count * sizeof(bcm_if_t)); 1938 } 1939 1940 cleanup: 1941 if (member_array) { 1942 sal_free(member_array); 1943 } 1944 if (member_id_buf) { 1945 sal_free(member_id_buf); 1946 } 1947 1948 return rv; 1949 } 1950 1951 /* 1952 * Function: 1953 * _bcm_opt_ecmp_rh_replace 1954 * Purpose: 1955 * Replace ECMP resilient hashing group members without rh set shuffle. 1956 * Parameters: 1957 * unit - (IN) SOC unit number. 1958 * ecmp - (IN) ECMP group info. 1959 * intf_count - (IN) Number of elements in intf_array. 1960 * intf_array - (IN) Array of Egress forwarding objects. 1961 * old_intf_count - (IN) Number of elements in old_intf_array. 1962 * old_intf_array - (IN) Array of Egress forwarding objects before replacing. 1963 * rh_intf_array - (IN/OUT) intf array entry buffer. Initially, this buffer 1964 * contains the old ECMP group's rh set entries. 1965 * Returns: 1966 * BCM_E_xxx 1967 */ 1968 STATIC int 1969 _bcm_opt_ecmp_rh_replace(int unit, 1970 bcm_l3_egress_ecmp_t *ecmp, 1971 int intf_count, 1972 bcm_if_t *intf_array, 1973 int old_intf_count, 1974 bcm_if_t *old_intf_array, 1975 bcm_if_t *rh_intf_array) 1976 { 1977 int rv = BCM_E_NONE; 1978 int ecmp_group; 1979 int offset; 1980 int num_entries; 1981 int alloc_size; 1982 int max_shared; 1983 int i, j; 1984 int shared_intf_count; 1985 int ex_old_intf_count; 1986 int ex_new_intf_count; 1987 bcm_if_t *shared_intf_array = NULL; 1988 bcm_if_t *ex_old_intf_array = NULL; 1989 bcm_if_t *ex_new_intf_array = NULL; 1990 bcm_if_t *temp_old_intf_array = NULL; 1991 bcm_if_t *temp_new_intf_array = NULL; 1992 int overlay = 0; 1993 1994 if (ecmp == NULL || 1995 ecmp->dynamic_mode != BCM_L3_ECMP_DYNAMIC_MODE_RESILIENT) { 1996 return BCM_E_PARAM; 1997 } 1998 1999 if (BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, ecmp->ecmp_intf)) { 2000 ecmp_group = ecmp->ecmp_intf - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2001 } else { 2002 return BCM_E_PARAM; 2003 } 2004 2005 num_entries = ecmp->dynamic_size; 2006 2007 if (intf_count > 0 && intf_array == NULL) { 2008 return BCM_E_PARAM; 2009 } 2010 2011 if (intf_count == 0) { 2012 /* Replacing the last member is the same as freeing all resources */ 2013 BCM_IF_ERROR_RETURN(_bcm_opt_ecmp_rh_free_resource(unit, ecmp_group)); 2014 2015 return BCM_E_NONE; 2016 } 2017 2018 if (old_intf_count == 0) { 2019 /* Replacing the first member is the same as setting one member */ 2020 return _bcm_opt_ecmp_rh_set(unit, ecmp, intf_count, intf_array, rh_intf_array); 2021 } 2022 2023 /* Determine members shared by old and new ECMP groups, and 2024 * members exclusive to the old and the new ECMP groups. 2025 */ 2026 max_shared = (old_intf_count > intf_count) ? intf_count : 2027 old_intf_count; 2028 2029 alloc_size = max_shared * sizeof(bcm_if_t); 2030 shared_intf_array = sal_alloc(alloc_size, "shared ecmp member array"); 2031 if (NULL == shared_intf_array) { 2032 rv = BCM_E_MEMORY; 2033 goto cleanup; 2034 } 2035 sal_memset(shared_intf_array, 0, alloc_size); 2036 2037 alloc_size = old_intf_count * sizeof(bcm_if_t); 2038 ex_old_intf_array = sal_alloc(alloc_size, 2039 "array of members exclusive to old ecmp group"); 2040 if (NULL == ex_old_intf_array) { 2041 rv = BCM_E_MEMORY; 2042 goto cleanup; 2043 } 2044 sal_memset(ex_old_intf_array, 0, alloc_size); 2045 2046 alloc_size = intf_count * sizeof(bcm_if_t); 2047 ex_new_intf_array = sal_alloc(alloc_size, 2048 "array of members exclusive to new ecmp group"); 2049 if (NULL == ex_new_intf_array) { 2050 rv = BCM_E_MEMORY; 2051 goto cleanup; 2052 } 2053 sal_memset(ex_new_intf_array, 0, alloc_size); 2054 2055 alloc_size = old_intf_count * sizeof(bcm_if_t); 2056 temp_old_intf_array = sal_alloc(alloc_size, "copy of old_intf_array"); 2057 if (NULL == temp_old_intf_array) { 2058 rv = BCM_E_MEMORY; 2059 goto cleanup; 2060 } 2061 sal_memcpy(temp_old_intf_array, old_intf_array, alloc_size); 2062 for (i = 0; i < old_intf_count; i++) { 2063 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, temp_old_intf_array[i])) { 2064 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 2065 } 2066 #ifdef BCM_TOMAHAWK3_SUPPORT 2067 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 2068 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 2069 temp_old_intf_array[i])) { 2070 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2071 } 2072 #endif 2073 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, temp_old_intf_array[i])) { 2074 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 2075 } else { 2076 rv = BCM_E_PARAM; 2077 goto cleanup; 2078 } 2079 temp_old_intf_array[i] -= offset; 2080 } 2081 alloc_size = intf_count * sizeof(bcm_if_t); 2082 temp_new_intf_array = sal_alloc(alloc_size, "copy of intf_array"); 2083 if (NULL == temp_new_intf_array) { 2084 rv = BCM_E_MEMORY; 2085 goto cleanup; 2086 } 2087 sal_memcpy(temp_new_intf_array, intf_array, alloc_size); 2088 for (i = 0; i < intf_count; i++) { 2089 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, temp_new_intf_array[i])) { 2090 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 2091 } 2092 #ifdef BCM_TOMAHAWK3_SUPPORT 2093 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 2094 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 2095 temp_new_intf_array[i])) { 2096 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2097 } 2098 #endif 2099 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, temp_new_intf_array[i])) { 2100 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 2101 } else { 2102 rv = BCM_E_PARAM; 2103 goto cleanup; 2104 } 2105 temp_new_intf_array[i] -= offset; 2106 } 2107 2108 /* Replace arrangement */ 2109 shared_intf_count = 0; 2110 ex_new_intf_count = 0; 2111 for (i = 0; i < intf_count; i++) { 2112 for (j = 0; j < old_intf_count; j++) { 2113 if (temp_new_intf_array[i] == temp_old_intf_array[j]) { 2114 shared_intf_array[shared_intf_count++] = temp_new_intf_array[i]; 2115 /* Mark the matched element invalid */ 2116 temp_old_intf_array[j] = BCM_XGS3_L3_INVALID_INDEX; 2117 break; 2118 } 2119 } 2120 if (j == old_intf_count) { 2121 ex_new_intf_array[ex_new_intf_count++] = temp_new_intf_array[i]; 2122 } 2123 } 2124 ex_old_intf_count = 0; 2125 for (i = 0; i < old_intf_count; i++) { 2126 if (temp_old_intf_array[i] != BCM_XGS3_L3_INVALID_INDEX) { 2127 ex_old_intf_array[ex_old_intf_count++] = temp_old_intf_array[i]; 2128 } 2129 } 2130 2131 if (shared_intf_count == 0) { 2132 /* The old and the new ECMP groups don't share any members. 2133 * Replacing rh set entries with max_shared members so as to 2134 * set shared_intf_count = max_shared. 2135 */ 2136 rv = _bcm_opt_ecmp_rh_arrange_with_no_shared_entries(unit, 2137 num_entries, rh_intf_array, 2138 old_intf_count, temp_old_intf_array, 2139 intf_count, temp_new_intf_array, 2140 &shared_intf_count, shared_intf_array, 2141 &ex_old_intf_count, ex_old_intf_array, 2142 &ex_new_intf_count, ex_new_intf_array); 2143 if (BCM_FAILURE(rv)) { 2144 goto cleanup; 2145 } 2146 } 2147 2148 /* Modify the rh set entry buffer to achieve balance among 2149 * shared members and members exclusive to the new ECMP group, 2150 * while keeping modifications of entries containing the 2151 * shared members to a minimum. 2152 */ 2153 2154 #if defined(BCM_TOMAHAWK3_SUPPORT) 2155 if (soc_feature(unit, soc_feature_l3_ecmp_hier_tbl) && 2156 (ecmp->ecmp_group_flags & BCM_L3_ECMP_OVERLAY)) { 2157 overlay = 1; 2158 } 2159 #endif 2160 2161 rv = _bcm_opt_ecmp_rh_balance_with_min_shared_mod(unit, 2162 num_entries, overlay, rh_intf_array, 2163 shared_intf_count, shared_intf_array, 2164 ex_old_intf_count, ex_old_intf_array, 2165 ex_new_intf_count, ex_new_intf_array); 2166 if (BCM_FAILURE(rv)) { 2167 goto cleanup; 2168 } 2169 2170 cleanup: 2171 if (shared_intf_array) { 2172 sal_free(shared_intf_array); 2173 } 2174 if (ex_old_intf_array) { 2175 sal_free(ex_old_intf_array); 2176 } 2177 if (ex_new_intf_array) { 2178 sal_free(ex_new_intf_array); 2179 } 2180 if (temp_old_intf_array) { 2181 sal_free(temp_old_intf_array); 2182 } 2183 if (temp_new_intf_array) { 2184 sal_free(temp_new_intf_array); 2185 } 2186 2187 return rv; 2188 } 2189 2190 2191 /* 2192 * Function: 2193 * bcm_opt_l3_egress_ecmp_rh_create 2194 * Purpose: 2195 * Create or modify an ECMP resilient hashing group. 2196 * Parameters: 2197 * unit - (IN) bcm device. 2198 * ecmp - (IN) ECMP group info. 2199 * intf_count - (IN) Number of elements in intf_array. 2200 * intf_array - (IN) Array of Egress forwarding objects. 2201 * op - (IN) Member operation: SET, ADD, DELETE, or REPLACE. 2202 * count - (IN) Number of elements in intf. 2203 * intf - (IN) Egress forwarding objects to add, delete, or replace. 2204 * rh_intf_array - (IN/OUT) intf array entry buffer. For new group, the rh set 2205 is output in this array. For modifications, this buffer 2206 * contains the old ECMP group's rh set entries. 2207 * Returns: 2208 * BCM_E_XXX 2209 */ 2210 int 2211 bcm_opt_l3_egress_ecmp_rh_create(int unit, bcm_l3_egress_ecmp_t *ecmp, 2212 int intf_count, 2213 bcm_if_t *intf_array, int op, int count, 2214 bcm_if_t *intf, 2215 bcm_if_t *rh_intf_array) 2216 { 2217 int rh_enable; 2218 int ecmp_group; 2219 2220 if (ecmp->dynamic_mode == BCM_L3_ECMP_DYNAMIC_MODE_RESILIENT) { 2221 /* Verify ecmp->dynamic_size */ 2222 rh_enable = 1; 2223 } else { 2224 rh_enable = 0; 2225 } 2226 2227 if (op == BCM_L3_ECMP_MEMBER_OP_SET) { 2228 /* Free resilient hashing resources associated with this ecmp group */ 2229 if (ecmp->flags & BCM_L3_WITH_ID) { 2230 ecmp_group = ecmp->ecmp_intf - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2231 BCM_IF_ERROR_RETURN(_bcm_opt_ecmp_rh_free_resource(unit, ecmp_group)); 2232 } 2233 2234 if (rh_enable) { 2235 /* Set resilient hashing members for this ecmp group */ 2236 BCM_IF_ERROR_RETURN 2237 (_bcm_opt_ecmp_rh_set(unit, ecmp, intf_count, intf_array, rh_intf_array)); 2238 } 2239 } else if (op == BCM_L3_ECMP_MEMBER_OP_ADD) { 2240 if (rh_enable) { 2241 /* Add new resilient hashing member to ecmp group */ 2242 BCM_IF_ERROR_RETURN 2243 (_bcm_opt_ecmp_rh_add(unit, ecmp, intf_count, intf_array, *intf, 2244 rh_intf_array)); 2245 } 2246 } else if (op == BCM_L3_ECMP_MEMBER_OP_DELETE) { 2247 if (rh_enable) { 2248 /* Delete resilient hashing member from ecmp group */ 2249 BCM_IF_ERROR_RETURN 2250 (_bcm_opt_ecmp_rh_delete(unit, ecmp, intf_count, intf_array, 2251 *intf, rh_intf_array)); 2252 } 2253 } else if (op == BCM_L3_ECMP_MEMBER_OP_REPLACE) { 2254 if (rh_enable) { 2255 /* Replace resilient hashing member for ecmp group */ 2256 BCM_IF_ERROR_RETURN 2257 (_bcm_opt_ecmp_rh_replace(unit, ecmp, intf_count, intf_array, 2258 count, intf, rh_intf_array)); 2259 } 2260 } else { 2261 return BCM_E_PARAM; 2262 } 2263 2264 return BCM_E_NONE; 2265 } 2266 2267 /* 2268 * Function: 2269 * bcm_opt_l3_egress_ecmp_rh_destroy 2270 * Purpose: 2271 * Destroy an ECMP resilient hashing group. 2272 * Parameters: 2273 * unit - (IN) bcm device. 2274 * mpintf - (IN) L3 interface id pointing to Egress multipath object. 2275 * Returns: 2276 * BCM_E_XXX 2277 */ 2278 int 2279 bcm_opt_l3_egress_ecmp_rh_destroy(int unit, bcm_if_t mpintf) 2280 { 2281 int ecmp_group; 2282 2283 if (BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, mpintf)) { 2284 ecmp_group = mpintf - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2285 } else { 2286 return BCM_E_PARAM; 2287 } 2288 2289 BCM_IF_ERROR_RETURN(_bcm_opt_ecmp_rh_free_resource(unit, ecmp_group)); 2290 2291 return BCM_E_NONE; 2292 } 2293 2294 /* 2295 * Function: 2296 * bcm_opt_l3_egress_ecmp_lb_get 2297 * Purpose: 2298 * Get load balancing info for the ECMP group. 2299 * Parameters: 2300 * unit - (IN) bcm device. 2301 * ecmp - (INOUT) ECMP group info. 2302 * Returns: 2303 * BCM_E_XXX 2304 */ 2305 int 2306 bcm_opt_l3_egress_ecmp_lb_get(int unit, bcm_l3_egress_ecmp_t *ecmp) 2307 { 2308 int ecmp_group; 2309 int lb_mode; 2310 ecmp_count_entry_t ecmp_count_entry; 2311 uint32 count; 2312 int min_count = 6; 2313 int max_count = 14; 2314 soc_mem_t mem = L3_ECMP_COUNTm; 2315 2316 if (BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, ecmp->ecmp_intf)) { 2317 ecmp_group = ecmp->ecmp_intf - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2318 } else { 2319 return BCM_E_PARAM; 2320 } 2321 2322 #ifdef BCM_TOMAHAWK3_SUPPORT 2323 if (soc_feature(unit, soc_feature_l3_ecmp_hier_tbl) && 2324 (BCM_XGS3_L3_TBL(unit, ecmp_info).ecmp_mode == 2325 ecmp_mode_hierarchical) && 2326 (ecmp_group < (BCM_XGS3_L3_ECMP_MAX_GROUPS(unit) / 2))) { 2327 ecmp->ecmp_group_flags |= BCM_L3_ECMP_OVERLAY; 2328 mem = ECMP_GROUP_HIERARCHICALm; 2329 } 2330 #endif 2331 2332 BCM_IF_ERROR_RETURN(soc_mem_read(unit, mem, MEM_BLOCK_ANY, 2333 ecmp_group, &ecmp_count_entry)); 2334 lb_mode = soc_mem_field32_get(unit, mem, 2335 &ecmp_count_entry, LB_MODEf); 2336 2337 if (lb_mode == BCM_TH_L3_ECMP_LB_MODE_RH) { 2338 ecmp->dynamic_mode = BCM_L3_ECMP_DYNAMIC_MODE_RESILIENT; 2339 count = soc_mem_field32_get(unit, mem, 2340 &ecmp_count_entry, COUNTf); 2341 if ((SOC_IS_TOMAHAWK2(unit)) || 2342 (SOC_IS_TOMAHAWK3(unit))) { 2343 max_count = 15; 2344 } 2345 if ((count < min_count) || (count > max_count)) { 2346 return BCM_E_INTERNAL; 2347 } else { 2348 ecmp->dynamic_size = (1 << count); 2349 } 2350 } else if (lb_mode == BCM_TH_L3_ECMP_LB_MODE_RANDOM) { 2351 ecmp->dynamic_mode = BCM_L3_ECMP_DYNAMIC_MODE_RANDOM; 2352 } else if (!(soc_feature(unit, soc_feature_l3_ecmp_weighted)) && 2353 (lb_mode == BCM_TH_L3_ECMP_LB_MODE_RR)) { 2354 ecmp->dynamic_mode = BCM_L3_ECMP_DYNAMIC_MODE_ROUND_ROBIN; 2355 } else { 2356 #ifdef BCM_TOMAHAWK3_SUPPORT 2357 if (soc_feature(unit, soc_feature_l3_ecmp_weighted)) { 2358 switch (lb_mode) { 2359 case BCM_TH3_L3_ECMP_LB_MODE_WEIGHTED_256: 2360 case BCM_TH3_L3_ECMP_LB_MODE_WEIGHTED_512: 2361 case BCM_TH3_L3_ECMP_LB_MODE_WEIGHTED_1024: 2362 case BCM_TH3_L3_ECMP_LB_MODE_WEIGHTED_2048: 2363 case BCM_TH3_L3_ECMP_LB_MODE_WEIGHTED_4096: 2364 ecmp->dynamic_mode = BCM_L3_ECMP_DYNAMIC_MODE_DISABLED; 2365 ecmp->ecmp_group_flags |= BCM_L3_ECMP_WEIGHTED; 2366 break; 2367 default: 2368 break; 2369 } 2370 } 2371 #endif 2372 } 2373 2374 return BCM_E_NONE; 2375 } 2376 2377 /* 2378 * Function: 2379 * bcm_opt_ecmp_lb_mode_set 2380 * Purpose: 2381 * Set load balancing mode for the given ECMP group 2382 * Parameters: 2383 * unit - (IN) SOC unit number. 2384 * ecmp_group_idx - (IN) ECMP group ID. 2385 * lb_mode - (IN) ECMP group loadbalancing mode 2386 * Returns: 2387 * BCM_E_xxx 2388 */ 2389 int 2390 bcm_opt_ecmp_lb_mode_set(int unit, 2391 int ecmp_group_idx, 2392 uint8 lb_mode) 2393 { 2394 ecmp_count_entry_t ecmp_count_entry; 2395 ecmp_rrlb_cnt_entry_t rrlb_cnt_entry; 2396 initial_l3_ecmp_group_entry_t initial_l3_ecmp_group_entry; 2397 soc_mem_t mem = L3_ECMP_COUNTm; 2398 2399 #if defined(BCM_TOMAHAWK3_SUPPORT) 2400 if (soc_feature(unit, soc_feature_l3_ecmp_hier_tbl) && 2401 (BCM_XGS3_L3_TBL(unit, ecmp_info).ecmp_mode == 2402 ecmp_mode_hierarchical) && 2403 (ecmp_group_idx < (BCM_XGS3_L3_ECMP_MAX_GROUPS(unit) / 2))) { 2404 mem = ECMP_GROUP_HIERARCHICALm; 2405 } 2406 #endif 2407 /* Update ECMP group table LB field*/ 2408 BCM_IF_ERROR_RETURN(soc_mem_read(unit, mem, MEM_BLOCK_ANY, ecmp_group_idx, 2409 &ecmp_count_entry)); 2410 soc_mem_field32_set(unit, mem, &ecmp_count_entry, 2411 LB_MODEf, lb_mode); 2412 BCM_IF_ERROR_RETURN(soc_mem_write(unit, mem, MEM_BLOCK_ALL, ecmp_group_idx, 2413 &ecmp_count_entry)); 2414 2415 if (SOC_MEM_IS_VALID(unit, INITIAL_L3_ECMP_GROUPm)) { 2416 BCM_IF_ERROR_RETURN(soc_mem_read(unit, INITIAL_L3_ECMP_GROUPm, 2417 MEM_BLOCK_ANY, ecmp_group_idx, &initial_l3_ecmp_group_entry)); 2418 soc_mem_field32_set(unit, INITIAL_L3_ECMP_GROUPm, 2419 &initial_l3_ecmp_group_entry, LB_MODEf, lb_mode); 2420 BCM_IF_ERROR_RETURN(soc_mem_write(unit, INITIAL_L3_ECMP_GROUPm, 2421 MEM_BLOCK_ALL, ecmp_group_idx, &initial_l3_ecmp_group_entry)); 2422 } 2423 2424 /* Reset round robin counter for the group */ 2425 if ((!(SOC_IS_TOMAHAWK3(unit))) && (lb_mode == BCM_TH_L3_ECMP_LB_MODE_RR)) { 2426 BCM_IF_ERROR_RETURN(READ_L3_ECMP_RRLB_CNTm(unit, MEM_BLOCK_ANY, 2427 ecmp_group_idx, &rrlb_cnt_entry)); 2428 soc_L3_ECMP_RRLB_CNTm_field32_set(unit, &rrlb_cnt_entry, 2429 RRLB_CNTf, 0); 2430 BCM_IF_ERROR_RETURN(WRITE_L3_ECMP_RRLB_CNTm(unit, MEM_BLOCK_ALL, 2431 ecmp_group_idx, &rrlb_cnt_entry)); 2432 } 2433 if (!SOC_IS_TRIDENT3X(unit)) { 2434 if (lb_mode == BCM_TH_L3_ECMP_LB_MODE_RH) { 2435 bcm_th_ecmp_group_rh_set(unit, ecmp_group_idx, 1); 2436 } else { 2437 bcm_th_ecmp_group_rh_set(unit, ecmp_group_idx, 0); 2438 } 2439 } 2440 2441 return BCM_E_NONE; 2442 } 2443 2444 /* 2445 * Function: 2446 * bcm_opt_ecmp_lb_mode_reset 2447 * Purpose: 2448 * Reset load balancing mode for the given ECMP group 2449 * Parameters: 2450 * unit - (IN) SOC unit number. 2451 * mpintf - (IN) ECMP group index. 2452 * Returns: 2453 * BCM_E_xxx 2454 */ 2455 int 2456 bcm_opt_ecmp_lb_mode_reset(int unit, 2457 bcm_if_t mpintf) 2458 { 2459 ecmp_count_entry_t ecmp_count_entry; 2460 int ecmp_group_idx; 2461 initial_l3_ecmp_group_entry_t initial_l3_ecmp_group_entry; 2462 soc_mem_t mem = L3_ECMP_COUNTm; 2463 2464 ecmp_group_idx = mpintf - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2465 #ifdef BCM_TOMAHAWK3_SUPPORT 2466 if (soc_feature(unit, soc_feature_l3_ecmp_hier_tbl) && 2467 (BCM_XGS3_L3_TBL(unit, ecmp_info).ecmp_mode == 2468 ecmp_mode_hierarchical) && 2469 (ecmp_group_idx < (BCM_XGS3_L3_ECMP_MAX_GROUPS(unit) / 2))) { 2470 mem = ECMP_GROUP_HIERARCHICALm; 2471 } 2472 #endif 2473 2474 /* Update ECMP group table LB field*/ 2475 BCM_IF_ERROR_RETURN(soc_mem_read(unit, mem, MEM_BLOCK_ANY, ecmp_group_idx, 2476 &ecmp_count_entry)); 2477 soc_mem_field32_set(unit, mem, &ecmp_count_entry, 2478 LB_MODEf, 0); 2479 BCM_IF_ERROR_RETURN(soc_mem_write(unit, mem, MEM_BLOCK_ALL, ecmp_group_idx, 2480 &ecmp_count_entry)); 2481 2482 if (SOC_MEM_IS_VALID(unit, INITIAL_L3_ECMP_GROUPm)) { 2483 BCM_IF_ERROR_RETURN(soc_mem_read(unit, INITIAL_L3_ECMP_GROUPm, MEM_BLOCK_ANY, 2484 ecmp_group_idx, &initial_l3_ecmp_group_entry)); 2485 soc_mem_field32_set(unit, INITIAL_L3_ECMP_GROUPm, 2486 &initial_l3_ecmp_group_entry, LB_MODEf, 0); 2487 BCM_IF_ERROR_RETURN(soc_mem_write(unit, INITIAL_L3_ECMP_GROUPm, 2488 MEM_BLOCK_ALL, ecmp_group_idx, &initial_l3_ecmp_group_entry)); 2489 } 2490 2491 if (!SOC_IS_TRIDENT3X(unit)) { 2492 bcm_th_ecmp_group_rh_set(unit, ecmp_group_idx, 0); 2493 } 2494 2495 return BCM_E_NONE; 2496 } 2497 2498 /* 2499 * Function: 2500 * bcm_opt_l3_egress_ecmp_rh_shared_copy 2501 * Purpose: 2502 * For members shared by the old and the new ECMP groups, 2503 * copy the resilient hash flow set entries containing these 2504 * members from the old to the new ECMP group, in order to minimize 2505 * flow-to-member reassignments when the old ECMP group in a route 2506 * entry is replaced by the new one. 2507 * Parameters: 2508 * unit - (IN) SOC unit number. 2509 * old_ecmp_group - (IN) Old ECMP group. 2510 * new_ecmp_group - (IN) New ECMP group. 2511 * Returns: 2512 * BCM_E_XXX 2513 */ 2514 int 2515 bcm_opt_l3_egress_ecmp_rh_shared_copy(int unit, int old_ecmp_group, 2516 int new_ecmp_group) 2517 { 2518 int rv = BCM_E_NONE; 2519 bcm_l3_egress_ecmp_t old_ecmp, new_ecmp, replace_ecmp; 2520 int old_intf_count, new_intf_count; 2521 bcm_if_t *old_intf_array = NULL; 2522 bcm_if_t *rh_old_intf_array = NULL; 2523 bcm_if_t *new_intf_array = NULL; 2524 int offset; 2525 int count; 2526 int max_shared; 2527 bcm_if_t *shared_intf_array = NULL; 2528 bcm_if_t *ex_old_intf_array = NULL; 2529 bcm_if_t *ex_new_intf_array = NULL; 2530 bcm_if_t *temp_old_intf_array = NULL; 2531 int shared_intf_count, ex_new_intf_count, ex_old_intf_count; 2532 int i, j; 2533 int num_entries; 2534 int overlay = 0; 2535 2536 /* Check if there are any routing entries pointing to the 2537 * new ECMP group. If so, the new ECMP group's flow set 2538 * entries should not be modified. 2539 */ 2540 if (BCM_XGS3_L3_ENT_REF_CNT(BCM_XGS3_L3_TBL_PTR(unit, ecmp_grp), 2541 new_ecmp_group) > 2) { 2542 /* When the new ECMP group was created, its reference count 2543 * was set to 1. Then, in bcm_xgs3_defip_add, its reference count 2544 * was incremented before this procedure is invoked. Hence, 2545 * if there are any routing entry already pointing at the new 2546 * ECMP group, the reference count would be greater than 2. 2547 */ 2548 return BCM_E_NONE; 2549 } 2550 2551 /* Check if the old ECMP group is resilient hash enabled. */ 2552 bcm_l3_egress_ecmp_t_init(&old_ecmp); 2553 old_ecmp.ecmp_intf = old_ecmp_group + BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2554 BCM_IF_ERROR_RETURN(bcm_esw_l3_egress_ecmp_get(unit, &old_ecmp, 2555 0, NULL, &old_intf_count)); 2556 if (old_ecmp.dynamic_mode != BCM_L3_ECMP_DYNAMIC_MODE_RESILIENT) { 2557 /* The old ECMP group was not resilient hash enabled. Copying 2558 * flow set entries is not applicable. 2559 */ 2560 return BCM_E_NONE; 2561 } 2562 2563 /* Check if the new ECMP group is resilient hash enabled. */ 2564 bcm_l3_egress_ecmp_t_init(&new_ecmp); 2565 new_ecmp.ecmp_intf = new_ecmp_group + BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2566 BCM_IF_ERROR_RETURN(bcm_esw_l3_egress_ecmp_get(unit, &new_ecmp, 2567 0, NULL, &new_intf_count)); 2568 if (new_ecmp.dynamic_mode != BCM_L3_ECMP_DYNAMIC_MODE_RESILIENT) { 2569 /* The new ECMP group is not resilient hash enabled. Copying 2570 * flow set entries is not applicable. 2571 */ 2572 return BCM_E_NONE; 2573 } 2574 2575 if (old_ecmp.dynamic_size != new_ecmp.dynamic_size) { 2576 /* The number of flow set entries in old ECMP group is not the 2577 * same as the number in new ECMP group. Copying 2578 * flow set entries is not applicable. 2579 */ 2580 return BCM_E_NONE; 2581 } 2582 2583 /* Get old ECMP group members */ 2584 old_intf_array = sal_alloc(old_ecmp.dynamic_size * sizeof(bcm_if_t), 2585 "old ecmp member array"); 2586 if (NULL == old_intf_array) { 2587 rv = BCM_E_MEMORY; 2588 goto cleanup; 2589 } 2590 sal_memset(old_intf_array, 0, old_ecmp.dynamic_size * sizeof(bcm_if_t)); 2591 rv = bcm_esw_l3_egress_ecmp_get(unit, &old_ecmp, old_intf_count, 2592 old_intf_array, &count); 2593 if (BCM_FAILURE(rv)) { 2594 goto cleanup; 2595 } 2596 for (i = 0; i < old_intf_count; i++) { 2597 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, old_intf_array[i])) { 2598 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 2599 } 2600 #ifdef BCM_TOMAHAWK3_SUPPORT 2601 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 2602 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 2603 old_intf_array[i])) { 2604 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2605 } 2606 #endif 2607 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, old_intf_array[i])) { 2608 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 2609 } else { 2610 rv = BCM_E_PARAM; 2611 goto cleanup; 2612 } 2613 old_intf_array[i] -= offset; 2614 } 2615 2616 /* Get new ECMP group members */ 2617 new_intf_array = sal_alloc(new_ecmp.dynamic_size * sizeof(bcm_if_t), 2618 "new ecmp member array"); 2619 if (NULL == new_intf_array) { 2620 rv = BCM_E_MEMORY; 2621 goto cleanup; 2622 } 2623 sal_memset(new_intf_array, 0, new_ecmp.dynamic_size * sizeof(bcm_if_t)); 2624 rv = bcm_esw_l3_egress_ecmp_get(unit, &new_ecmp, new_intf_count, 2625 new_intf_array, &count); 2626 if (BCM_FAILURE(rv)) { 2627 goto cleanup; 2628 } 2629 for (i = 0; i < new_intf_count; i++) { 2630 if (BCM_XGS3_L3_EGRESS_IDX_VALID(unit, new_intf_array[i])) { 2631 offset = BCM_XGS3_EGRESS_IDX_MIN(unit); 2632 } 2633 #ifdef BCM_TOMAHAWK3_SUPPORT 2634 else if ((soc_feature(unit, soc_feature_l3_ecmp_hier_tbl)) && 2635 BCM_XGS3_L3_MPATH_EGRESS_IDX_VALID(unit, 2636 new_intf_array[i])) { 2637 offset = BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2638 } 2639 #endif 2640 else if (BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, new_intf_array[i])) { 2641 offset = BCM_XGS3_DVP_EGRESS_IDX_MIN(unit); 2642 } else { 2643 rv = BCM_E_PARAM; 2644 goto cleanup; 2645 } 2646 new_intf_array[i] -= offset; 2647 } 2648 2649 /* Determine members shared by old and new ECMP groups, and 2650 * members exclusive to the old and the new ECMP groups. 2651 */ 2652 max_shared = (old_intf_count > new_intf_count) ? new_intf_count : 2653 old_intf_count; 2654 shared_intf_array = sal_alloc(max_shared * sizeof(bcm_if_t), 2655 "shared ecmp member array"); 2656 if (NULL == shared_intf_array) { 2657 rv = BCM_E_MEMORY; 2658 goto cleanup; 2659 } 2660 sal_memset(shared_intf_array, 0, max_shared * sizeof(bcm_if_t)); 2661 2662 ex_old_intf_array = sal_alloc(old_intf_count * sizeof(bcm_if_t), 2663 "array of members exclusive to old ecmp group"); 2664 if (NULL == ex_old_intf_array) { 2665 rv = BCM_E_MEMORY; 2666 goto cleanup; 2667 } 2668 sal_memset(ex_old_intf_array, 0, old_intf_count * sizeof(bcm_if_t)); 2669 2670 ex_new_intf_array = sal_alloc(new_intf_count * sizeof(bcm_if_t), 2671 "array of members exclusive to new ecmp group"); 2672 if (NULL == ex_new_intf_array) { 2673 rv = BCM_E_MEMORY; 2674 goto cleanup; 2675 } 2676 sal_memset(ex_new_intf_array, 0, new_intf_count * sizeof(bcm_if_t)); 2677 2678 temp_old_intf_array = sal_alloc(old_intf_count * sizeof(bcm_if_t), 2679 "copy of old_intf_array"); 2680 if (NULL == temp_old_intf_array) { 2681 rv = BCM_E_MEMORY; 2682 goto cleanup; 2683 } 2684 sal_memcpy(temp_old_intf_array, old_intf_array, old_intf_count * sizeof(bcm_if_t)); 2685 2686 shared_intf_count = 0; 2687 ex_new_intf_count = 0; 2688 for (i = 0; i < new_intf_count; i++) { 2689 for (j = 0; j < old_intf_count; j++) { 2690 if (new_intf_array[i] == temp_old_intf_array[j]) { 2691 shared_intf_array[shared_intf_count++] = new_intf_array[i]; 2692 /* Mark the matched element invalid */ 2693 temp_old_intf_array[j] = BCM_XGS3_L3_INVALID_INDEX; 2694 break; 2695 } 2696 } 2697 if (j == old_intf_count) { 2698 ex_new_intf_array[ex_new_intf_count++] = new_intf_array[i]; 2699 } 2700 } 2701 2702 ex_old_intf_count = 0; 2703 for (i = 0; i < old_intf_count; i++) { 2704 if (temp_old_intf_array[i] != BCM_XGS3_L3_INVALID_INDEX) { 2705 ex_old_intf_array[ex_old_intf_count++] = temp_old_intf_array[i]; 2706 } 2707 } 2708 2709 if (shared_intf_count == 0) { 2710 /* The old and the new ECMP groups don't share any members. 2711 * Copying flow set entries is not applicable. 2712 */ 2713 rv = BCM_E_NONE; 2714 goto cleanup; 2715 } 2716 2717 /* Get old ECMP RH Set */ 2718 rh_old_intf_array = sal_alloc(old_ecmp.dynamic_size * sizeof(bcm_if_t), 2719 "new ecmp member array"); 2720 if (NULL == rh_old_intf_array) { 2721 rv = BCM_E_MEMORY; 2722 goto cleanup; 2723 } 2724 sal_memset(rh_old_intf_array, 0, old_ecmp.dynamic_size * sizeof(bcm_if_t)); 2725 rv = bcm_xgs3_l3_egress_multipath_get(unit, 2726 old_ecmp_group + BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit), 2727 BCM_XGS3_L3_ECMP_MAX(unit), 2728 rh_old_intf_array, &num_entries); 2729 if (BCM_FAILURE(rv)) { 2730 goto cleanup; 2731 } 2732 /* Modify the flow set entry buffer to achieve balance among 2733 * shared members and members exclusive to the new ECMP group, 2734 * while keeping modifications of entries containing the 2735 * shared members to a minimum. 2736 */ 2737 2738 #if defined(BCM_TOMAHAWK3_SUPPORT) 2739 if (soc_feature(unit, soc_feature_l3_ecmp_hier_tbl) && 2740 (BCM_XGS3_L3_TBL(unit, ecmp_info).ecmp_mode == 2741 ecmp_mode_hierarchical) && 2742 (new_ecmp_group < (BCM_XGS3_L3_ECMP_MAX_GROUPS(unit) / 2))) { 2743 overlay = 1; 2744 } 2745 #endif 2746 2747 rv = _bcm_opt_ecmp_rh_balance_with_min_shared_mod(unit, 2748 num_entries, overlay, rh_old_intf_array, 2749 shared_intf_count, shared_intf_array, 2750 ex_old_intf_count, ex_old_intf_array, 2751 ex_new_intf_count, ex_new_intf_array); 2752 if (BCM_FAILURE(rv)) { 2753 goto cleanup; 2754 } 2755 2756 /* Replace the new ECMP group's RH set entries with the buffer */ 2757 bcm_l3_egress_ecmp_t_init(&replace_ecmp); 2758 replace_ecmp.ecmp_intf = new_ecmp_group + BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 2759 replace_ecmp.flags = BCM_L3_REPLACE | BCM_L3_WITH_ID; 2760 /* Set no sorting flag so that RH set is not sorted */ 2761 replace_ecmp.ecmp_group_flags = BCM_L3_ECMP_PATH_NO_SORTING; 2762 /* Set RH flag so that RH length checks apply */ 2763 replace_ecmp.ecmp_group_flags |= BCM_L3_ECMP_RH_OPT; 2764 replace_ecmp.max_paths = num_entries; 2765 rv = bcm_esw_l3_egress_ecmp_create(unit, &replace_ecmp, num_entries, 2766 rh_old_intf_array); 2767 if (BCM_FAILURE(rv)) { 2768 goto cleanup; 2769 } 2770 BCM_XGS3_L3_ECMP_GROUP_FLAGS_RESET(unit, 2771 (replace_ecmp.ecmp_intf - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit)), 2772 BCM_L3_ECMP_PATH_NO_SORTING); 2773 2774 /* set back lb mode. no need to set intf array, since they are same */ 2775 rv = bcm_opt_ecmp_lb_mode_set(unit, new_ecmp_group, 2776 BCM_TH_L3_ECMP_LB_MODE_RH); 2777 2778 cleanup: 2779 if (old_intf_array) { 2780 sal_free(old_intf_array); 2781 } 2782 if (new_intf_array) { 2783 sal_free(new_intf_array); 2784 } 2785 if (shared_intf_array) { 2786 sal_free(shared_intf_array); 2787 } 2788 if (ex_old_intf_array) { 2789 sal_free(ex_old_intf_array); 2790 } 2791 if (ex_new_intf_array) { 2792 sal_free(ex_new_intf_array); 2793 } 2794 if (temp_old_intf_array) { 2795 sal_free(temp_old_intf_array); 2796 } 2797 if (rh_old_intf_array) { 2798 sal_free(rh_old_intf_array); 2799 } 2800 2801 return rv; 2802 } 2803 2804 2805 #ifdef BCM_WARM_BOOT_SUPPORT 2806 2807 /* 2808 * Function: 2809 * bcm_opt_l3_ecmp_rh_member_sync 2810 * Purpose: 2811 * Store ECMP members in the scache. 2812 * Parameters: 2813 * unit - (IN) SOC unit number. 2814 * scache_ptr - (IN/OUT) Scache pointer 2815 * Returns: 2816 * BCM_E_XXX 2817 */ 2818 int 2819 bcm_opt_l3_ecmp_rh_member_sync(int unit, uint8 **scache_ptr) 2820 { 2821 int16 i; 2822 int j; 2823 bcm_if_t *rh_intf_arr_ptr = NULL; 2824 int intf_count; 2825 uint8 *end_scache_ptr; 2826 int16 *grp_ptr; 2827 bcm_if_t *mem_ptr; 2828 2829 if ((scache_ptr == NULL) || (*scache_ptr == NULL)) { 2830 return BCM_E_PARAM; 2831 } 2832 2833 /* ECMP RH max paths info */ 2834 sal_memcpy((*scache_ptr), &BCM_XGS3_L3_ECMP_RH_MAX_PATHS(unit), 2835 sizeof(int)); 2836 (*scache_ptr) += sizeof(int); 2837 2838 /* ECMP RH per group max_paths info */ 2839 for (i = 0; i < BCM_XGS3_L3_ECMP_MAX_GROUPS(unit); i++) { 2840 rh_intf_arr_ptr = (_opt_ecmp_rh_info[unit]->rhg[i].rh_intf_arr); 2841 if (rh_intf_arr_ptr) { 2842 sal_memcpy((*scache_ptr), 2843 &(_opt_ecmp_rh_info[unit]->rhg[i].max_paths), 2844 sizeof(uint16)); 2845 } 2846 (*scache_ptr) += sizeof(uint16); 2847 } 2848 2849 /* 2850 * Scache will have size as large as ECMP Member table. 2851 * Each entry will be of (ecmp group index, ecmp member). 2852 * Multiple members for a group will take up multiple consecutive entries 2853 */ 2854 end_scache_ptr = *scache_ptr + 2855 (RH_ECMP_MEMBER_TBL_MAX(unit) * (sizeof(int16) + sizeof(bcm_if_t))); 2856 /* Copy (group index, member) tuple for each group into the member table */ 2857 for (i = 0; i < BCM_XGS3_L3_ECMP_MAX_GROUPS(unit); i++) { 2858 rh_intf_arr_ptr = (_opt_ecmp_rh_info[unit]->rhg[i].rh_intf_arr); 2859 if (rh_intf_arr_ptr) { 2860 intf_count = _opt_ecmp_rh_info[unit]->rhg[i].rh_intf_count; 2861 2862 /* Store groups with empty members */ 2863 if (intf_count == 0) { 2864 intf_count = 1; 2865 } 2866 2867 for (j = 0; j < intf_count; j++) { 2868 /* Increment group index before storing so that 0 can be used to 2869 * check for an invalid entry 2870 */ 2871 grp_ptr = (int16 *)(*scache_ptr); 2872 *grp_ptr = i + 1; 2873 *scache_ptr += sizeof(int16); 2874 mem_ptr = (bcm_if_t *)(*scache_ptr); 2875 *mem_ptr = rh_intf_arr_ptr[j]; 2876 *scache_ptr += sizeof(bcm_if_t); 2877 } 2878 } 2879 } 2880 *scache_ptr = end_scache_ptr; 2881 2882 return BCM_E_NONE; 2883 } 2884 2885 /* 2886 * Function: 2887 * bcm_opt_l3_ecmp_rh_member_recover 2888 * Purpose: 2889 * Recover RH ECMP members from scache. 2890 * Parameters: 2891 * unit - (IN) SOC unit number. 2892 * scache_ptr - (IN/OUT) Scache pointer 2893 * Returns: 2894 * BCM_E_XXX 2895 */ 2896 int 2897 bcm_opt_l3_ecmp_rh_member_recover(int unit, uint8 **scache_ptr, int ecmp_max_paths) 2898 { 2899 bcm_if_t **rh_intf_arr_ptr; 2900 int stable_size = 0; 2901 int intf_count; 2902 int j; 2903 uint8 *end_scache_ptr; 2904 int16 *grp_ptr; 2905 int16 ecmp_group_index; 2906 bcm_if_t *mem_ptr; 2907 uint8 *start_grp_ptr; 2908 bcm_if_t *hash_intf_array = NULL; 2909 int alloc_size; 2910 uint16 hash; 2911 int grp_size; 2912 ecmp_count_entry_t entry; 2913 2914 if ((scache_ptr == NULL) || (*scache_ptr == NULL)) { 2915 return BCM_E_PARAM; 2916 } 2917 2918 if (SOC_WARM_BOOT(unit)) { 2919 BCM_IF_ERROR_RETURN(soc_stable_size_get(unit, &stable_size)); 2920 2921 if (stable_size == 0) { /* level 1 */ 2922 /* Nothing to recover */ 2923 } else { /* Level 2 */ 2924 /* Set max_paths here for hash calculation */ 2925 BCM_XGS3_L3_ECMP_MAX_PATHS(unit) = ecmp_max_paths; 2926 /* recover from scache into book-keeping structs */ 2927 /* Copy RH max paths */ 2928 sal_memcpy(&BCM_XGS3_L3_ECMP_RH_MAX_PATHS(unit), (*scache_ptr), 2929 sizeof(int)); 2930 (*scache_ptr) += sizeof(int); 2931 2932 /* Retrieve ECMP RH per group max_paths info */ 2933 for (j = 0; j < BCM_XGS3_L3_ECMP_MAX_GROUPS(unit); j++) { 2934 sal_memcpy(&(_opt_ecmp_rh_info[unit]->rhg[j].max_paths), 2935 (*scache_ptr), 2936 sizeof(uint16)); 2937 (*scache_ptr) += sizeof(uint16); 2938 } 2939 2940 end_scache_ptr = *scache_ptr + 2941 (RH_ECMP_MEMBER_TBL_MAX(unit) * (sizeof(int16) + sizeof(bcm_if_t))); 2942 while ((*scache_ptr) < end_scache_ptr) { 2943 start_grp_ptr = (*scache_ptr); 2944 ecmp_group_index = *((int16 *)start_grp_ptr); 2945 /* Look for the first member of a valid group */ 2946 if (ecmp_group_index != 0) { 2947 intf_count = 0; 2948 grp_size = 0; 2949 /* Find member count */ 2950 do { 2951 intf_count++; 2952 start_grp_ptr += (sizeof(int16) + sizeof(bcm_if_t)); 2953 grp_ptr = (int16 *)(start_grp_ptr); 2954 } while (ecmp_group_index == *grp_ptr); 2955 grp_size = intf_count; 2956 /* Decrement group index to get the original value */ 2957 ecmp_group_index--; 2958 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_index].rh_intf_count = 2959 intf_count; 2960 rh_intf_arr_ptr = 2961 &(_opt_ecmp_rh_info[unit]->rhg[ecmp_group_index].rh_intf_arr); 2962 *rh_intf_arr_ptr = sal_alloc(sizeof(bcm_if_t) * intf_count, 2963 "ECMP RH entry count array"); 2964 if (NULL == *rh_intf_arr_ptr) { 2965 return(BCM_E_MEMORY); 2966 } 2967 sal_memset(*rh_intf_arr_ptr, 0, (sizeof(bcm_if_t) * 2968 intf_count)); 2969 /* Go back to the beginning of the group and copy ECMP member array */ 2970 start_grp_ptr = (*scache_ptr); 2971 for (j = 0; j < intf_count; j++) { 2972 start_grp_ptr += sizeof(int16); 2973 mem_ptr = (bcm_if_t *)(start_grp_ptr); 2974 *(*rh_intf_arr_ptr + j) = *mem_ptr; 2975 start_grp_ptr += sizeof(bcm_if_t); 2976 } 2977 2978 /* Group with empty members will have invalid interface */ 2979 if (!(BCM_XGS3_L3_EGRESS_IDX_VALID(unit, *(*rh_intf_arr_ptr))) 2980 && !(BCM_XGS3_DVP_EGRESS_IDX_VALID(unit, 2981 *(*rh_intf_arr_ptr)))) { 2982 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_index]. 2983 rh_intf_count = 0; 2984 intf_count = 0; 2985 } 2986 2987 /* 2988 * Sort ECMP member array. This should already be in sorted order. 2989 * Sort again before computing data hash to be safe 2990 */ 2991 alloc_size = sizeof(bcm_if_t) * BCM_XGS3_L3_ECMP_MAX(unit); 2992 hash_intf_array = sal_alloc(alloc_size, "RH intf array"); 2993 if (NULL == hash_intf_array) { 2994 return(BCM_E_MEMORY); 2995 } 2996 sal_memset(hash_intf_array, 0, alloc_size); 2997 sal_memcpy(hash_intf_array, *rh_intf_arr_ptr, 2998 sizeof(bcm_if_t) * intf_count); 2999 _shr_sort(hash_intf_array, intf_count, sizeof(int), _opt_rh_cmp_int); 3000 sal_memcpy(*rh_intf_arr_ptr, hash_intf_array, 3001 (sizeof(bcm_if_t) * intf_count)); 3002 /* Compute member hash */ 3003 _bcm_opt_rh_ecmp_grp_hash_calc(unit, hash_intf_array, &hash); 3004 _opt_ecmp_rh_info[unit]->rhg[ecmp_group_index].data_hash = hash; 3005 sal_free(hash_intf_array); 3006 hash_intf_array = NULL; 3007 3008 BCM_IF_ERROR_RETURN(READ_L3_ECMP_COUNTm(unit, MEM_BLOCK_ANY, 3009 ecmp_group_index, &entry)); 3010 if (BCM_TH_L3_ECMP_LB_MODE_RH == 3011 soc_L3_ECMP_COUNTm_field32_get(unit, &entry, LB_MODEf)) { 3012 bcm_th_ecmp_group_rh_set(unit, ecmp_group_index, 1); 3013 } 3014 3015 /* Advance scache pointer to the beginning of next distinct group id */ 3016 *scache_ptr += ((sizeof(int16) + sizeof(bcm_if_t)) * grp_size); 3017 } else { 3018 *scache_ptr += (sizeof(int16) + sizeof(bcm_if_t)); 3019 } 3020 } 3021 *scache_ptr = end_scache_ptr; 3022 } 3023 } 3024 3025 return BCM_E_NONE; 3026 } 3027 3028 #endif /* BCM_WARM_BOOT_SUPPORT */ 3029 3030 #ifndef BCM_SW_STATE_DUMP_DISABLE 3031 3032 /* 3033 * Function: 3034 * bcm_opt_ecmp_rh_sw_dump 3035 * Purpose: 3036 * Displays ECMP resilient hashing state maintained by software. 3037 * Parameters: 3038 * unit - Device unit number 3039 * Returns: 3040 * None 3041 */ 3042 void bcm_opt_ecmp_rh_sw_dump(int unit) 3043 { 3044 int i, j; 3045 bcm_if_t *rh_intf_arr_ptr; 3046 3047 LOG_CLI((BSL_META_U(unit, 3048 " ECMP Resilient Hashing Info -\n"))); 3049 3050 /* Print ECMP RH member entries*/ 3051 if (_opt_ecmp_rh_info[unit]) { 3052 for (i = 0; i < BCM_XGS3_L3_ECMP_MAX_GROUPS(unit); i++) { 3053 /* Get ECMP member array */ 3054 rh_intf_arr_ptr = (_opt_ecmp_rh_info[unit]->rhg[i].rh_intf_arr); 3055 if (rh_intf_arr_ptr) { 3056 LOG_CLI((BSL_META_U(unit, 3057 "RH Group %4d, Member count %4d, Max paths %4d: "), 3058 i, _opt_ecmp_rh_info[unit]->rhg[i].rh_intf_count, 3059 _opt_ecmp_rh_info[unit]->rhg[i].max_paths)); 3060 for (j = 0; j < _opt_ecmp_rh_info[unit]->rhg[i].rh_intf_count; j++) 3061 { 3062 LOG_CLI((BSL_META_U(unit, 3063 " %4d"), rh_intf_arr_ptr[j])); 3064 } 3065 LOG_CLI((BSL_META_U(unit, 3066 "\n"))); 3067 } 3068 } 3069 } 3070 3071 LOG_CLI((BSL_META_U(unit, 3072 "\n"))); 3073 3074 return; 3075 } 3076 3077 #endif /* BCM_SW_STATE_DUMP_DISABLE */ 3078 3079 #endif /* BCM_TOMAHAWK_SUPPORT && INCLIDE_L3 */ 3080