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

l3x.c (54408B)


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