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

l2u.c (14371B)


      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  * XGS3 L2 User Table Manipulation API routines.
      8  *
      9  * The L2 User Table (L2_USER_ENTRY) is used for various
     10  * purposes including:
     11  *
     12  *  - L2 table overflow
     13  *  - BPDU addresses
     14  *  - 802.1 reserved addresses
     15  *  - L2 address blocks (using MAC address mask)
     16  *
     17  * The table is segmented into two parts of which the
     18  * first is referenced by index, and the second is not.
     19  * This segmentation is implemented to facilitate
     20  * different requirements from the BCM API functions.
     21  */
     22 
     23 #include <sal/core/libc.h>
     24 
     25 #include <soc/drv.h>
     26 #include <soc/l2u.h>
     27 #include <soc/debug.h>
     28 #include <soc/util.h>
     29 #include <soc/mem.h>
     30 
     31 #ifdef BCM_XGS3_SWITCH_SUPPORT
     32 
     33 #define SOC_MEM_COMPARE_RETURN(a, b) {		\
     34         if ((a) < (b)) { return -1; }		\
     35         if ((a) > (b)) { return  1; }		\
     36 }
     37 
     38 /*
     39  * Function:
     40  *	_soc_mem_cmp_l2u
     41  * Purpose:
     42  *	Compare two L2 User table entries
     43  * Parameters:
     44  *	unit, entry A and entry B
     45  * Returns:
     46  *	Negative for A < B, zero for A == B, positive for A > B
     47  */
     48 STATIC int
     49 _soc_mem_cmp_l2u(int unit, void *ent_a, void *ent_b)
     50 {
     51     uint32 mask_a[SOC_MAX_MEM_WORDS / 2], mask_b[SOC_MAX_MEM_WORDS / 2];
     52     uint32 key_type_a, key_type_b;
     53     sal_mac_addr_t mac_a, mac_b;
     54     vlan_id_t vlan_a, vlan_b;
     55 
     56     soc_L2_USER_ENTRYm_field_get(unit, ent_a, MASKf, mask_a);
     57     soc_L2_USER_ENTRYm_field_get(unit, ent_b, MASKf, mask_b);
     58     SOC_MEM_COMPARE_RETURN(mask_a[0], mask_b[0]);
     59     SOC_MEM_COMPARE_RETURN(mask_a[1], mask_b[1]);
     60 
     61     vlan_a = soc_L2_USER_ENTRYm_field32_get(unit, ent_a, VLAN_IDf);
     62     vlan_b = soc_L2_USER_ENTRYm_field32_get(unit, ent_b, VLAN_IDf);
     63     SOC_MEM_COMPARE_RETURN(vlan_a, vlan_b);
     64 
     65     if (soc_mem_field_valid(unit, L2_USER_ENTRYm, KEY_TYPEf)) {
     66         key_type_a = soc_L2_USER_ENTRYm_field32_get(unit, ent_a, KEY_TYPEf);
     67         key_type_b = soc_L2_USER_ENTRYm_field32_get(unit, ent_b, KEY_TYPEf);
     68         SOC_MEM_COMPARE_RETURN(key_type_a, key_type_b);
     69     }
     70 
     71     soc_L2_USER_ENTRYm_mac_addr_get(unit, ent_a, MAC_ADDRf, mac_a);
     72     soc_L2_USER_ENTRYm_mac_addr_get(unit, ent_b, MAC_ADDRf, mac_b);
     73 
     74     return ENET_CMP_MACADDR(mac_a, mac_b);
     75 }
     76 
     77 /*
     78  * Function:
     79  *      _soc_l2u_overlap_get
     80  * Purpose:
     81  *      Check if L2 User table entries overlap
     82  * Parameters:
     83  *	unit, entry A and entry B
     84  * Returns:
     85  *      True if entries overlap
     86  */
     87 STATIC int 
     88 _soc_l2u_overlap_get(int unit, l2u_entry_t *ent_a, l2u_entry_t *ent_b)
     89 {
     90     uint32 mask_a[SOC_MAX_MEM_WORDS / 2], mask_b[SOC_MAX_MEM_WORDS / 2];
     91     uint32 mac_a[2], mac_b[2];
     92     vlan_id_t vlan_a, vlan_b;
     93 
     94     soc_L2_USER_ENTRYm_field_get(unit, ent_a, MASKf, mask_a);
     95     soc_L2_USER_ENTRYm_field_get(unit, ent_b, MASKf, mask_b);
     96 
     97     vlan_a = soc_L2_USER_ENTRYm_field32_get(unit, ent_a, VLAN_IDf);
     98     vlan_a &= (mask_a[1] >> 16) & (mask_b[1] >> 16);
     99     vlan_b = soc_L2_USER_ENTRYm_field32_get(unit, ent_b, VLAN_IDf);
    100     vlan_b &= (mask_a[1] >> 16) & (mask_b[1] >> 16);
    101     if (vlan_a == vlan_b) {
    102         return TRUE;
    103     }
    104 
    105     soc_L2_USER_ENTRYm_field_get(unit, ent_a, MAC_ADDRf, mac_a);
    106     mac_a[0] &= mask_a[0] & mask_b[0];
    107     mac_a[1] &= mask_a[1] & mask_b[1];
    108     soc_L2_USER_ENTRYm_field_get(unit, ent_b, MAC_ADDRf, mac_b);
    109     mac_b[0] &= mask_a[0] & mask_b[0];
    110     mac_b[1] &= mask_a[1] & mask_b[1];
    111 
    112     if (sal_memcmp(mac_a, mac_b, sizeof(mac_a)) == 0) {
    113         return TRUE;
    114     }
    115 
    116     return FALSE;
    117 }
    118 
    119 /*
    120  * Function:
    121  *      soc_l2u_overlap
    122  * Purpose:
    123  *      Check for overlapping entry in L2 User table
    124  * Parameters:
    125  *      unit - SOC unit number
    126  *	entry - entry to check
    127  *      index - (OUT) index where found
    128  * Returns:
    129  *      SOC_E_NONE
    130  *      SOC_E_EXISTS
    131  */
    132 int 
    133 soc_l2u_overlap_check(int unit, l2u_entry_t *entry, int *index)
    134 {
    135     l2u_entry_t l2u_entry;
    136     int i, i_min, i_max, skip_l2u;
    137 
    138     skip_l2u = soc_property_get(unit, spn_SKIP_L2_USER_ENTRY, 0);
    139     if (skip_l2u) {
    140         return SOC_E_UNAVAIL;
    141     }
    142 
    143     i_min = soc_mem_index_min(unit, L2_USER_ENTRYm);
    144     i_max = soc_mem_index_max(unit, L2_USER_ENTRYm);
    145 
    146     for (i = i_min; i <= i_max; i++) {
    147         SOC_IF_ERROR_RETURN(
    148             READ_L2_USER_ENTRYm(unit, MEM_BLOCK_ANY, i, &l2u_entry));
    149         if (!soc_L2_USER_ENTRYm_field32_get(unit, &l2u_entry, VALIDf)) {
    150             continue;
    151         }
    152         if (_soc_l2u_overlap_get(unit, &l2u_entry, entry)) {
    153             *index = i;
    154             return SOC_E_EXISTS;
    155         }
    156     }
    157     return SOC_E_NONE;
    158 }
    159 
    160 /*
    161  * Function:
    162  *      soc_l2u_search
    163  * Purpose:
    164  *      Search for entry in L2 User table
    165  * Parameters:
    166  *      unit - SOC unit number
    167  *	key - entry to look for
    168  *	result - matching entry (if found)
    169  *      index - (OUT) index where found
    170  * Returns:
    171  *      SOC_E_NONE      - matching entry found
    172  *      SOC_E_NOT_FOUND - no matching entries found
    173  */
    174 int 
    175 soc_l2u_search(int unit, l2u_entry_t *key, l2u_entry_t *result, int *index)
    176 {
    177     int rv = SOC_E_NONE;
    178     l2u_entry_t *entry;
    179     int i, i_max, i_min;
    180     soc_mem_t mem = L2_USER_ENTRYm;
    181     void *buf = NULL;
    182 
    183     buf = soc_cm_salloc(unit, SOC_MEM_TABLE_BYTES(unit, mem), "l2_user");
    184     if (buf == NULL) {
    185         return SOC_E_MEMORY;
    186     }
    187 
    188     i_min = soc_mem_index_min(unit, mem);
    189     i_max = soc_mem_index_max(unit, mem);
    190 
    191     rv = soc_mem_read_range(unit, mem, MEM_BLOCK_ANY, i_min, i_max, buf);
    192     if (SOC_FAILURE(rv)) {
    193         soc_cm_sfree(unit, buf);
    194         return rv;
    195     }
    196 
    197     for (i = i_min; i <= i_max; i++) {
    198         entry = soc_mem_table_idx_to_pointer(unit, mem, l2u_entry_t *, buf, i);
    199         if (!soc_mem_field32_get(unit, mem, entry, VALIDf)) {
    200             continue;
    201         }
    202         if (_soc_mem_cmp_l2u(unit, entry, key) == 0) {
    203             *index = i;
    204             sal_memcpy(result, entry, sizeof(l2u_entry_t));
    205             soc_cm_sfree(unit, buf);
    206             return SOC_E_NONE;
    207         }
    208     }
    209     soc_cm_sfree(unit, buf);
    210     return SOC_E_NOT_FOUND;
    211 }
    212 
    213 /*
    214  * Function:
    215  *      soc_l2u_find_unused
    216  * Purpose:
    217  *      Search for unused entry in L2 User table
    218  * Parameters:
    219  *      unit - SOC unit number
    220  *	key - entry to be stored in the unused entry
    221  *      index - (OUT) index where found
    222  * Returns:
    223  *      SOC_E_NONE      - free entry found
    224  *      SOC_E_FULL      - no free entries found
    225  */
    226 int 
    227 soc_l2u_find_free_entry(int unit, l2u_entry_t *key, int *free_index)
    228 {
    229     l2u_entry_t entry, free_mask;
    230     int index, i, entry_words, rv;
    231     int start, end, step;
    232     uint32 mask[SOC_MAX_MEM_WORDS / 2];
    233 
    234     entry_words = soc_mem_entry_words(unit, L2_USER_ENTRYm);
    235 
    236     sal_memset(&free_mask, 0, sizeof(free_mask));
    237     soc_L2_USER_ENTRYm_field32_set(unit, &free_mask, VALIDf, 1);
    238 
    239     soc_L2_USER_ENTRYm_field_get(unit, key, MASKf, mask);
    240     if (mask[0] == 0xffffffff && (mask[1] & 0xffff) == 0xffff) {
    241         /* Search from high priority end */
    242         start = soc_mem_index_min(unit, L2_USER_ENTRYm);
    243         end = soc_mem_index_max(unit, L2_USER_ENTRYm) + 1;
    244         step = 1;
    245     } else {
    246         start = soc_mem_index_max(unit, L2_USER_ENTRYm);
    247         end = soc_mem_index_min(unit, L2_USER_ENTRYm) - 1;
    248         step = -1;
    249     }
    250     for (index = start; index != end; index += step) {
    251         rv = READ_L2_USER_ENTRYm(unit, MEM_BLOCK_ANY, index, &entry);
    252         if (SOC_SUCCESS(rv)) {
    253             for (i = 0; i < entry_words; i++) {
    254                 if (entry.entry_data[i] & free_mask.entry_data[i]) {
    255                     break;
    256                 }
    257             }
    258             if (i == entry_words) {
    259                 *free_index = index;
    260                 return SOC_E_NONE;
    261             }
    262         }
    263     }
    264 
    265     return SOC_E_FULL;
    266 }
    267 
    268 /*
    269  * Function:
    270  *      soc_l2u_insert
    271  * Purpose:
    272  *      Add entry to L2 User table
    273  * Parameters:
    274  *      unit - SOC unit number
    275  *	entry - pointer to l2u_entry_t
    276  *	index - where to insert or -1 for default insertion policy
    277  *	index_used - (OUT) entry used if index = -1
    278  * Returns:
    279  *      SOC_E_NONE - successfully added entry
    280  *      SOC_E_FULL - table is full
    281  *      SOC_E_FAIL - overlapping entry already exists
    282  * Notes:
    283  *      If index -1 is specified, an entry with no zeros in the MAC
    284  *      address mask will be inserted at the first unused slot found
    285  *      when searching from the high priority end of the table. Any
    286  *      other entry will be inserted at the first unused entry found
    287  *      when searching from the low priority end of the table.
    288  */
    289 int 
    290 soc_l2u_insert(int unit, l2u_entry_t *entry, int index, int *index_used)
    291 {
    292     l2u_entry_t l2u_entry;
    293     int i, i_max, i_min, rv;
    294 
    295     i_min = soc_mem_index_min(unit, L2_USER_ENTRYm);
    296     i_max = soc_mem_index_max(unit, L2_USER_ENTRYm);
    297 
    298     if (index == -1) {
    299 
    300         soc_mem_lock(unit, L2_USER_ENTRYm);
    301 
    302         /* Avoid duplicates */
    303         rv = soc_l2u_search(unit, entry, &l2u_entry, &i);
    304         if (rv != SOC_E_NOT_FOUND) {
    305             soc_mem_unlock(unit, L2_USER_ENTRYm);
    306             *index_used = i;
    307             return rv;
    308         }
    309 
    310         rv = soc_l2u_find_free_entry(unit, entry, &i);
    311         soc_mem_unlock(unit, L2_USER_ENTRYm);
    312         if (SOC_FAILURE(rv)) {
    313             return rv;
    314         }
    315         index = i;
    316 
    317     } else if (index < i_min || index > i_max) {
    318         return SOC_E_PARAM;
    319     }
    320 
    321     soc_mem_lock(unit, L2_USER_ENTRYm);
    322 
    323     sal_memcpy(&l2u_entry, entry, sizeof(l2u_entry));
    324     rv = WRITE_L2_USER_ENTRYm(unit, MEM_BLOCK_ALL, index, &l2u_entry);
    325 
    326     soc_mem_unlock(unit, L2_USER_ENTRYm);
    327 
    328     *index_used = index;
    329 
    330     return rv;
    331 }
    332 
    333 /*
    334  * Function:
    335  *      soc_l2u_get
    336  * Purpose:
    337  *      Get entry from L2 User table
    338  * Parameters:
    339  *      unit - SOC unit number
    340  *	entry - entry to delete, or NULL if index is used
    341  *	index - entry to get
    342  * Returns:
    343  *      SOC_E_XXX
    344  */
    345 int 
    346 soc_l2u_get(int unit, l2u_entry_t *entry, int index)
    347 {
    348     int i_max, i_min, rv;
    349 
    350     if (entry == NULL) {
    351         return SOC_E_PARAM;
    352     }
    353 
    354     i_min = soc_mem_index_min(unit, L2_USER_ENTRYm);
    355     i_max = soc_mem_index_max(unit, L2_USER_ENTRYm);
    356 
    357     if (index < i_min || index > i_max) {
    358         return SOC_E_PARAM;
    359     }
    360 
    361     soc_mem_lock(unit, L2_USER_ENTRYm);
    362 
    363     rv = READ_L2_USER_ENTRYm(unit, MEM_BLOCK_ANY, index, entry);
    364 
    365     soc_mem_unlock(unit, L2_USER_ENTRYm);
    366 
    367     return rv;
    368 }
    369 
    370 /*
    371  * Function:
    372  *      soc_l2u_delete
    373  * Purpose:
    374  *      Delete entry from L2 User table
    375  * Parameters:
    376  *      unit - SOC unit number
    377  *	entry - entry to delete, or NULL if index is used
    378  *	index - entry to delete, must be -1 if non-zero key
    379  *	index_deleted - (OUT) entry deleted if index = -1
    380  * Returns:
    381  *      SOC_E_XXX
    382  */
    383 int 
    384 soc_l2u_delete(int unit, l2u_entry_t *entry, int index, int *index_deleted)
    385 {
    386     l2u_entry_t l2u_entry;
    387     int i, i_max, i_min, rv;
    388 
    389     /* We need either an entry or a valid index */
    390     if (entry == NULL && index == -1) {
    391         return SOC_E_PARAM;
    392     }
    393 
    394     i_min = soc_mem_index_min(unit, L2_USER_ENTRYm);
    395     i_max = soc_mem_index_max(unit, L2_USER_ENTRYm);
    396 
    397     soc_mem_lock(unit, L2_USER_ENTRYm);
    398 
    399     if (index == -1) {
    400         if (soc_l2u_search(unit, entry, &l2u_entry, &i) < 0) {
    401             /* Not in table */
    402             soc_mem_unlock(unit, L2_USER_ENTRYm);
    403             return SOC_E_NOT_FOUND;
    404         }
    405         index = i;
    406 
    407     } else if (index < i_min || index > i_max) {
    408         soc_mem_unlock(unit, L2_USER_ENTRYm);
    409         return SOC_E_PARAM;
    410     }
    411 
    412     sal_memset(&l2u_entry, 0, sizeof(l2u_entry));
    413     rv = WRITE_L2_USER_ENTRYm(unit, MEM_BLOCK_ALL, index, &l2u_entry);
    414 
    415     soc_mem_unlock(unit, L2_USER_ENTRYm);
    416 
    417     *index_deleted = index;
    418 
    419     return rv;
    420 }
    421 
    422 /*
    423  * Function:
    424  *	soc_l2u_delete_by_key
    425  * Description:
    426  *	Delete L2 User entries by search key.
    427  * Parameters:
    428  *	unit  - device unit
    429  *      key   - L2 User table search key
    430  * Returns:
    431  *	BCM_E_XXX
    432  */
    433 
    434 int
    435 soc_l2u_delete_by_key(int unit, l2u_key_t *key)
    436 {
    437     l2u_entry_t l2u;
    438     sal_mac_addr_t addr;
    439     int i, i_min, i_max, rv;
    440     int value, skip_l2u;
    441 
    442     skip_l2u = soc_property_get(unit, spn_SKIP_L2_USER_ENTRY, 0);
    443     if (skip_l2u) {
    444         return SOC_E_UNAVAIL;
    445     }
    446 
    447     i_min = soc_mem_index_min(unit, L2_USER_ENTRYm);
    448     i_max = soc_mem_index_max(unit, L2_USER_ENTRYm);
    449 
    450     soc_mem_lock(unit, L2_USER_ENTRYm);
    451 
    452     for (i = i_min; i <= i_max; i++) {
    453         rv = READ_L2_USER_ENTRYm(unit, MEM_BLOCK_ANY, i, &l2u);
    454         if (SOC_FAILURE(rv)) {
    455             soc_mem_unlock(unit, L2_USER_ENTRYm);
    456             return rv;
    457         }
    458         if (!_l2u_field32_get(unit, &l2u, VALIDf)) {
    459             continue;
    460         }
    461         if (key->flags & L2U_KEY_MAC) {
    462             _l2u_mac_addr_get(unit, &l2u, MAC_ADDRf, addr);
    463             if (ENET_CMP_MACADDR(key->mac, addr) != 0) {
    464                 continue;
    465             }
    466         }
    467         
    468         value = _l2u_field32_get(unit, &l2u, VLAN_IDf);
    469         if ((key->flags & L2U_KEY_VLAN) && value != key->vlan) {
    470             continue;
    471         }
    472 
    473         value = _l2u_field32_get(unit, &l2u, PORT_TGIDf);
    474         if ((key->flags & L2U_KEY_PORT) && value  != key->port) {
    475             continue;
    476         }
    477 
    478         value = _l2u_field32_get(unit, &l2u, MODULE_IDf);
    479         if ((key->flags & L2U_KEY_MODID) && value != key->modid) {
    480             continue;
    481         }
    482         sal_memset(&l2u, 0, sizeof(l2u));
    483         rv = WRITE_L2_USER_ENTRYm(unit, MEM_BLOCK_ALL, i, &l2u);
    484         if (SOC_FAILURE(rv)) {
    485             soc_mem_unlock(unit, L2_USER_ENTRYm);
    486             return rv;
    487         }
    488     }
    489 
    490     soc_mem_unlock(unit, L2_USER_ENTRYm);
    491 
    492     return SOC_E_NONE;
    493 }
    494 
    495 /*
    496  * Function:
    497  *	soc_l2u_delete_all
    498  * Description:
    499  *	Delete all L2 User entries.
    500  * Parameters:
    501  *	unit  - device unit
    502  * Returns:
    503  *	BCM_E_XXX
    504  */
    505 
    506 int
    507 soc_l2u_delete_all(int unit)
    508 {
    509     l2u_entry_t l2u;
    510     int i, i_min, i_max, skip_l2u, rv = SOC_E_NONE;
    511 
    512     skip_l2u = soc_property_get(unit, spn_SKIP_L2_USER_ENTRY, 0);
    513     if (skip_l2u) {
    514         return SOC_E_UNAVAIL;
    515     }
    516 
    517     i_min = soc_mem_index_min(unit, L2_USER_ENTRYm);
    518     i_max = soc_mem_index_max(unit, L2_USER_ENTRYm);
    519 
    520     soc_mem_lock(unit, L2_USER_ENTRYm);
    521 
    522     sal_memset(&l2u, 0, sizeof(l2u));
    523     for (i = i_min; i <= i_max; i++) {
    524         rv = WRITE_L2_USER_ENTRYm(unit, MEM_BLOCK_ALL, i, &l2u);
    525         if (SOC_FAILURE(rv)) {
    526             break;
    527         }
    528     }
    529 
    530     soc_mem_unlock(unit, L2_USER_ENTRYm);
    531 
    532     return rv;
    533 }
    534 
    535 #endif /* BCM_XGS3_SWITCH_SUPPORT */