cpu2cpu.c (30231B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: cpu2cpu.c 8 * Purpose: Provides CPU to CPU packet transmit 9 * Requires: CPU keys for destination; cputrans for pkt alloc 10 * Notes: 11 * Applications must directly register with some RX to receive packets. 12 * They may then use c2c_pkt_recognize to determine if a packet 13 * is a cpu-to-cpu packet. 14 * 15 * Since RX is not a service of CPU-to-CPU, loopback is not 16 * supported at this level. 17 * 18 * CPU indexing is managed by a local cpudb. That module must be properly 19 * initialized before using cpu2cpu transport including calls to 20 * add CPU keys and other info to the local CPU. 21 * 22 * This module uses the tx packet allocation routines implemented in 23 * cputrans. 24 */ 25 26 #include <shared/bsl.h> 27 28 #include <assert.h> 29 30 #include <shared/idents.h> 31 32 #include <sal/core/sync.h> 33 #include <sal/core/libc.h> 34 #include <shared/alloc.h> 35 36 #include <bcm/types.h> 37 #include <bcm/pkt.h> 38 #include <bcm/tx.h> 39 #include <bcm/rx.h> 40 #include <bcm/error.h> 41 42 #include <appl/cpudb/cpudb.h> 43 44 #include <appl/cputrans/cpu2cpu.h> 45 #include <appl/cputrans/cputrans.h> 46 47 #include "t_util.h" 48 49 /**************************************************************** 50 * 51 * Configurable parameters 52 */ 53 static bcm_trans_ptr_t *c2c_trans_ptr = C2C_TRANS_PTR_DEFAULT; 54 static uint16 c2c_snap_type = C2C_SNAP_TYPE_DEFAULT; 55 static uint16 c2c_trans_type = C2C_TRANS_TYPE_DEFAULT; 56 static bcm_mac_t c2c_snap_mac = C2C_SNAP_MAC_DEFAULT; 57 static uint32 c2c_flags; 58 static int c2c_src_modid = C2C_SRC_MODID_DEFAULT; 59 60 #define C2C_LOCAL_MAC (c2c_db_ref->local_entry->base.mac) 61 62 static sal_mutex_t c2c_mutex; 63 static int init_done; 64 65 static cpudb_ref_t c2c_db_ref = CPUDB_REF_NULL; 66 67 #define C2C_INIT if (!init_done) BCM_IF_ERROR_RETURN(c2c_init()) 68 69 #define C2C_LOCK sal_mutex_take(c2c_mutex, sal_sem_FOREVER) 70 #define C2C_UNLOCK sal_mutex_give(c2c_mutex) 71 72 #define PKT_TRANS_PTR(pkt) ((bcm_trans_ptr_t *)((pkt)->trans_ptr)) 73 74 typedef struct { 75 c2c_cb_f callback; 76 } c2c_cb_cookie_t; 77 78 STATIC void * 79 _c2c_callback_cookie_set(c2c_cb_f callback) 80 { 81 c2c_cb_cookie_t *cookie = sal_alloc(sizeof(*cookie), "c2c_cookie"); 82 83 if (cookie) { 84 cookie->callback = callback; 85 } 86 87 return (void *)cookie; 88 } 89 90 STATIC void 91 _c2c_callback_cookie_get(void *cookie, c2c_cb_f *cb) 92 { 93 if (cookie) { 94 c2c_cb_cookie_t *c2c_cookie = (c2c_cb_cookie_t *)cookie; 95 *cb = c2c_cookie->callback; 96 sal_free(c2c_cookie); 97 } else { 98 *cb = NULL; 99 } 100 } 101 102 int 103 c2c_src_mod_set(int src_mod) 104 { 105 c2c_src_modid = src_mod; 106 107 return BCM_E_NONE; 108 } 109 110 int 111 c2c_src_mod_get(int *src_mod) 112 { 113 *src_mod = c2c_src_modid; 114 115 return BCM_E_NONE; 116 } 117 118 static INLINE int 119 c2c_src_mod(void) 120 { 121 return (c2c_src_modid < 0) ? 122 c2c_db_ref->local_entry->dest_mod : c2c_src_modid; 123 } 124 125 STATIC int 126 c2c_init(void) 127 { 128 if (c2c_mutex == NULL) { 129 c2c_mutex = sal_mutex_create("C2C_mutex"); 130 if (c2c_mutex == NULL) { 131 return BCM_E_MEMORY; 132 } 133 } 134 135 if (c2c_db_ref == CPUDB_REF_NULL) { 136 c2c_db_ref = cpudb_create(); 137 if (c2c_db_ref == CPUDB_REF_NULL) { 138 return BCM_E_MEMORY; 139 } 140 } 141 142 init_done = TRUE; 143 144 return BCM_E_NONE; 145 } 146 147 /* Assumes lock held */ 148 STATIC int 149 _c2c_cpu_add(const cpudb_entry_t *db_ent) 150 { 151 cpudb_entry_t *ent; 152 153 LOG_INFO(BSL_LS_TKS_C2C, 154 (BSL_META("C2C: add key " CPUDB_KEY_FMT ", flags 0x%x (%d,%d,%d,%d)\n"), 155 CPUDB_KEY_DISP(db_ent->base.key), db_ent->flags, 156 db_ent->tx_unit,db_ent->tx_port, 157 db_ent->dest_mod,db_ent->dest_port)); 158 159 CPUDB_KEY_SEARCH(c2c_db_ref, db_ent->base.key, ent); 160 if (ent == NULL) { 161 ent = cpudb_entry_create(c2c_db_ref, db_ent->base.key, FALSE); 162 if (ent == NULL) { 163 return BCM_E_MEMORY; 164 } 165 } 166 167 sal_memcpy(&ent->base, &db_ent->base, sizeof(cpudb_base_t)); 168 ent->tx_unit = db_ent->tx_unit; 169 ent->tx_port = db_ent->tx_port; 170 ent->dest_mod = db_ent->dest_mod; 171 ent->dest_port = db_ent->dest_port; 172 ent->flags = db_ent->flags; 173 ent->trans_ptr = db_ent->trans_ptr; 174 175 if (ent->flags & CPUDB_F_IS_LOCAL) { 176 c2c_db_ref->local_entry = ent; 177 } 178 179 return BCM_E_NONE; 180 } 181 182 int 183 c2c_cpu_add(const cpudb_entry_t *db_ent) 184 { 185 int rv; 186 187 C2C_INIT; 188 LOG_INFO(BSL_LS_TKS_C2C, 189 (BSL_META("C2C added " CPUDB_KEY_FMT_EOLN), 190 CPUDB_KEY_DISP(db_ent->base.key))); 191 192 C2C_LOCK; 193 rv = _c2c_cpu_add(db_ent); 194 C2C_UNLOCK; 195 196 return rv; 197 } 198 199 int 200 c2c_cpu_remove(const cpudb_key_t key) 201 { 202 int rv; 203 204 C2C_INIT; 205 LOG_INFO(BSL_LS_TKS_C2C, 206 (BSL_META("C2C removed " CPUDB_KEY_FMT_EOLN), 207 CPUDB_KEY_DISP(key))); 208 209 C2C_LOCK; 210 rv = cpudb_entry_remove(c2c_db_ref, key); 211 C2C_UNLOCK; 212 213 return rv; 214 } 215 216 int 217 c2c_cpu_clear(void) 218 { 219 int rv; 220 221 C2C_INIT; 222 C2C_LOCK; 223 rv = cpudb_clear(c2c_db_ref, FALSE); 224 C2C_UNLOCK; 225 226 return rv; 227 } 228 229 /* Assumes local entry does not change */ 230 int 231 c2c_cpu_update(cpudb_ref_t db_ref) 232 { 233 cpudb_entry_t *entry, *new_ref_entry; 234 int rv = BCM_E_NONE; 235 static cpudb_key_t del_keys[CPUDB_CPU_MAX]; 236 int count, i; 237 238 LOG_INFO(BSL_LS_TKS_C2C, 239 (BSL_META("C2C CPU DB %s\n"), 240 (db_ref==NULL) ? "clear" : "update")); 241 C2C_INIT; 242 243 C2C_LOCK; 244 if (db_ref == CPUDB_REF_NULL) { /* Clear out DB completely */ 245 rv = cpudb_clear(c2c_db_ref, FALSE); 246 } else { 247 /* 248 * Remove entries from c2c db that are not in new DB; 249 * Can't remove entries while looping over entries, so accumulate 250 * in del_keys structure 251 */ 252 count = 0; 253 CPUDB_FOREACH_ENTRY(c2c_db_ref, entry) { 254 CPUDB_KEY_SEARCH(db_ref, entry->base.key, new_ref_entry); 255 if (new_ref_entry == NULL) { 256 if (entry == c2c_db_ref->local_entry) { 257 LOG_INFO(BSL_LS_TKS_C2C, 258 (BSL_META("C2C: Update error, can't remove local\n"))); 259 continue; 260 } 261 CPUDB_KEY_COPY(del_keys[count], entry->base.key); 262 if (++count >= CPUDB_CPU_MAX) { 263 break; 264 } 265 } 266 } 267 for (i = 0; i < count; i++) { 268 rv = cpudb_entry_remove(c2c_db_ref, del_keys[i]); 269 if (rv < 0) { 270 LOG_INFO(BSL_LS_TKS_C2C, 271 (BSL_META("C2C: Failed to remove entry on update\n"))); 272 break; 273 } 274 } 275 276 /* Add entries that are in new DB */ 277 CPUDB_FOREACH_ENTRY(db_ref, entry) { 278 rv = _c2c_cpu_add(entry); 279 if (rv < 0) { 280 LOG_INFO(BSL_LS_TKS_C2C, 281 (BSL_META("C2C: Failed to remove entry on update\n"))); 282 break; 283 } 284 } 285 } 286 C2C_UNLOCK; 287 288 return rv; 289 } 290 291 292 /**************************************************************** 293 * 294 * Forward declarations 295 */ 296 297 STATIC void c2c_send_callback(int unit, bcm_pkt_t *pkt, void *cookie); 298 STATIC void c2c_send_callback_nofree(int unit, bcm_pkt_t *pkt, void *cookie); 299 300 STATIC int good_c2c_pkt(uint8 *pkt_data, 301 cpudb_key_t *src_key, 302 uint16 *mplx_num); 303 STATIC void c2c_l2_hdr_pack(const bcm_mac_t dest_mac, 304 const bcm_mac_t src_mac, 305 uint8 *pkt_buf, 306 uint16 mplx_num, 307 uint16 vlan); 308 309 310 /**************************************************************** 311 * 312 * Configuration functions 313 */ 314 315 /* 316 * Function: 317 * c2c_config_set 318 * Purpose: 319 * Set the configurable parameters for CPU to CPU config 320 * Parameters: 321 * trans_ptr - Tranport pointer structure 322 * local_mac - Local MAC address IGNORED 323 * Returns: 324 * BCM_E_XXX 325 */ 326 327 int 328 c2c_config_set( 329 bcm_trans_ptr_t *trans_ptr, 330 bcm_mac_t local_mac, 331 uint32 flags) 332 { 333 if (trans_ptr->tp_tx == NULL || 334 trans_ptr->tp_setup_tx == NULL) { 335 return BCM_E_PARAM; 336 } 337 338 c2c_trans_ptr = trans_ptr; 339 340 if (local_mac != NULL) { 341 LOG_INFO(BSL_LS_TKS_C2C, 342 (BSL_META("c2c local mac set is deprecated.\n"))); 343 } 344 345 c2c_flags = flags; 346 347 return BCM_E_NONE; 348 } 349 350 351 /* 352 * Function: 353 * c2c_config_get 354 * Purpose: 355 * Get the configurable parameters for CPU to CPU config 356 * Parameters: 357 * trans_ptr - (OUT) Pointer to tranport pointer structure 358 * local_mac - (OUT) Local MAC address 359 * Returns: 360 * BCM_E_XXX 361 * Notes: 362 */ 363 364 int 365 c2c_config_get( 366 bcm_trans_ptr_t **trans_ptr, 367 bcm_mac_t local_mac, 368 uint32 *flags) 369 { 370 C2C_LOCK; 371 *trans_ptr = c2c_trans_ptr; 372 if (c2c_db_ref != NULL && c2c_db_ref->local_entry != NULL) { 373 sal_memcpy(local_mac, C2C_LOCAL_MAC, sizeof(bcm_mac_t)); 374 } else { 375 sal_memset(local_mac, 0, sizeof(bcm_mac_t)); 376 } 377 *flags = c2c_flags; 378 C2C_UNLOCK; 379 380 return BCM_E_NONE; 381 } 382 383 384 /* 385 * Function: 386 * c2c_snap_set 387 * Purpose: 388 * Set the SNAP mac and type ID 389 * Parameters: 390 * new_mac - MAC to use 391 * new_type - Type ID to use; host order 392 * Returns: 393 * BCM_E_XXX 394 * Notes: 395 * It defaults to Broadcom, so generally not required 396 */ 397 398 int 399 c2c_snap_set(bcm_mac_t new_mac, uint16 new_type, uint16 new_trans_type) 400 { 401 sal_memcpy(c2c_snap_mac, new_mac, sizeof(bcm_mac_t)); 402 c2c_snap_type = new_type; 403 c2c_trans_type = new_trans_type; 404 405 return BCM_E_NONE; 406 } 407 408 409 /* 410 * Function: 411 * c2c_snap_get 412 * Purpose: 413 * Get the SNAP mac and type ID 414 * Parameters: 415 * snap_mac - (OUT) MAC to use 416 * snap_type - (OUT) Type ID to use; host order 417 * Returns: 418 * BCM_E_XXX 419 * Notes: 420 * It defaults to Broadcom, so generally not required 421 */ 422 423 int 424 c2c_snap_get(bcm_mac_t snap_mac, uint16 *snap_type, uint16 *trans_type) 425 { 426 sal_memcpy(snap_mac, c2c_snap_mac, sizeof(bcm_mac_t)); 427 *snap_type = c2c_snap_type; 428 *trans_type = c2c_trans_type; 429 430 return BCM_E_NONE; 431 } 432 433 434 435 /**************************************************************** 436 * 437 * CPU to CPU transmit routines 438 * 439 * There are two choices here: 440 * c2c_tx: Allocate a bcm packet, send it, deallocate it 441 * Or: 442 * c2c_pkt_create: Create a c2c pkt that can be sent multiple times 443 * c2c_pkt_update: Update pkt info according to current cpudb 444 * c2c_pkt_send: Send a pkt created with c2c_pkt_create 445 * c2c_pkt_destroy: Deallocate a c2c packet. 446 */ 447 448 /* 449 * Function: 450 * c2c_pkt_create 451 * Purpose: 452 * Returns a bcm_pkt_t that can be sent by c2c 453 * Parameters: 454 * See c2c_tx below 455 * Returns: 456 * BCM_E_XXX 457 * Notes: 458 * The buffer should include C2C_DATA_OFFSET bytes for the 459 * packet headers. 460 * 461 * This routine is meant to create a packet which is repeatedly 462 * sent without being changed. It is not meant to allocate 463 * a packet which is updated and sent out for different 464 * reasons or to different CPUs. 465 * 466 * The 'cos' parameter contains the cos and internal priority 467 * values encoded. Use CPUTRANS_COS_SET() CPUTRANS_INT_PRIO_SET() 468 * to set values accordingly. The internal priority is optional; 469 * if this is not provided, the cos value is used for internal priority. 470 */ 471 472 bcm_pkt_t * 473 c2c_pkt_create( 474 cpudb_key_t dest_key, 475 uint8 *pkt_buf, 476 int len, 477 int cos, 478 int vlan, 479 int seg_len, 480 uint16 mplx_num, 481 uint32 ct_flags, 482 int *tot_segs, 483 int *rvp) 484 { 485 bcm_pkt_t *pkt, *cur_pkt; 486 const cpudb_entry_t *db_ent; 487 uint8 *pkt_data; 488 int src_mod, src_port, dest_mod, dest_port; 489 int tx_unit, tx_port; 490 cpudb_key_t local_key; 491 bcm_mac_t dest_mac, src_mac; 492 int pkt_cos, pkt_int_prio; 493 int rv; 494 495 if (!init_done) { 496 if ((rv = c2c_init()) != BCM_E_NONE) { 497 LOG_INFO(BSL_LS_TKS_C2C, 498 (BSL_META("c2c pc. init failed\n"))); 499 *rvp = rv; 500 return NULL; 501 } 502 } 503 504 C2C_LOCK; 505 CPUDB_KEY_SEARCH(c2c_db_ref, dest_key, db_ent); 506 if (db_ent == NULL || c2c_db_ref->local_entry == NULL) { 507 LOG_INFO(BSL_LS_TKS_C2C, 508 (BSL_META("c2c: Could not find %s%s%s for " 509 CPUDB_KEY_FMT_EOLN), 510 db_ent == NULL ? "db entry" : "", 511 db_ent || c2c_db_ref->local_entry ? "" : ",", 512 c2c_db_ref->local_entry == NULL ? "local entry" : "", 513 CPUDB_KEY_DISP(dest_key))); 514 *rvp = (db_ent == NULL) ? BCM_E_NOT_FOUND : BCM_E_EMPTY; 515 C2C_UNLOCK; 516 return NULL; 517 } 518 #if defined(BROADCOM_DEBUG) 519 if (!(db_ent->flags & CPUDB_F_TX_KNOWN)) { 520 C2C_UNLOCK; 521 LOG_INFO(BSL_LS_TKS_C2C, 522 (BSL_META("c2c: Unknown transmit port (flags 0x%x) for dest key " 523 CPUDB_KEY_FMT_EOLN), 524 db_ent->flags, 525 CPUDB_KEY_DISP(dest_key))); 526 *rvp = BCM_E_INTERNAL; 527 return NULL; 528 } 529 #endif /* BROADCOM_DEBUG */ 530 531 /* Get needed info from db entries */ 532 src_mod = c2c_src_mod(); 533 src_port = c2c_db_ref->local_entry->dest_port; 534 CPUDB_KEY_COPY(local_key, c2c_db_ref->local_entry->base.key); 535 dest_mod = db_ent->dest_mod; 536 dest_port = db_ent->dest_port; 537 tx_unit = db_ent->tx_unit; 538 tx_port = db_ent->tx_port; 539 sal_memcpy(&dest_mac, db_ent->base.mac, sizeof(bcm_mac_t)); 540 sal_memcpy(&src_mac, c2c_db_ref->local_entry->base.mac, sizeof(bcm_mac_t)); 541 C2C_UNLOCK; 542 543 pkt = cputrans_tx_pkt_list_alloc(pkt_buf, len, seg_len, ct_flags, 544 tot_segs); 545 if (pkt == NULL) { 546 LOG_INFO(BSL_LS_TKS_C2C, 547 (BSL_META("c2c: Could not alloc cputrans pkt\n"))); 548 *rvp = BCM_E_MEMORY; 549 return NULL; 550 } 551 #if defined(BROADCOM_DEBUG) 552 assert(pkt->next != pkt); 553 #endif /* BROADCOM_DEBUG */ 554 555 #if defined(BCM_C2C_TRANSPORT_SWITCHING) 556 /* Record the current entry in packet meta-data */ 557 pkt->trans_ptr = (void *)(db_ent->trans_ptr); 558 #endif 559 560 /* If internal priority is not provided, set value to cos */ 561 pkt_cos = CPUTRANS_COS_GET(cos); 562 if (cos & CPUTRANS_INT_PRIO_VALID) { 563 pkt_int_prio = CPUTRANS_INT_PRIO_GET(cos); 564 } else { 565 pkt_int_prio = pkt_cos; 566 } 567 568 /* Set up packet data in each segment */ 569 for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) { 570 571 cur_pkt->unit = tx_unit; 572 BCM_PBMP_PORT_SET(cur_pkt->tx_pbmp, tx_port); 573 cur_pkt->dest_mod = dest_mod; 574 cur_pkt->dest_port = dest_port; 575 576 577 cur_pkt->flags |= (BCM_TX_SRC_MOD|BCM_TX_SRC_PORT); 578 cur_pkt->src_mod = src_mod; 579 cur_pkt->src_port = src_port; 580 cur_pkt->opcode = BCM_HG_OPCODE_UC; 581 cur_pkt->cos = pkt_cos; 582 cur_pkt->prio_int = pkt_int_prio; 583 cur_pkt->flags |= BCM_TX_PRIO_INT; 584 585 /* L2 header is always in block 0 */ 586 pkt_data = cur_pkt->pkt_data[0].data; 587 c2c_l2_hdr_pack(dest_mac, src_mac, pkt_data, 588 mplx_num, BCM_VLAN_CTRL(cos, 0, vlan)); 589 CPUDB_KEY_PACK(&pkt_data[CPUTRANS_SRC_KEY_OFS], local_key); 590 } 591 592 LOG_DEBUG(BSL_LS_TKS_C2C, 593 (BSL_META("c2c: pkt from (%d.%d) to (%d.%d) " 594 "op %d cos %d int_prio %d\n"), 595 pkt->src_mod, pkt->src_port, 596 pkt->dest_mod, pkt->dest_port, 597 pkt->opcode, pkt_cos, pkt_int_prio)); 598 599 *rvp = BCM_E_NONE; 600 return pkt; 601 } 602 603 /* 604 * Function: 605 * c2c_pkt_update 606 * Purpose: 607 * Update the routing information in a packet created by c2c_pkt_create 608 * Parameters: 609 * pkt - Returned by c2c_pkt_create 610 * Returns: 611 * BCM_E_XXX 612 * Notes: 613 * If topology info in the system changes, this can be used 614 * to update packets to reflect that. 615 */ 616 617 int 618 c2c_pkt_update( 619 bcm_pkt_t *pkt, 620 cpudb_key_t dest_key) 621 { 622 bcm_pkt_t *cur_pkt; 623 const cpudb_entry_t *db_ent; 624 int src_mod, src_port, dest_mod, dest_port; 625 int tx_unit, tx_port; 626 627 if (!init_done) { 628 if (c2c_init() != BCM_E_NONE) { 629 LOG_INFO(BSL_LS_TKS_C2C, 630 (BSL_META("c2c update. init failed\n"))); 631 return BCM_E_INIT; 632 } 633 } 634 635 C2C_LOCK; 636 CPUDB_KEY_SEARCH(c2c_db_ref, dest_key, db_ent); 637 #if defined(BROADCOM_DEBUG) 638 if (db_ent == NULL || c2c_db_ref->local_entry == NULL) { 639 C2C_UNLOCK; 640 LOG_INFO(BSL_LS_TKS_C2C, 641 (BSL_META("c2c update: key %sfound, local_entry %sNULL, dest key " 642 CPUDB_KEY_FMT_EOLN), 643 (db_ent == NULL) ? "not " : "", 644 (c2c_db_ref->local_entry) == NULL ? "" : "not ", 645 CPUDB_KEY_DISP(dest_key))); 646 return BCM_E_NOT_FOUND; 647 } 648 if (!(db_ent->flags & CPUDB_F_TX_KNOWN)) { 649 C2C_UNLOCK; 650 LOG_INFO(BSL_LS_TKS_C2C, 651 (BSL_META("c2c update: Unknown tx port (flags 0x%x) for dest key " 652 CPUDB_KEY_FMT_EOLN), 653 db_ent->flags, 654 CPUDB_KEY_DISP(dest_key))); 655 return BCM_E_NOT_FOUND; 656 } 657 #else 658 if (db_ent == NULL) { 659 C2C_UNLOCK; 660 return BCM_E_NOT_FOUND; 661 } 662 #endif /* BROADCOM_DEBUG */ 663 664 /* Get needed info from db entries */ 665 src_mod = c2c_src_mod(); 666 src_port = c2c_db_ref->local_entry->dest_port; 667 dest_mod = db_ent->dest_mod; 668 dest_port = db_ent->dest_port; 669 tx_unit = db_ent->tx_unit; 670 tx_port = db_ent->tx_port; 671 C2C_UNLOCK; 672 673 /* Set up topology packet data in each segment */ 674 for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) { 675 cur_pkt->unit = tx_unit; 676 BCM_PBMP_PORT_SET(cur_pkt->tx_pbmp, tx_port); 677 cur_pkt->dest_mod = dest_mod; 678 cur_pkt->dest_port = dest_port; 679 680 681 cur_pkt->flags |= (BCM_TX_SRC_MOD|BCM_TX_SRC_PORT); 682 cur_pkt->src_mod = src_mod; 683 cur_pkt->src_port = src_port; 684 /* Assume mac/key for local box haven't changed */ 685 } 686 687 return BCM_E_NONE; 688 } 689 690 #if defined(BCM_C2C_TRANSPORT_SWITCHING) 691 #define SETUP_TX_RESOLVE(_pkt, _setup, _tx_list) do { \ 692 bcm_trans_ptr_t *_tp; \ 693 _setup = c2c_trans_ptr->tp_setup_tx; \ 694 _tx_list = c2c_trans_ptr->tp_tx_list; \ 695 _tp = PKT_TRANS_PTR(_pkt); \ 696 if (_tp != NULL) { \ 697 if (_tp->tp_setup_tx != NULL) { \ 698 _setup = _tp->tp_setup_tx; \ 699 } \ 700 if (_tp->tp_tx_list != NULL) { \ 701 _tx_list = _tp->tp_tx_list; \ 702 } \ 703 } \ 704 } while (0) 705 #else 706 #define SETUP_TX_RESOLVE(_pkt, _setup, _tx_list) do { \ 707 _setup = c2c_trans_ptr->tp_setup_tx; \ 708 _tx_list = c2c_trans_ptr->tp_tx_list; \ 709 } while (0) 710 #endif /* Transport switching */ 711 712 /* 713 * Function: 714 * c2c_pkt_send 715 * Purpose: 716 * Send a packet prepared by c2c_pkt_create. Will not 717 * deallocate on completion. 718 * Parameters: 719 * pkt - The packet or list of packets to send 720 * callback - If non-NULL, send async and make callback 721 * cookie - Passed on callback 722 * Returns: 723 * BCM_E_XXX 724 * Notes: 725 * This function may be called multiple times on the same 726 * packet(s). The header of the packet should not be altered. 727 * If callback is non-NULL, it will be made after the last packet 728 * is sent. 729 */ 730 731 int 732 c2c_pkt_send( 733 bcm_pkt_t *pkt, 734 c2c_cb_f callback, 735 void *cookie) 736 { 737 int rv; 738 bcm_pkt_cb_f local_callback = NULL; 739 bcm_tx_pkt_setup_f setup_fn; 740 bcm_tx_list_f tx_list_fn; 741 c2c_cb_cookie_t *c2c_cookie = NULL; 742 743 C2C_INIT; 744 745 SETUP_TX_RESOLVE(pkt, setup_fn, tx_list_fn); 746 747 if (setup_fn != NULL) { 748 rv = setup_fn(pkt->unit, pkt); /* Set up the packet */ 749 if (rv < 0) { /* Problem packet configuration */ 750 LOG_INFO(BSL_LS_TKS_C2C, 751 (BSL_META("c2c tx: Error setting up packet\n"))); 752 return rv; 753 } 754 } 755 756 if (callback != NULL) { /* Async send; but don't free after call */ 757 local_callback = c2c_send_callback_nofree; 758 pkt->_last_pkt->cookie = cookie; 759 c2c_cookie = _c2c_callback_cookie_set(callback); 760 if (c2c_cookie == NULL) { 761 return BCM_E_MEMORY; 762 } 763 } 764 765 assert(tx_list_fn != NULL); 766 rv = tx_list_fn(pkt->unit, pkt, local_callback, c2c_cookie); 767 return rv; 768 } 769 770 771 /* 772 * Function: 773 * c2c_pkt_destroy 774 * Purpose: 775 * Deallocate a packet created with c2c_pkt_create 776 * Parameters: 777 * pkt - The packet to deallocate 778 * Returns: 779 * BCM_E_XXX 780 * Notes: 781 */ 782 783 void 784 c2c_pkt_destroy(bcm_pkt_t *pkt) 785 { 786 assert(pkt->next != pkt); 787 cputrans_tx_pkt_list_free(pkt); 788 } 789 790 /* 791 * Function: 792 * c2c_tx 793 * Purpose: 794 * Transmit a (single) packet to a CPU given by index. 795 * Parameters: 796 * dest_key - To what CPU is this being sent 797 * pkt_buf - DMA-able buffer to be sent 798 * len - Total bytes in packet 799 * vlan - VLAN id (12 bits) 800 * cos - COS and internal priority to use 801 * seg_len - Bytes per data segment 802 * mplx_num - (TCP-like) multiplexing number for packet 803 * ct_flags - Indicate if packet contains space for header: 804 * callback - If non-null, send async and make callback 805 * cookie - Passed to callback when complete 806 * Returns: 807 * BCM_E_XXX 808 * Notes: 809 * The length field in the L2 header of the packet is not touched. 810 * Does not support segmentation/reassembly. 811 * 812 * The 'cos' parameter contains the cos and internal priority 813 * values encoded. Use CPUTRANS_COS_SET() CPUTRANS_INT_PRIO_SET() 814 * to set values accordingly. The internal priority is optional; 815 * if this is not provided, the cos value is used for internal priority. 816 */ 817 818 int 819 c2c_tx( 820 cpudb_key_t dest_key, 821 uint8 *pkt_buf, 822 int len, 823 int cos, 824 int vlan, 825 int seg_len, 826 uint16 mplx_num, 827 uint32 ct_flags, 828 c2c_cb_f callback, 829 void *cookie) 830 { 831 bcm_pkt_t *pkt, *cur_pkt; 832 int rv; 833 bcm_pkt_cb_f local_callback = NULL; 834 bcm_tx_pkt_setup_f setup_fn; 835 bcm_tx_list_f tx_list_fn; 836 c2c_cb_cookie_t *c2c_cookie = NULL; 837 838 C2C_INIT; 839 840 pkt = c2c_pkt_create(dest_key, pkt_buf, len, cos, vlan, 841 seg_len, mplx_num, ct_flags, NULL, &rv); 842 if (rv != BCM_E_NONE) { 843 LOG_INFO(BSL_LS_TKS_C2C, 844 (BSL_META("c2c send. can't create (%s)\n"), 845 bcm_errmsg(rv))); 846 return rv; 847 } 848 849 SETUP_TX_RESOLVE(pkt, setup_fn, tx_list_fn); 850 851 /* Set up the packet */ 852 for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) { 853 rv = setup_fn(pkt->unit, cur_pkt); 854 if (rv < 0) { /* Problem packet configuration */ 855 LOG_INFO(BSL_LS_TKS_C2C, 856 (BSL_META("c2c tx: Error setting up packet\n"))); 857 cputrans_tx_pkt_list_free(pkt); 858 return rv; 859 } 860 } 861 862 /* Callback should deallocate packet when called */ 863 if (callback != NULL) { /* Async send */ 864 local_callback = c2c_send_callback; 865 pkt->_last_pkt->cookie = cookie; 866 c2c_cookie = _c2c_callback_cookie_set(callback); 867 if (c2c_cookie == NULL) { 868 cputrans_tx_pkt_list_free(pkt); 869 return BCM_E_MEMORY; 870 } 871 } 872 873 rv = tx_list_fn(pkt->unit, pkt, local_callback, c2c_cookie); 874 875 /* Free packet if sending sync, or if an error occurs */ 876 if (callback == NULL || rv != BCM_E_NONE) { 877 cputrans_tx_pkt_list_free(pkt); 878 } 879 880 return rv; 881 } 882 883 884 /* 885 * Function: 886 * c2c_pkt_recognize 887 * Purpose: 888 * Check if the given packet is a CPU2CPU packet and 889 * get the index if it is. 890 * Parameters: 891 * pkt_data - The buffer to examine, from start of L2 hdr 892 * src_key - (OUT) Source KEY 893 * mplx_num - (OUT) Packet multiplexing value, if packet recognized 894 * Returns: 895 * BCM_E_NONE if it is a CPU 2 CPU packet; sets cpu_idx. 896 * BCM_E_NOT_FOUND if not a CPU 2 CPU packet. 897 * Notes: 898 * If the DB reference is non-NULL, then it is searched for the source 899 * MAC address of the packet as well. 900 * 901 * If src_key is non-NULL as well, it is filled in with a pointer 902 * to the found entry. 903 */ 904 905 int 906 c2c_pkt_recognize(uint8 *pkt_data, 907 cpudb_key_t *src_key, 908 uint16 *mplx_num) 909 { 910 int is_good; 911 912 if (!pkt_data) { 913 return 0; 914 } 915 916 C2C_INIT; 917 918 C2C_LOCK; 919 is_good = good_c2c_pkt(pkt_data, src_key, mplx_num); 920 C2C_UNLOCK; 921 922 LOG_DEBUG(BSL_LS_TKS_C2C, 923 (BSL_META("c2c: pkt is %srecognized. type, %d\n"), 924 is_good ? "" : "not ", mplx_num ? *mplx_num : -1)); 925 926 return is_good ? BCM_E_NONE : BCM_E_NOT_FOUND; 927 } 928 929 930 /* 931 * Boolean function that returns true if the packet is recognized 932 * as cpu-to-cpu transport packet. 933 * If the DB reference is non-NULL, then it is searched for the source 934 * MAC address of the packet as well. 935 */ 936 937 STATIC int 938 good_c2c_pkt(uint8 *pkt_data, 939 cpudb_key_t *src_key, 940 uint16 *mplx_num) 941 { 942 uint16 snap_type, trans_type; 943 944 /* Dest MAC is ours? */ 945 if (c2c_flags & C2C_F_DEST_MAC_CHECK_ENABLE) { 946 if (c2c_db_ref != NULL && c2c_db_ref->local_entry != NULL) { 947 if (sal_memcmp(pkt_data, C2C_LOCAL_MAC, sizeof(bcm_mac_t))) { 948 return 0; 949 } 950 } 951 } 952 953 954 955 /* SNAP pkt? */ 956 if (sal_memcmp(&pkt_data[CPUTRANS_SNAP_OFS], c2c_snap_mac, 957 sizeof(bcm_mac_t))) { 958 LOG_DEBUG(BSL_LS_TKS_C2C, 959 (BSL_META("c2c: Not SNAP mac\n"))); 960 return 0; 961 } 962 963 /* SNAP type correct? */ 964 UNPACK_SHORT(&pkt_data[CPUTRANS_SNAP_TYPE_OFS], snap_type); 965 if (snap_type != c2c_snap_type) { 966 LOG_DEBUG(BSL_LS_TKS_C2C, 967 (BSL_META("c2c: Wrong SNAP type 0x%04x\n"), 968 snap_type)); 969 return 0; 970 } 971 972 /* SNAP local type correct? */ 973 UNPACK_SHORT(&pkt_data[CPUTRANS_TR_TYPE_OFS], trans_type); 974 if (trans_type != c2c_trans_type) { 975 LOG_DEBUG(BSL_LS_TKS_C2C, 976 (BSL_META("c2c: Wrong trans type 0x%04x\n"), 977 trans_type)); 978 return 0; 979 } 980 981 /* It is a c2c packet; get application packet type and source key */ 982 if (src_key != NULL) { 983 CPUDB_KEY_UNPACK(&pkt_data[CPUTRANS_SRC_KEY_OFS], *src_key); 984 } 985 if (mplx_num != NULL) { 986 /* Get the local packet type from the next short in the pkt */ 987 UNPACK_SHORT(&pkt_data[CPUTRANS_TR_MULT_OFS], (*mplx_num)); 988 } 989 990 return 1; 991 } 992 993 STATIC void 994 _do_callback(bcm_pkt_t *pkt, void *cookie) 995 { 996 c2c_cb_f cb; 997 int pkt_data_blk; 998 /* Grab the callback from the packet */ 999 _c2c_callback_cookie_get(cookie, &cb); 1000 /* Check where packet data is; see cputrans for more info */ 1001 pkt_data_blk = 1; 1002 if (pkt->blk_count == 1) { 1003 pkt_data_blk = 0; 1004 } 1005 if (cb) { 1006 cb(pkt->pkt_data[pkt_data_blk].data, pkt->cookie); 1007 } 1008 } 1009 1010 /* Callback and deallocate packet after being sent. Does not touch 1011 * user data linked into the packet. 1012 */ 1013 STATIC void 1014 c2c_send_callback(int unit, bcm_pkt_t *pkt, void *cookie) 1015 { 1016 _do_callback(pkt, cookie); 1017 assert(pkt->next != pkt); 1018 cputrans_tx_pkt_list_free(pkt); 1019 } 1020 1021 1022 /* Callback and don't deallocate packet; appl must call pkt_destroy. */ 1023 STATIC void 1024 c2c_send_callback_nofree(int unit, bcm_pkt_t *pkt, void *cookie) 1025 { 1026 _do_callback(pkt, cookie); 1027 } 1028 1029 /* Set up the L2 header for the CPU to CPU packet; destination is in cm */ 1030 1031 STATIC void 1032 c2c_l2_hdr_pack(const bcm_mac_t dest_mac, 1033 const bcm_mac_t src_mac, 1034 uint8 *pkt_buf, 1035 uint16 mplx_num, 1036 uint16 vlan) 1037 { 1038 /* Set up the L2 header in the packet */ 1039 sal_memcpy(pkt_buf, dest_mac, sizeof(bcm_mac_t)); 1040 sal_memcpy(&pkt_buf[CPUTRANS_SMAC_OFS], src_mac, sizeof(bcm_mac_t)); 1041 PACK_SHORT(&pkt_buf[CPUTRANS_VTAG_OFS], 0x8100); 1042 PACK_SHORT(&pkt_buf[CPUTRANS_VTAG_OFS + sizeof(uint16)], vlan); 1043 /* Skip type/len field */ 1044 /* Following three "packs" could be combined */ 1045 sal_memcpy(&pkt_buf[CPUTRANS_SNAP_OFS], c2c_snap_mac, sizeof(bcm_mac_t)); 1046 PACK_SHORT(&pkt_buf[CPUTRANS_SNAP_TYPE_OFS], c2c_snap_type); 1047 PACK_SHORT(&pkt_buf[CPUTRANS_TR_TYPE_OFS], c2c_trans_type); 1048 PACK_SHORT(&pkt_buf[CPUTRANS_TR_MULT_OFS], mplx_num); 1049 } 1050 1051 #if defined(BROADCOM_DEBUG) 1052 void 1053 c2c_dump(void) 1054 { 1055 cpudb_entry_t *entry; 1056 1057 if (!init_done) { 1058 return; 1059 } 1060 1061 LOG_INFO(BSL_LS_TKS_C2C, 1062 (BSL_META("C2C Database\n"))); 1063 1064 C2C_LOCK; 1065 1066 if (c2c_db_ref == CPUDB_REF_NULL) { 1067 LOG_INFO(BSL_LS_TKS_C2C, 1068 (BSL_META(" Empty\n"))); 1069 } else { 1070 CPUDB_FOREACH_ENTRY(c2c_db_ref, entry) { 1071 LOG_INFO(BSL_LS_TKS_C2C, 1072 (BSL_META(" CPU Key " CPUDB_KEY_FMT ", flags 0x%x\n"), 1073 CPUDB_KEY_DISP(entry->base.key), entry->flags)); 1074 } 1075 } 1076 1077 C2C_UNLOCK; 1078 1079 return; 1080 } 1081 #endif /* BROADCOM_DEBUG */