openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

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