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 */