next_hop.c (61054B)
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: next_hop.c 8 * 9 * Purpose: 10 * Implements next hop broadcast. See next_hop_trans.txt for 11 * more information. 12 * 13 * Requires: nh_tx, cputrans 14 * 15 * Notes: 16 * This module provides a broadcast mechanism allowing one CPU to send 17 * to all others in the system. It uses the next hop packet transport 18 * sending. It is not reliable. All CPUs in the system must have this 19 * running in order for packets to be properly forwarded. 20 * 21 * When a NEXT_HOP packet is received, the source CPU KEY and sequence 22 * number are examined. If the packet has been seen before, it is 23 * discarded. Otherwise, it is queued for local processing. If the 24 * packet is a broadcast or if the unicast address is unknown, then 25 * the packet is also forwarded out all local stack ports. 26 * 27 * If the source CPU is unknown, it is added to the local (ie, 28 * local to this file) database. If the DB is full, the variable 29 * key_lru_replace_enable indicates whether the least recently used 30 * CPU key in the data base should be replaced with the new 31 * key. 32 * 33 * Future Enhancement: Permit a callback to be registered that 34 * passes the old and new CPU keys when a replacement occurs. 35 * 36 * Stack ports may be marked as "duplex". Packets received on a duplex 37 * port will not be forwarded back out that port. It is not required 38 * that ports be marked as duplex: If a packet is sent back on a duplex 39 * port, the receiving CPU should discard the packet. 40 * 41 * Packets may be directed to a specific CPU by KEY. Currently, 42 * no attempt is made to expedite the routing; the packet still gets 43 * flooded to all CPUs; the difference is that only packets with 44 * the local KEY or broadcast KEY as destination are sent up the 45 * local stack (local callback is made). In the future, routing 46 * may be implemented so that the packet is only sent out a single 47 * stack port when possible. 48 * 49 * This module uses the tx packet allocation routines implemented in 50 * cputrans. 51 * 52 * Loop-back is not supported at this layer. 53 */ 54 55 56 57 #include <shared/bsl.h> 58 59 #include <assert.h> 60 61 #include <shared/idents.h> 62 63 #include <sal/core/sync.h> 64 #include <sal/core/libc.h> 65 #include <sal/core/thread.h> 66 #include <shared/alloc.h> 67 #include <sal/core/time.h> 68 69 #include <bcm/types.h> 70 #include <bcm/pkt.h> 71 #include <bcm/rx.h> 72 #include <bcm/error.h> 73 74 #include <appl/cputrans/nh_tx.h> 75 #include <appl/cputrans/next_hop.h> 76 #include <appl/cputrans/cputrans.h> 77 78 #include <appl/cpudb/cpudb.h> 79 80 #include "t_util.h" 81 82 #define STK_PORTS_MAX CPUDB_CXN_MAX 83 84 /**************************************************************** 85 * 86 * Internal Synchronization 87 */ 88 89 static sal_mutex_t next_hop_lock; 90 static sal_mutex_t next_hop_reg_lock; 91 static sal_mutex_t next_hop_unit_lock; 92 static sal_sem_t next_hop_sem; 93 94 #define NEXT_HOP_INIT if (next_hop_lock == NULL) \ 95 BCM_IF_ERROR_RETURN(_next_hop_init()) 96 97 #define NEXT_HOP_LOCK sal_mutex_take(next_hop_lock, sal_mutex_FOREVER) 98 #define NEXT_HOP_UNLOCK sal_mutex_give(next_hop_lock) 99 100 #define NEXT_HOP_REG_LOCK \ 101 sal_mutex_take(next_hop_reg_lock, sal_mutex_FOREVER) 102 #define NEXT_HOP_REG_UNLOCK sal_mutex_give(next_hop_reg_lock) 103 104 #define NEXT_HOP_UNIT_LOCK \ 105 sal_mutex_take(next_hop_unit_lock, sal_mutex_FOREVER) 106 #define NEXT_HOP_UNIT_UNLOCK sal_mutex_give(next_hop_unit_lock) 107 108 /* The Destination KEY address in the NEXT_HOP header */ 109 #define NEXT_HOP_DEST_KEY_SET(pkt_data, key) \ 110 CPUDB_KEY_PACK(&((pkt_data)[CPUTRANS_DEST_KEY_OFS]), key) 111 #define NEXT_HOP_DEST_KEY_GET(pkt_data, key) \ 112 CPUDB_KEY_UNPACK(&((pkt_data)[CPUTRANS_DEST_KEY_OFS]), key) 113 114 /* KEY extraction: The KEY address stored in the NEXT_HOP header */ 115 #define NEXT_HOP_SRC_KEY_SET(pkt_data, key) \ 116 CPUDB_KEY_PACK(&((pkt_data)[CPUTRANS_SRC_KEY_OFS]), key) 117 #define NEXT_HOP_SRC_KEY_GET(pkt_data, key) \ 118 CPUDB_KEY_UNPACK(&((pkt_data)[CPUTRANS_SRC_KEY_OFS]), key) 119 120 #define NEXT_HOP_SEQ_NUM_SET(pkt_data, val) \ 121 PACK_SHORT(&((pkt_data)[CPUTRANS_NH_SEQ_NUM_OFS]), val) 122 #define NEXT_HOP_SEQ_NUM_GET(pkt_data, val) \ 123 UNPACK_SHORT(&((pkt_data)[CPUTRANS_NH_SEQ_NUM_OFS]), val) 124 125 #define NEXT_HOP_MPLX_NUM_SET(pkt_data, val) \ 126 PACK_LONG(&((pkt_data)[CPUTRANS_NH_MPLX_NUM_OFS]), val) 127 #define NEXT_HOP_MPLX_NUM_GET(pkt_data, val) \ 128 UNPACK_LONG(&((pkt_data)[CPUTRANS_NH_MPLX_NUM_OFS]), val) 129 130 /* Init signals and synchronization */ 131 static volatile int setup_done; /* Are pointers set up? */ 132 static volatile int next_hop_exit; /* Force thread to exit */ 133 static volatile sal_thread_t nh_thread_id = SAL_THREAD_ERROR; 134 135 /**************************************************************** 136 * 137 * Configuration information 138 * 139 * next_hop_local_key - Local CPU's KEY addr for nexthop 140 * next_hop_trans_ptr - Transport pointer structure 141 * next_hop_mtu - Max pkt size supported in bytes 142 */ 143 144 static cpudb_key_t next_hop_local_key; 145 static bcm_trans_ptr_t *next_hop_trans_ptr = NEXT_HOP_TRANS_PTR_DEFAULT; 146 static int next_hop_thread_priority = NEXT_HOP_THREAD_PRIORITY_DEFAULT; 147 static int next_hop_rx_priority = NEXT_HOP_RX_PRIORITY_DEFAULT; 148 149 static int next_hop_mtu = NEXT_HOP_MTU_DEFAULT; 150 151 #define NEXT_HOP_VLAN_DEFAULT 1 152 #define NEXT_HOP_COS_DEFAULT 0 153 154 static int nh_vlan = NEXT_HOP_VLAN_DEFAULT; 155 static int nh_cos = NEXT_HOP_COS_DEFAULT; 156 157 static int next_hop_rx_queue_size = NEXT_HOP_RX_QUEUE_SIZE; 158 static int next_hop_tx_queue_size = NEXT_HOP_TX_QUEUE_SIZE; 159 160 /**************************************************************** 161 * 162 * Stack port management 163 */ 164 165 /* 166 * Local stack ports. (unit, port) and flag to indicate duplex 167 */ 168 static struct stk_port_t { 169 int unit; 170 int port; 171 uint32 flags; 172 #define NH_FLAGS_DUPLEX 0x1 173 } nh_stk_ports[STK_PORTS_MAX]; 174 static int num_stk_ports; 175 176 /* Keep track of which local units we're registered on */ 177 static int units[STK_PORTS_MAX]; 178 static int num_units; 179 180 181 /**************************************************************** 182 * 183 * Callback management 184 */ 185 186 /* 187 * Handler list. Unsorted. Added in order of registration. 188 */ 189 typedef struct cb_ctl_s { 190 next_hop_rx_callback_f callback; 191 int mplx_num; 192 void *cookie; 193 } cb_ctl_t; 194 static cb_ctl_t cb_ctl[NEXT_HOP_CALLBACK_MAX]; 195 static int num_cb_ctl; 196 197 198 /**************************************************************** 199 * 200 * TX/RX queue definitions 201 * The RX queue holds packets 202 * The TX queue needs a little more info, so we define the 203 * following structure. 204 * 205 * For TX entries, if the _F_PERSISTENT flag is not set, then 206 * the packet will be deallocated when the trx is complete. 207 */ 208 209 typedef struct tx_queue_s tx_queue_t; 210 struct tx_queue_s { 211 bcm_pkt_t *pkt; /* Packet to be transmitted */ 212 next_hop_tx_callback_f callback; /* Callback when trx is done */ 213 void *cookie; /* Passed to callback */ 214 uint32 flags; 215 #define PACKET_F_PERSISTENT 0x1 /* Keep pkt until destroyed */ 216 volatile tx_queue_t *next; /* For linked lists. */ 217 }; 218 219 static tx_queue_t *tx_queue_alloc_ptr; 220 static volatile tx_queue_t *tx_queue_freelist; 221 static volatile tx_queue_t *tx_queue; 222 static volatile tx_queue_t *tx_queue_tail; 223 224 static bcm_pkt_t *rx_queue_alloc_ptr; 225 static volatile bcm_pkt_t *rx_queue; 226 static volatile bcm_pkt_t *rx_queue_tail; 227 static volatile bcm_pkt_t *rx_queue_freelist; 228 229 /* 230 * Per system CPU information. 231 * Circular queues of sequence numbers per CPU; use local index here. 232 * The indexes are purely local; we want next hop to work independent of 233 * what CPUDBs might exist and what their states might be. 234 */ 235 #define CPU_KEY_MAX (2 * CPUDB_CPU_MAX) 236 typedef struct { 237 cpudb_key_t key; /* The key to use in addressing CPU */ 238 239 int tx_unit; /* Not yet used; for directed pkts */ 240 int tx_port; 241 242 uint16 seq_nums_seen[NEXT_HOP_SEQ_NUM_TRACK]; 243 int seq_num_last; /* Index in seq_nums_seen where latest entry put */ 244 sal_time_t last_ref; /* Last time CPU was referenced; for LRU */ 245 } cpu_seq_list_t; 246 247 static cpu_seq_list_t *cpu_seq_list[CPU_KEY_MAX]; 248 /* Allow KEY add to replace least-recently-used entry if full */ 249 static int key_lru_replace_enable = FALSE; 250 251 static int rx_pkt_drop_count; 252 static int tx_error_count; 253 254 /**************************************************************** 255 * 256 * Forward declarations 257 */ 258 259 STATIC void next_hop_thread(void *cookie); 260 STATIC void _next_hop_cleanup(void); 261 STATIC bcm_rx_t next_hop_rx_callback(int unit, bcm_pkt_t *pkt, void *cookie); 262 STATIC int _next_hop_init(void); 263 static INLINE int _nh_key_find(cpudb_key_t key); 264 static INLINE int _nh_key_add(cpudb_key_t key); 265 STATIC int _port_add(int unit, int port, int duplex, int *reg); 266 267 268 /**************************************************************** 269 * 270 * Start and Stop API 271 */ 272 273 /* 274 * Function: 275 * 276 * Purpose: 277 * 278 * Parameters: 279 * thread_priority - Priority to start task at; < 0 -> use default 280 * rx_priority - RX Register priority; < 0 -> use default 281 * local_key - Local CPU key to use to identify this CPU 282 * Returns: 283 * BCM_E_XXX 284 * Notes: 285 * Cannot be called when next hop is running. 286 */ 287 288 289 int 290 next_hop_config_set(bcm_trans_ptr_t *trans_ptr, 291 int thread_priority, 292 int rx_priority) 293 { 294 if (setup_done) { 295 return BCM_E_BUSY; 296 } 297 298 if (trans_ptr != NULL) { 299 if (trans_ptr->tp_data_free == NULL) { 300 return BCM_E_PARAM; 301 } 302 next_hop_trans_ptr = trans_ptr; 303 } 304 305 if (thread_priority >= 0) { 306 next_hop_thread_priority = thread_priority; 307 } 308 309 if (rx_priority >= 0) { 310 next_hop_rx_priority = rx_priority; 311 } 312 313 return BCM_E_NONE; 314 } 315 316 /* 317 * Function: 318 * 319 * Purpose: 320 * 321 * Parameters: 322 * thread_priority - Priority to start task at; < 0 -> use default 323 * rx_priority - RX Register priority; < 0 -> use default 324 * local_key - Local CPU key to use to identify this CPU 325 * Returns: 326 * BCM_E_XXX 327 * Notes: 328 */ 329 330 int 331 next_hop_config_get(bcm_trans_ptr_t **trans_ptr, 332 int *thread_priority, 333 int *rx_priority) 334 { 335 336 if (trans_ptr != NULL) { 337 *trans_ptr = next_hop_trans_ptr; 338 } 339 340 if (thread_priority != NULL) { 341 *thread_priority = next_hop_thread_priority; 342 } 343 344 if (rx_priority != NULL) { 345 *rx_priority = next_hop_rx_priority; 346 } 347 348 return BCM_E_NONE; 349 } 350 351 352 /* 353 * Function: 354 * next_hop_(cos,vlan)_(set,get) 355 * Purpose: 356 * Set/get the COS, internal priority and VLAN settings used by 357 * Notes: 358 * The RX subsystem and chip COS configurations must be 359 * set up consistently with the values used here; similarly 360 * for the VLAN settings. 361 * 362 * These values are only used when -1 is specified to the 363 * tx/pkt_create functions for cos/vlan parameters. 364 * 365 * The 'cos' parameter contains the cos and internal priority 366 * values encoded. Use CPUTRANS_COS_SET() CPUTRANS_INT_PRIO_SET() 367 * to set values accordingly. The internal priority is optional; 368 * if this is not provided, the cos value is used for internal priority. 369 */ 370 371 int 372 next_hop_cos_set(int cos) 373 { 374 nh_cos = cos; 375 376 return BCM_E_NONE; 377 } 378 379 int 380 next_hop_cos_get(int *cos) 381 { 382 *cos = nh_cos; 383 384 return BCM_E_NONE; 385 } 386 387 int 388 next_hop_vlan_set(int vlan) 389 { 390 nh_vlan = vlan; 391 392 return BCM_E_NONE; 393 } 394 395 int 396 next_hop_vlan_get(int *vlan) 397 { 398 *vlan = nh_vlan; 399 400 return BCM_E_NONE; 401 } 402 403 404 /* 405 * Function: 406 * next_hop_queue_size_set 407 * Purpose: 408 * Set the RX and TX queue sizes. 409 * Parameters: 410 * rx_size - RX queue size: 411 * If value is positive, set new value 412 * If value is negative, do not change current value 413 * tx_size - TX queue size: 414 * If value is positive, set new value 415 * If value is negative, do not change current value 416 * Returns: 417 * BCM_E_XXX 418 * Notes: 419 * API cannot be called when next hop is running. 420 */ 421 int 422 next_hop_queue_size_set(int rx_size, int tx_size) 423 { 424 if (setup_done) { 425 return BCM_E_BUSY; 426 } 427 428 if ((rx_size == 0) || (tx_size == 0)) { 429 return BCM_E_PARAM; 430 } 431 432 if (rx_size > 0) { 433 next_hop_rx_queue_size = rx_size; 434 } 435 if (tx_size > 0) { 436 next_hop_tx_queue_size = tx_size; 437 } 438 439 return BCM_E_NONE; 440 } 441 442 /* 443 * Function: 444 * next_hop_queue_size_get 445 * Purpose: 446 * Get the RX and TX queue sizes. 447 * Parameters: 448 * rx_size - (OUT) If non-null, returns RX queue size 449 * tx_size - (OUT) If non-null, returns TX queue size 450 * Returns: 451 * BCM_E_XXX 452 * Notes: 453 */ 454 int 455 next_hop_queue_size_get(int *rx_size, int *tx_size) 456 { 457 if (rx_size != NULL) { 458 *rx_size = next_hop_rx_queue_size; 459 } 460 if (tx_size != NULL) { 461 *tx_size = next_hop_tx_queue_size; 462 } 463 464 return BCM_E_NONE; 465 } 466 467 468 /* 469 * Function: 470 * next_hop_start 471 * Purpose: 472 * Start Next Hop broadcast packet reception 473 * Parameters: 474 * local_base - Base information about local CPU 475 * Returns: 476 * BCM_E_XXX 477 * Notes: 478 * 479 * This function does not add stack ports by default; some 480 * external agent must register stack ports with next hop either 481 * before or after this function is called. This is because 482 * there may be other qualifiers that make a stack port listed in 483 * the base configuration ineligible to receive next hop packets, 484 * such as the port being in a non-stacking mode, or the port 485 * does not have link. 486 */ 487 488 int 489 next_hop_start(const cpudb_base_t *local_base) 490 { 491 int rv; 492 int i; 493 494 if (setup_done) { /* Stop before restart */ 495 next_hop_stop(); 496 } 497 498 if (!cputrans_tx_setup_done()) { 499 BCM_IF_ERROR_RETURN(cputrans_tx_pkt_setup(-1, next_hop_trans_ptr)); 500 } 501 502 NEXT_HOP_INIT; 503 NEXT_HOP_LOCK; 504 505 nh_tx_setup(next_hop_trans_ptr); 506 507 CPUDB_KEY_COPY(next_hop_local_key, local_base->key); 508 nh_tx_local_mac_set(local_base->mac); 509 510 nh_thread_id = sal_thread_create("bcmNHOP", SAL_THREAD_STKSZ, 511 next_hop_thread_priority, 512 next_hop_thread, NULL); 513 if (nh_thread_id == SAL_THREAD_ERROR) { 514 NEXT_HOP_UNLOCK; 515 return BCM_E_RESOURCE; 516 } 517 518 /* Register with all known units; may be redundant with above */ 519 for (i = 0; i < num_units; i++) { 520 rv = cputrans_rx_unit_register(units[i], "next_hop", 521 next_hop_rx_callback, 522 next_hop_rx_priority, NULL, 523 BCM_RCO_F_ALL_COS); 524 if (rv < 0) { 525 cputrans_rx_unregister(next_hop_rx_callback, 526 next_hop_rx_priority); 527 NEXT_HOP_UNLOCK; 528 return rv; 529 } 530 } 531 532 tx_error_count = 0; 533 rx_pkt_drop_count = 0; 534 setup_done = TRUE; 535 536 NEXT_HOP_UNLOCK; 537 538 return BCM_E_NONE; 539 } 540 541 542 int 543 next_hop_update(const cpudb_base_t *local_base) 544 { 545 NEXT_HOP_INIT; 546 NEXT_HOP_LOCK; 547 548 CPUDB_KEY_COPY(next_hop_local_key, local_base->key); 549 nh_tx_local_mac_set(local_base->mac); 550 551 NEXT_HOP_UNLOCK; 552 553 return BCM_E_NONE; 554 } 555 556 557 /* 558 * Function: 559 * next_hop_stop 560 * Purpose: 561 * Stop next-hop broadcast packet reception 562 * Returns: 563 * BCM_E_XXX 564 */ 565 566 int 567 next_hop_stop(void) 568 { 569 int i; 570 571 NEXT_HOP_INIT; 572 573 if (!setup_done) { 574 return BCM_E_NONE; 575 } 576 577 /* Keep any callbacks from doing anything */ 578 setup_done = FALSE; 579 580 /* Unregister from units */ 581 cputrans_rx_unregister(next_hop_rx_callback, 582 next_hop_rx_priority); 583 584 /* Force the thread to exit */ 585 if (nh_thread_id != SAL_THREAD_ERROR) { 586 next_hop_exit = TRUE; 587 sal_sem_give(next_hop_sem); 588 /* Allow thread to exit */ 589 for (i = 0; i < 50; i++) { 590 if (nh_thread_id == SAL_THREAD_ERROR) { 591 break; 592 } 593 sal_usleep(10000); 594 } 595 if (nh_thread_id != SAL_THREAD_ERROR) { 596 LOG_INFO(BSL_LS_TKS_NH, 597 (BSL_META("Warning: NEXT_HOP thread did not exit\n"))); 598 } 599 } 600 601 /* Deallocate local resouces */ 602 _next_hop_cleanup(); 603 604 return BCM_E_NONE; 605 } 606 607 608 /**************************************************************** 609 * 610 * Callback registration 611 */ 612 613 614 /* 615 * Function: 616 * next_hop_register 617 * Purpose: 618 * Register a callback for the given port number 619 * Parameters: 620 * callback - Call back function 621 * cookie - Passed back on callback 622 * mplx_num - Which port number to listen on 623 * Returns: 624 * BCM_E_XXX 625 * Notes: 626 */ 627 628 int 629 next_hop_register(next_hop_rx_callback_f callback, void *cookie, 630 int mplx_num) 631 { 632 int i; 633 634 NEXT_HOP_INIT; 635 NEXT_HOP_REG_LOCK; 636 637 /* See if already registered */ 638 for (i = 0; i < num_cb_ctl; i++) { 639 if (cb_ctl[i].mplx_num == mplx_num && 640 cb_ctl[i].callback == callback && 641 cb_ctl[i].cookie == cookie) { 642 NEXT_HOP_REG_UNLOCK; 643 return BCM_E_NONE; 644 } 645 } 646 647 LOG_INFO(BSL_LS_TKS_NH, 648 (BSL_META("Registering %p\n"), 649 callback)); 650 651 if (num_cb_ctl >= NEXT_HOP_CALLBACK_MAX) { 652 NEXT_HOP_REG_UNLOCK; 653 return BCM_E_RESOURCE; 654 } 655 656 cb_ctl[num_cb_ctl].mplx_num = mplx_num; 657 cb_ctl[num_cb_ctl].callback = callback; 658 cb_ctl[num_cb_ctl].cookie = cookie; 659 num_cb_ctl++; 660 661 NEXT_HOP_REG_UNLOCK; 662 return BCM_E_NONE; 663 } 664 665 666 /* 667 * Function: 668 * next_hop_unregister 669 * Purpose: 670 * Unregister a callback for the given port number 671 * Parameters: 672 * callback - Call back function 673 * mplx_num - Which port number to listen on 674 * Returns: 675 * BCM_E_XXX 676 * Notes: 677 */ 678 679 int 680 next_hop_unregister(next_hop_rx_callback_f callback, int mplx_num) 681 { 682 int idx; 683 int j; 684 685 NEXT_HOP_INIT; 686 NEXT_HOP_REG_LOCK; 687 688 for (idx = 0; idx < num_cb_ctl; idx++) { 689 if (cb_ctl[idx].callback == callback && 690 cb_ctl[idx].mplx_num == mplx_num) { 691 for (j = idx + 1; j < num_cb_ctl; j++) { 692 sal_memcpy(&cb_ctl[j - 1], &cb_ctl[j], 693 sizeof(struct cb_ctl_s)); 694 } 695 num_cb_ctl--; 696 NEXT_HOP_REG_UNLOCK; 697 return BCM_E_NONE; 698 } 699 } 700 701 NEXT_HOP_REG_UNLOCK; 702 return BCM_E_NOT_FOUND; 703 } 704 705 706 /* 707 * Function: 708 * next_hop_running 709 * Purpose: 710 * Indicate if Next Hop is set up 711 * Returns: 712 * TRUE if setup has been done. 713 */ 714 715 int 716 next_hop_running(void) 717 { 718 return setup_done; 719 } 720 721 /* Local initialization: Allocate needed structures */ 722 STATIC int 723 _next_hop_init(void) 724 { 725 int bytes; 726 int i; 727 int rv = BCM_E_NONE; 728 729 /* Allocate synchronization structures */ 730 next_hop_lock = sal_mutex_create("next_hop_lock"); 731 if (next_hop_lock == NULL) { 732 return BCM_E_MEMORY; 733 } 734 735 next_hop_reg_lock = sal_mutex_create("next_hop_reg_lock"); 736 if (next_hop_reg_lock == NULL) { 737 _next_hop_cleanup(); 738 return BCM_E_MEMORY; 739 } 740 741 next_hop_unit_lock = sal_mutex_create("next_hop_unit_lock"); 742 if (next_hop_unit_lock == NULL) { 743 _next_hop_cleanup(); 744 return BCM_E_MEMORY; 745 } 746 747 next_hop_sem = sal_sem_create("next_hop_sem", sal_sem_BINARY, 0); 748 if (next_hop_sem == NULL) { 749 _next_hop_cleanup(); 750 return BCM_E_MEMORY; 751 } 752 753 /* Allocate RX queue */ 754 bytes = sizeof(bcm_pkt_t) * next_hop_rx_queue_size; 755 rx_queue_alloc_ptr = sal_alloc(bytes, "next_hop_queue"); 756 if (rx_queue_alloc_ptr == NULL) { 757 _next_hop_cleanup(); 758 return BCM_E_MEMORY; 759 } 760 sal_memset(rx_queue_alloc_ptr, 0, bytes); 761 762 for (i = 0; i < next_hop_rx_queue_size; i++) { 763 rx_queue_alloc_ptr[i].next = &rx_queue_alloc_ptr[i + 1]; 764 } 765 rx_queue_alloc_ptr[i - 1].next = NULL; 766 rx_queue_freelist = rx_queue_alloc_ptr; 767 768 rx_queue = rx_queue_tail = NULL; 769 770 /* Allocate TX queue */ 771 bytes = sizeof(tx_queue_t) * next_hop_tx_queue_size; 772 tx_queue_alloc_ptr = sal_alloc(bytes, "next_hop_tx_queue"); 773 if (tx_queue_alloc_ptr == NULL) { 774 _next_hop_cleanup(); 775 return BCM_E_MEMORY; 776 } 777 sal_memset(tx_queue_alloc_ptr, 0, bytes); 778 779 for (i = 0; i < next_hop_tx_queue_size; i++) { 780 tx_queue_alloc_ptr[i].next = &tx_queue_alloc_ptr[i + 1]; 781 } 782 tx_queue_alloc_ptr[i - 1].next = NULL; 783 tx_queue_freelist = tx_queue_alloc_ptr; 784 785 tx_queue = tx_queue_tail = NULL; 786 787 return rv; 788 } 789 790 #define CHECK_FREE(id) \ 791 do { \ 792 if ((id) != NULL) { \ 793 sal_free(id); \ 794 (id) = NULL; \ 795 } \ 796 } while (0) 797 798 /* Local deallocation if error in allocation */ 799 STATIC void 800 _next_hop_cleanup(void) 801 { 802 if (next_hop_lock != NULL) { 803 sal_mutex_destroy(next_hop_lock); 804 next_hop_lock = NULL; 805 } 806 if (next_hop_reg_lock != NULL) { 807 sal_mutex_destroy(next_hop_reg_lock); 808 next_hop_reg_lock = NULL; 809 } 810 if (next_hop_unit_lock != NULL) { 811 sal_mutex_destroy(next_hop_unit_lock); 812 next_hop_unit_lock = NULL; 813 } 814 if (next_hop_sem != NULL) { 815 sal_sem_destroy(next_hop_sem); 816 next_hop_sem = NULL; 817 } 818 CHECK_FREE(rx_queue_alloc_ptr); 819 CHECK_FREE(tx_queue_alloc_ptr); 820 tx_queue = tx_queue_tail = NULL; 821 rx_queue = rx_queue_tail = NULL; 822 } 823 824 #undef CHECK_FREE 825 826 /**************************************************************** 827 * 828 * CPU key address management: 829 * External: max_entries_get, key_get, key_invalidate. 830 */ 831 832 /* 833 * Function: 834 * next_hop_max_entries_get 835 * Purpose: 836 * Get the max possible CPU entries in internal DB 837 * Returns: 838 * Integer number of entries 839 * Notes: 840 * This can be used to then run through the entries with key_get 841 * to determine which CPU keys are in the system 842 */ 843 844 int 845 next_hop_max_entries_get(void) 846 { 847 return CPU_KEY_MAX; 848 } 849 850 851 /* 852 * Function: 853 * next_hop_key_get 854 * Purpose: 855 * Get the i-th next hop CPU key. Index is local to nexthop. 856 * Parameters: 857 * idx - which internal index to lookup 858 * Returns: 859 * Pointer to CPU's KEY; CPUDB_KEY_NULL if not valid 860 */ 861 862 const cpudb_key_t * 863 next_hop_key_get(int idx) 864 { 865 if (cpu_seq_list[idx] != NULL) { 866 return (const cpudb_key_t *) &(cpu_seq_list[idx]->key); 867 } 868 869 return NULL; 870 } 871 872 /* 873 * Function: 874 * next_hop_key_invalidate 875 * Purpose: 876 * Invalidate an existing CPU key 877 * Parameters: 878 * key 879 * Returns: 880 * BCM_E_XXX 881 * Notes: 882 * No error if not found. 883 * 884 * The number of CPU keys that next hop tracks is twice the 885 * number of supported CPUs. But if the system is reconfiguring 886 * over time, then higher layer applications should invalidate 887 * departed keys to make room for new ones. 888 */ 889 890 int 891 next_hop_key_invalidate(cpudb_key_t key) 892 { 893 int local_idx; 894 895 NEXT_HOP_INIT; 896 NEXT_HOP_LOCK; 897 local_idx = _nh_key_find(key); 898 if (local_idx >= 0) { 899 sal_free(cpu_seq_list[local_idx]); 900 cpu_seq_list[local_idx] = NULL; 901 } 902 903 NEXT_HOP_UNLOCK; 904 return BCM_E_NONE; 905 } 906 907 908 /**************************************************************** 909 * 910 * Configuration information 911 * MTU Max packet size 912 * LRU Should CPU key be bumped in least-recently-used 913 * fashion. 914 */ 915 916 /* 917 * Function: 918 * next_hop_mtu_get 919 * Purpose: 920 * Get the current max transmit unit size in bytes 921 * Parameters: 922 * mtu - (OUT) Where to store value 923 * Returns: 924 * BCM_E_XXX 925 * Notes: 926 * Can be called anytime 927 */ 928 929 int 930 next_hop_mtu_get(int *mtu) 931 { 932 *mtu = next_hop_mtu; 933 934 return BCM_E_NONE; 935 } 936 937 938 /* 939 * Function: 940 * next_hop_mtu_set 941 * Purpose: 942 * Set the max transmit unit size in bytes 943 * Parameters: 944 * mtu - The max transmit unit size in bytes 945 * Returns: 946 * BCM_E_XXX 947 * Notes: 948 * Must be called before any initialization is done. 949 */ 950 951 int 952 next_hop_mtu_set(int mtu) 953 { 954 955 if (next_hop_lock != NULL) { 956 return BCM_E_BUSY; 957 } 958 959 next_hop_mtu = mtu; 960 961 return BCM_E_NONE; 962 } 963 964 965 /* 966 * Function: 967 * next_hop_lru_enable_get 968 * Purpose: 969 * Return current value of KEY LRU replacement policy 970 * Returns: 971 * Boolean indicating current state: 972 * TRUE LRU replacement of CPU key on ADD is enabled. 973 * FALSE LRU replacement of CPU key on ADD is disabled. 974 */ 975 976 int 977 next_hop_lru_enable_get(void) 978 { 979 return key_lru_replace_enable; 980 } 981 982 /* 983 * Function: 984 * next_hop_lru_enable_set 985 * Purpose: 986 * Set the KEY LRU replacement policy 987 * Parameters: 988 * lru - The boolean value to set policy to. 989 * TRUE LRU replacement of CPU key on ADD is enabled. 990 * FALSE LRU replacement of CPU key on ADD is disabled. 991 */ 992 993 void 994 next_hop_lru_enable_set(int lru) 995 { 996 key_lru_replace_enable = lru; 997 } 998 999 1000 1001 /**************************************************************** 1002 * 1003 * Forward declarations, stack port configuration 1004 */ 1005 1006 static INLINE int _unit_on_list(int unit); 1007 static INLINE void _update_unit_list(void); 1008 1009 STATIC int 1010 _port_add(int unit, int port, int duplex, int *reg) 1011 { 1012 int i; 1013 1014 for (i = 0; i < num_stk_ports; i++) { 1015 if (nh_stk_ports[i].unit == unit && 1016 nh_stk_ports[i].port == port) { 1017 nh_stk_ports[i].flags = duplex ? NH_FLAGS_DUPLEX : 0; 1018 return BCM_E_NONE; 1019 } 1020 } 1021 1022 if (num_stk_ports >= STK_PORTS_MAX) { 1023 return BCM_E_RESOURCE; 1024 } 1025 1026 /* If new unit and setup_done, register callback */ 1027 *reg = (_unit_on_list(unit) < 0 && setup_done); 1028 1029 nh_stk_ports[num_stk_ports].unit = unit; 1030 nh_stk_ports[num_stk_ports].port = port; 1031 nh_stk_ports[num_stk_ports].flags = duplex ? NH_FLAGS_DUPLEX : 0; 1032 num_stk_ports++; 1033 _update_unit_list(); 1034 1035 return BCM_E_NONE; 1036 } 1037 1038 /* 1039 * Function: 1040 * next_hop_port_add 1041 * Purpose: 1042 * Add a port from the list of stack ports 1043 * Parameters: 1044 * unit - Local physical unit number 1045 * port - Local physical port on unit 1046 * duplex - Is the connection known to be duplex? 1047 * Returns: 1048 * BCM_E_XXX 1049 * Notes: 1050 * Can re-add a port and change duplex setting. 1051 * Not an error if the port is already known. 1052 */ 1053 1054 int 1055 next_hop_port_add(int unit, int port, int duplex) 1056 { 1057 int rv; 1058 int reg = FALSE; 1059 NEXT_HOP_INIT; 1060 1061 NEXT_HOP_UNIT_LOCK; 1062 NEXT_HOP_LOCK; 1063 rv = _port_add(unit, port, duplex, ®); 1064 NEXT_HOP_UNLOCK; 1065 if (BCM_SUCCESS(rv) && reg) { 1066 rv = cputrans_rx_unit_register(unit, "next_hop", 1067 next_hop_rx_callback, 1068 next_hop_rx_priority, NULL, 1069 BCM_RCO_F_ALL_COS); 1070 } 1071 NEXT_HOP_UNIT_UNLOCK; 1072 if (BCM_FAILURE(rv) && reg) { 1073 next_hop_port_remove(unit, port); 1074 } else { 1075 LOG_INFO(BSL_LS_TKS_NH, 1076 (BSL_META_U(unit, 1077 "Added port (%d,%d)=%d\n"), 1078 unit, port, rv)); 1079 } 1080 return rv; 1081 } 1082 1083 1084 /* 1085 * Function: 1086 * next_hop_port_remove 1087 * Purpose: 1088 * Remove a port from the list of stack ports 1089 * Parameters: 1090 * unit - Local physical unit number 1091 * port - Local physical port on unit 1092 * Returns: 1093 * BCM_E_XXX 1094 * Notes: 1095 * Returns NOT_FOUND if the port isn't on the list 1096 */ 1097 1098 int 1099 next_hop_port_remove(int unit, int port) 1100 { 1101 int i, j, found, unreg; 1102 1103 NEXT_HOP_INIT; 1104 1105 unreg = FALSE; 1106 NEXT_HOP_UNIT_LOCK; 1107 NEXT_HOP_LOCK; 1108 found = FALSE; 1109 for (i = 0; i < num_stk_ports; i++) { 1110 if (nh_stk_ports[i].unit == unit && 1111 nh_stk_ports[i].port == port) { 1112 found = TRUE; 1113 break; 1114 } 1115 } 1116 1117 if (!found) { 1118 NEXT_HOP_UNLOCK; 1119 NEXT_HOP_UNIT_UNLOCK; 1120 return BCM_E_NOT_FOUND; 1121 } 1122 1123 for (j = i + 1; j < num_stk_ports; j++) { 1124 nh_stk_ports[j - 1].unit = nh_stk_ports[j].unit; 1125 nh_stk_ports[j - 1].port = nh_stk_ports[j].port; 1126 } 1127 num_stk_ports--; 1128 1129 /* Update the unit list and see if removed; unregister if so */ 1130 _update_unit_list(); 1131 unreg = (_unit_on_list(unit) < 0 && setup_done); 1132 1133 NEXT_HOP_UNLOCK; 1134 if (unreg) { 1135 cputrans_rx_unit_unregister(unit, next_hop_rx_callback, 1136 next_hop_rx_priority); 1137 } 1138 NEXT_HOP_UNIT_UNLOCK; 1139 LOG_INFO(BSL_LS_TKS_NH, 1140 (BSL_META_U(unit, 1141 "Removed port (%d,%d)\n"), 1142 unit,port)); 1143 return BCM_E_NONE; 1144 } 1145 1146 1147 /* Manage list of known units; return unit index if found; otherwise -1 */ 1148 static INLINE int 1149 _unit_on_list(int unit) 1150 { 1151 int i; 1152 for (i = 0; i < num_units; i++) { 1153 if (units[i] == unit) { 1154 return i; 1155 } 1156 } 1157 return -1; 1158 } 1159 1160 /* Updates unit list; Assumes lock is held; completely regenerates 1161 * the unit list from the stack port list. 1162 */ 1163 STATIC INLINE void 1164 _update_unit_list(void) 1165 { 1166 int idx, i; 1167 1168 num_units = 0; 1169 for (i = 0; i < num_stk_ports; i++) { 1170 idx = _unit_on_list(nh_stk_ports[i].unit); 1171 if (idx < 0) { 1172 units[num_units++] = nh_stk_ports[i].unit; 1173 } 1174 } 1175 } 1176 1177 1178 /* 1179 * Function: 1180 * next_hop_num_ports_get 1181 * Purpose: 1182 * Get the number of ports in NH database 1183 * Returns: 1184 * Integer number of stack ports 1185 */ 1186 1187 int 1188 next_hop_num_ports_get(void) 1189 { 1190 return num_stk_ports; 1191 } 1192 1193 1194 /* 1195 * Function: 1196 * next_hop_port_get 1197 * Purpose: 1198 * Get a next hop port 1199 * Parameters: 1200 * idx - Which index to look up 1201 * unit - Where to put unit; may be NULL 1202 * port - Where to put port; may be NULL 1203 * duplex - Where to put duplex setting; may be NULL 1204 * Returns: 1205 * BCM_E_XXX 1206 * Notes: 1207 */ 1208 1209 int 1210 next_hop_port_get(int idx, int *unit, int *port, int *duplex) 1211 { 1212 if (idx < 0 || idx >= num_stk_ports) { 1213 return BCM_E_NOT_FOUND; 1214 } 1215 1216 if (unit != NULL) { 1217 *unit = nh_stk_ports[idx].unit; 1218 } 1219 if (port != NULL) { 1220 *port = nh_stk_ports[idx].port; 1221 } 1222 if (duplex != NULL) { 1223 *duplex = nh_stk_ports[idx].flags & NH_FLAGS_DUPLEX; 1224 } 1225 1226 return BCM_E_NONE; 1227 } 1228 1229 1230 /**************************************************************** 1231 * 1232 * Primary API calls: 1233 * next_hop_pkt_create Create a (persistent) packet 1234 * next_hop_pkt_send Send packet from create 1235 * next_hop_pkt_destroy Release packet from create 1236 * next_hop_tx Send buffer once 1237 */ 1238 1239 static INLINE uint16 next_hop_seq_num_get(void); 1240 static INLINE int _tx_packet_enqueue(bcm_pkt_t *pkt, 1241 next_hop_tx_callback_f callback, 1242 void *cookie, 1243 uint32 flags); 1244 STATIC int _packet_send(bcm_pkt_t *pkt, int forward, 1245 next_hop_tx_callback_f callback, 1246 void *cookie); 1247 1248 /* 1249 * Function: 1250 * next_hop_pkt_create 1251 * Purpose: 1252 * Set up and allocate a packet or list of packets to be transmitted 1253 * by next hop 1254 * Parameters: 1255 * pkt_buf - The packet buffer to send 1256 * len - Number of bytes of buffer to send 1257 * vlan - VLAN used on packet 1258 * cos - COS and internal priority used on packet 1259 * seg_len - Number of bytes in a segment 1260 * ct_flags - Bitmap of flags passed in 1261 * CPUTRANS_NO_HEADER_ALLOC Space for transport header available 1262 * at start of packet 1263 * mplx_num - Port number to place in packet 1264 * nh_dest_key - Broadcast or destination CPU key used inside of 1265 * nexthop packet (not L2 header). 1266 * Returns: 1267 * BCM_E_XXX 1268 * Notes: 1269 * See next_hop_tx for notes. This routine sets up a tx transaction 1270 * but does not enqueue it. 1271 * 1272 * Once created, the packet may be sent repeatedly using the 1273 * next_hop_pkt_update and next_hop_pkt_send routines. When done, 1274 * next_hop_pkt_destroy must be called on the packet. 1275 * 1276 * If cos (or vlan) is < 0, the default is used. 1277 * 1278 * The 'cos' parameter contains the cos and internal priority 1279 * values encoded. Use CPUTRANS_COS_SET() CPUTRANS_INT_PRIO_SET() 1280 * to set values accordingly. The internal priority is optional; 1281 * if this is not provided, the cos value is used for internal priority. 1282 */ 1283 1284 #define NO_BCM_UNIT 255 1285 1286 bcm_pkt_t * 1287 next_hop_pkt_create(uint8 *pkt_buf, 1288 int len, 1289 int cos, 1290 int vlan, 1291 int seg_len, 1292 uint32 ct_flags, 1293 int mplx_num, 1294 const cpudb_key_t nh_dest_key, 1295 int *tot_segs, 1296 int *rvp) 1297 { 1298 bcm_pkt_t *pkt, *cur_pkt; 1299 uint8 *hdr_buf; 1300 int rv; 1301 1302 if (next_hop_lock == NULL) { 1303 if ((rv=_next_hop_init()) < 0) { 1304 *rvp = rv; 1305 return NULL; 1306 } 1307 } 1308 1309 if (!setup_done) { 1310 *rvp = BCM_E_INIT; 1311 return NULL; 1312 } 1313 1314 pkt = cputrans_tx_pkt_list_alloc(pkt_buf, len, seg_len, ct_flags, 1315 tot_segs); 1316 if (pkt == NULL) { 1317 *rvp = BCM_E_MEMORY; 1318 return NULL; 1319 } 1320 1321 pkt->rx_unit = NO_BCM_UNIT; 1322 for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) { 1323 /* Set up the packet header for each segment, always in first block. */ 1324 hdr_buf = cur_pkt->pkt_data[0].data; 1325 1326 NEXT_HOP_SRC_KEY_SET(hdr_buf, next_hop_local_key); 1327 NEXT_HOP_SEQ_NUM_SET(hdr_buf, next_hop_seq_num_get()); 1328 if (ct_flags & CPUTRANS_BCAST) { 1329 NEXT_HOP_DEST_KEY_SET(hdr_buf, cpudb_bcast_key); 1330 } else { 1331 NEXT_HOP_DEST_KEY_SET(hdr_buf, nh_dest_key); 1332 } 1333 NEXT_HOP_MPLX_NUM_SET(hdr_buf, mplx_num); 1334 1335 cur_pkt->call_back = NULL; 1336 } 1337 1338 cos = (cos < 0) ? nh_cos : cos; 1339 vlan = (vlan < 0) ? nh_vlan : vlan; 1340 1341 nh_pkt_setup(pkt, cos, vlan, NEXT_HOP_PKT_TYPE, 0); 1342 *rvp = BCM_E_NONE; 1343 return pkt; 1344 } 1345 1346 /* 1347 * Function: 1348 * next_hop_pkt_update 1349 * Purpose: 1350 * Update a packet or list of packets created by next_hop_pkt_create, 1351 * cputrans_tx_pkt_alloc or cputrans_tx_pkt_list_alloc. 1352 * Parameters: 1353 * pkt - Packet to update 1354 * mplx_num - Port number to use in packet 1355 * nh_dest_key - Destination key to use to address CPU 1356 * Returns: 1357 * BCM_E_XXX 1358 * Notes: 1359 * Initializes the next hop and nh_tx (transport) headers 1360 * 1361 * This will change the sequence number for each packet as 1362 * well, so that the packet appears as a different NH packet 1363 * when sent. 1364 */ 1365 1366 int 1367 next_hop_pkt_update(bcm_pkt_t *pkt, int mplx_num, 1368 const cpudb_key_t nh_dest_key) 1369 { 1370 uint8 *hdr_buf; 1371 bcm_pkt_t *cur_pkt; 1372 1373 for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) { 1374 hdr_buf = cur_pkt->pkt_data[0].data; 1375 NEXT_HOP_DEST_KEY_SET(hdr_buf, nh_dest_key); 1376 NEXT_HOP_MPLX_NUM_SET(hdr_buf, mplx_num); 1377 NEXT_HOP_SEQ_NUM_SET(hdr_buf, next_hop_seq_num_get()); 1378 } 1379 1380 return BCM_E_NONE; 1381 } 1382 1383 1384 /* 1385 * Function: 1386 * next_hop_buffer_init 1387 * Purpose: 1388 * Initialize a buffer that is to be sent using nh_tx and received 1389 * by the next hop layer 1390 * Parameters: 1391 * hdr_buf - Pointer to header to update 1392 * mplex_num - Multiplexing port number for packet 1393 * nh_dest_key - Next hop destination CPU key to use 1394 * Returns: 1395 * BCM_E_XXX 1396 * Notes: 1397 * hdr_buf must point to the start of the CPUTRANS header for 1398 * the packet. 1399 * 1400 * For now, this sets the type/length field to get the pkt thru. 1401 */ 1402 1403 int 1404 next_hop_buffer_init(uint8 *hdr_buf, int mplex_num, 1405 const cpudb_key_t nh_dest_key) 1406 { 1407 int len_fld_val = 0x601; 1408 1409 PACK_SHORT(&hdr_buf[CPUTRANS_LEN_OFS], len_fld_val); 1410 NEXT_HOP_SRC_KEY_SET(hdr_buf, next_hop_local_key); 1411 NEXT_HOP_DEST_KEY_SET(hdr_buf, nh_dest_key); 1412 NEXT_HOP_MPLX_NUM_SET(hdr_buf, mplex_num); 1413 NEXT_HOP_SEQ_NUM_SET(hdr_buf, next_hop_seq_num_get()); 1414 1415 return BCM_E_NONE; 1416 } 1417 1418 /* 1419 * Function: 1420 * next_hop_pkt_send 1421 * Purpose: 1422 * Transmit a packet or linked list of pkts prepared by next_hop_pkt_create 1423 * Parameters: 1424 * pkt - The packet(s) prepared by next_hop_pkt_create 1425 * Returns: 1426 * BCM_E_XXX 1427 * Notes: 1428 * The packet(s) passed to this routine must have been created by 1429 * next_hop_pkt_create 1430 */ 1431 1432 int 1433 next_hop_pkt_send(bcm_pkt_t *pkt, 1434 next_hop_tx_callback_f callback, 1435 void *cookie) 1436 { 1437 int rv = BCM_E_NONE; 1438 1439 if (callback != NULL) { /* Send async */ 1440 rv = _tx_packet_enqueue(pkt, callback, cookie, PACKET_F_PERSISTENT); 1441 } else { 1442 rv = _packet_send(pkt, FALSE, callback, cookie); 1443 } 1444 1445 return rv; 1446 } 1447 1448 1449 /* 1450 * Function: 1451 * next_hop_pkt_destroy 1452 * Purpose: 1453 * "Free" a packet or list of packets created by next_hop_pkt_create 1454 * Parameters: 1455 * pkt - The packet pointer returned by next_hop_pkt_create 1456 * Returns: 1457 * BCM_E_XXX 1458 * Notes: 1459 * After calling this routine, the packet should no longer be 1460 * referenced. 1461 */ 1462 1463 int 1464 next_hop_pkt_destroy(bcm_pkt_t *pkt) 1465 { 1466 cputrans_tx_pkt_list_free(pkt); 1467 1468 return BCM_E_NONE; 1469 } 1470 1471 1472 /* 1473 * Function: 1474 * next_hop_tx 1475 * Purpose: 1476 * Transmit a next hop packet 1477 * Parameters: 1478 * pkt_buf - The packet buffer to send 1479 * len - Number of bytes of buffer to send 1480 * cos - COS and internal priority used on packet 1481 * vlan - VLAN used on packet 1482 * seg_len - Number of bytes in a segment 1483 * ct_flags - Bitmap of flags passed in 1484 * NEXT_HOP_NO_HEADER_ALLOC Header space is available at 1485 * beginning of packet buffer. 1486 * mplx_num - Port number to place in packet 1487 * nh_dest_key - Broadcast or destination key used inside of 1488 * nexthop packet (not L2 header). 1489 * callback - If non-NULL send asynchronously 1490 * cookie - Passed to callback 1491 * Returns: 1492 * BCM_E_XXX 1493 * Notes: 1494 * Note that the application does not need to forward the 1495 * packets that it receives; that happens automatically at this layer. 1496 * 1497 * If HEADER_ALLOC flag is NOT set, then the packet buffer must 1498 * have space (NEXT_HOP_HEADER_BYTES bytes) for next hop info at 1499 * the head of packet buffer. 1500 * 1501 * If asynchronous send is done, the callback must call 1502 * next_hop_tx_done with the cookie passed back in tx_cookie. 1503 * 1504 * If cos (or vlan) is < 0, the default is used. 1505 * 1506 * The 'cos' parameter contains the cos and internal priority 1507 * values encoded. Use CPUTRANS_COS_SET() CPUTRANS_INT_PRIO_SET() 1508 * to set values accordingly. The internal priority is optional; 1509 * if this is not provided, the cos value is used for internal priority. 1510 */ 1511 1512 int 1513 next_hop_tx(uint8 *pkt_buf, 1514 int len, 1515 int cos, 1516 int vlan, 1517 int seg_len, 1518 uint32 ct_flags, 1519 int mplx_num, 1520 const cpudb_key_t nh_dest_key, 1521 next_hop_tx_callback_f callback, 1522 void *cookie) 1523 { 1524 bcm_pkt_t *pkt; 1525 int rv = BCM_E_NONE; 1526 1527 NEXT_HOP_INIT; 1528 if (!setup_done) { 1529 return BCM_E_INIT; 1530 } 1531 1532 pkt = next_hop_pkt_create(pkt_buf, 1533 len, 1534 cos, 1535 vlan, 1536 seg_len, 1537 ct_flags, 1538 mplx_num, 1539 nh_dest_key, NULL, &rv); 1540 1541 if (pkt == NULL) { 1542 return BCM_E_RESOURCE; 1543 } 1544 1545 if (callback != NULL) { /* Send async, dealloc pkt when done */ 1546 rv = _tx_packet_enqueue(pkt, callback, cookie, 0); 1547 } else { /* Send sync */ 1548 rv = _packet_send(pkt, FALSE, callback, cookie); 1549 } 1550 1551 if (callback == NULL || rv != BCM_E_NONE) { 1552 next_hop_pkt_destroy(pkt); 1553 } 1554 1555 return rv; 1556 } 1557 1558 /* 1559 * Function: 1560 * next_hop_data_free 1561 * Purpose: 1562 * Free data buffers from stolen packet pointers 1563 * Parameters: 1564 * pkt_buf - The buffer to free 1565 * Returns: 1566 * BCM_E_XXX 1567 * Notes: 1568 */ 1569 1570 void 1571 next_hop_data_free(uint8 *pkt_buf) 1572 { 1573 next_hop_trans_ptr->tp_data_free(-1, pkt_buf); 1574 } 1575 1576 /**************************************************************** 1577 * 1578 * Next Hop Thread 1579 */ 1580 1581 1582 /**************************************************************** 1583 * 1584 * Next hop thread functions 1585 * next_hop_thread The main thread control function 1586 * handle_txrx_queue Process all pending operations in tx/rx queueu 1587 */ 1588 1589 static INLINE void handle_tx_queue(void); 1590 static INLINE void handle_rx_queue(void); 1591 static INLINE void _packet_up_stack(bcm_pkt_t *pkt); 1592 static INLINE void _rx_packet_free(bcm_pkt_t *pkt); 1593 static INLINE void _tx_queue_free(tx_queue_t *entry); 1594 1595 /* 1596 * Function: 1597 * next_hop_thread 1598 * Purpose: 1599 * Main thread for Next Hop Broadcast transport 1600 * Parameters: 1601 * cookie - ignored 1602 * Returns: 1603 * BCM_E_XXX 1604 * Notes: 1605 * Simple loop to service TX and RX queues. Will exit when 1606 * next_hop_exit is set. 1607 */ 1608 1609 STATIC void 1610 next_hop_thread(void *cookie) 1611 { 1612 1613 COMPILER_REFERENCE(cookie); 1614 1615 next_hop_exit = FALSE; 1616 while (!next_hop_exit) { 1617 sal_sem_take(next_hop_sem, sal_sem_FOREVER); 1618 1619 if (next_hop_exit) { 1620 break; 1621 } 1622 1623 handle_tx_queue(); 1624 handle_rx_queue(); 1625 } 1626 1627 nh_thread_id = SAL_THREAD_ERROR; 1628 sal_thread_exit(0); 1629 } 1630 1631 /* 1632 * Handle TX packet queue 1633 */ 1634 1635 static INLINE void 1636 handle_tx_queue(void) 1637 { 1638 tx_queue_t *local_tx_queue, *entry; 1639 1640 if (tx_queue != NULL) { 1641 1642 /* Grab TX queue and process entries */ 1643 NEXT_HOP_LOCK; 1644 local_tx_queue = (tx_queue_t *)tx_queue; 1645 tx_queue = NULL; 1646 tx_queue_tail = NULL; 1647 NEXT_HOP_UNLOCK; 1648 1649 while (local_tx_queue != NULL) { 1650 entry = local_tx_queue; 1651 local_tx_queue = (tx_queue_t *)local_tx_queue->next; 1652 1653 (void)_packet_send(entry->pkt, FALSE, 1654 entry->callback, entry->cookie); 1655 1656 if (!(entry->flags & PACKET_F_PERSISTENT)) { 1657 cputrans_tx_pkt_list_free(entry->pkt); 1658 } 1659 _tx_queue_free(entry); 1660 } 1661 } 1662 } 1663 1664 /* 1665 * Handle RX packet queue 1666 */ 1667 1668 static INLINE void 1669 handle_rx_queue(void) 1670 { 1671 bcm_pkt_t *local_rx_queue; 1672 bcm_pkt_t *pkt; 1673 int is_bcast; /* Packet is broadcast */ 1674 int is_directed; /* Packet sent to a specific key */ 1675 int is_directed_local; /* Packet sent to local cpu directly */ 1676 cpudb_key_t key; /* From next hop header */ 1677 int is_neighbor; /* Don't forward. */ 1678 1679 if (rx_queue != NULL) { 1680 1681 /* Grab RX queue and process entries */ 1682 NEXT_HOP_LOCK; 1683 local_rx_queue = (bcm_pkt_t *)rx_queue; 1684 rx_queue = NULL; 1685 rx_queue_tail = NULL; 1686 NEXT_HOP_UNLOCK; 1687 1688 while (local_rx_queue != NULL) { 1689 pkt = local_rx_queue; 1690 local_rx_queue = local_rx_queue->next; 1691 pkt->next = NULL; 1692 1693 /* See if packet should go up local stack */ 1694 NEXT_HOP_DEST_KEY_GET(pkt->pkt_data[0].data, key); 1695 is_bcast = !CPUDB_KEY_BCAST_COMPARE(key); 1696 is_neighbor = !CPUDB_KEY_NEIGHBOR_COMPARE(key); 1697 is_directed = !(is_bcast || is_neighbor); 1698 is_directed_local = !CPUDB_KEY_COMPARE(key, next_hop_local_key); 1699 1700 LOG_DEBUG(BSL_LS_TKS_NH, 1701 (BSL_META("nh pkt " CPUDB_KEY_FMT 1702 ". bcast %d dir %d local %d neighbor %d\n"), 1703 CPUDB_KEY_DISP(key), is_bcast, is_directed, 1704 is_directed_local, is_neighbor)); 1705 1706 /* Transmit the data first in case application steals the packet */ 1707 if (is_bcast || (is_directed && !is_directed_local)) { 1708 pkt->pkt_data[0].len = pkt->pkt_len; 1709 pkt->flags |= BCM_TX_CRC_REGEN; 1710 (void)_packet_send(pkt, TRUE, NULL, NULL); 1711 } 1712 1713 if (is_bcast || is_directed_local || is_neighbor) { 1714 _packet_up_stack(pkt); 1715 } 1716 1717 _rx_packet_free(pkt); 1718 } 1719 } 1720 } 1721 1722 1723 /**************************************************************** 1724 * 1725 * RX queue management 1726 */ 1727 1728 /* 1729 * Allocate an RX packet and copy data from another packet 1730 */ 1731 static INLINE bcm_pkt_t * 1732 _rx_packet_alloc(bcm_pkt_t *pkt) 1733 { 1734 bcm_pkt_t *new_pkt; 1735 1736 NEXT_HOP_LOCK; 1737 if (rx_queue_freelist == NULL) { 1738 NEXT_HOP_UNLOCK; 1739 return NULL; 1740 } 1741 new_pkt = (bcm_pkt_t *)rx_queue_freelist; 1742 rx_queue_freelist = rx_queue_freelist->next; 1743 NEXT_HOP_UNLOCK; 1744 1745 sal_memcpy(new_pkt, pkt, sizeof(bcm_pkt_t)); 1746 new_pkt->pkt_data = &new_pkt->_pkt_data; 1747 1748 return new_pkt; 1749 } 1750 1751 /* 1752 * Free an RX packet and associated data 1753 */ 1754 static INLINE void 1755 _rx_packet_free(bcm_pkt_t *pkt) 1756 { 1757 if (pkt->alloc_ptr != NULL) { 1758 next_hop_trans_ptr->tp_data_free(pkt->rx_unit, pkt->alloc_ptr); 1759 } 1760 NEXT_HOP_LOCK; 1761 pkt->next = (bcm_pkt_t *)rx_queue_freelist; 1762 rx_queue_freelist = pkt; 1763 NEXT_HOP_UNLOCK; 1764 } 1765 1766 /* 1767 * Enqueue an RX packet; grab a packet from the rx freelist 1768 */ 1769 static INLINE int 1770 _rx_packet_enqueue(bcm_pkt_t *pkt) 1771 { 1772 bcm_pkt_t *new_pkt; 1773 1774 LOG_DEBUG(BSL_LS_TKS_NH, 1775 (BSL_META("next hop rx enqueue\n"))); 1776 1777 new_pkt = _rx_packet_alloc(pkt); 1778 if (new_pkt == NULL) { 1779 LOG_INFO(BSL_LS_TKS_NH, 1780 (BSL_META("NH: no queue, resource\n"))); 1781 return BCM_E_RESOURCE; 1782 } 1783 #if defined(BCM_RXP_DEBUG) 1784 bcm_rx_pool_own(pkt->alloc_ptr, "nh_pkt_enqueue"); 1785 #endif 1786 1787 NEXT_HOP_LOCK; 1788 if (rx_queue_tail == NULL) { /* Queue is currently empty */ 1789 rx_queue = new_pkt; 1790 } else { 1791 rx_queue_tail->next = new_pkt; 1792 } 1793 rx_queue_tail = new_pkt; 1794 NEXT_HOP_UNLOCK; 1795 1796 sal_sem_give(next_hop_sem); /* Wake handling thread */ 1797 1798 return BCM_E_NONE; 1799 } 1800 1801 /**************************************************************** 1802 * 1803 * TX queue management 1804 */ 1805 1806 /* 1807 * Enqueue a TX packet 1808 */ 1809 static INLINE int 1810 _tx_packet_enqueue(bcm_pkt_t *pkt, 1811 next_hop_tx_callback_f callback, 1812 void *cookie, 1813 uint32 flags) 1814 { 1815 tx_queue_t *entry; 1816 1817 LOG_DEBUG(BSL_LS_TKS_NH, 1818 (BSL_META("next hop tx enqueue\n"))); 1819 1820 NEXT_HOP_LOCK; 1821 if (tx_queue_freelist == NULL) { 1822 NEXT_HOP_UNLOCK; 1823 return BCM_E_RESOURCE; 1824 } 1825 entry = (tx_queue_t *)tx_queue_freelist; 1826 tx_queue_freelist = entry->next; 1827 entry->pkt = pkt; 1828 entry->callback = callback; 1829 entry->cookie = cookie; 1830 entry->next = NULL; 1831 entry->flags = flags; 1832 1833 if (tx_queue_tail == NULL) { /* Queue is currently empty */ 1834 tx_queue = entry; 1835 } else { 1836 tx_queue_tail->next = entry; 1837 } 1838 tx_queue_tail = entry; 1839 NEXT_HOP_UNLOCK; 1840 1841 sal_sem_give(next_hop_sem); /* Wake handling thread */ 1842 1843 return BCM_E_NONE; 1844 } 1845 1846 /* Release a tx queue entry to the freelist */ 1847 static INLINE void 1848 _tx_queue_free(tx_queue_t *entry) 1849 { 1850 NEXT_HOP_LOCK; 1851 entry->next = tx_queue_freelist; 1852 tx_queue_freelist = entry; 1853 NEXT_HOP_UNLOCK; 1854 } 1855 1856 /**************************************************************** 1857 * 1858 * Low level transmit function 1859 * Transmit the packet or packets pointed to by pkt. 1860 * Always synchronous send from here. 1861 * Slightly different behavior if this is a received packet 1862 * that is being forwarded versus a packet from the application layer 1863 * being sent out. 1864 */ 1865 1866 STATIC int 1867 _packet_send(bcm_pkt_t *pkt, 1868 int forward, /* Is received packet being forwarded? */ 1869 next_hop_tx_callback_f callback, 1870 void *cookie) 1871 { 1872 int rv = BCM_E_NONE; 1873 int tmp_rv, i; 1874 bcm_pkt_t *cur_pkt; 1875 1876 for (i = 0; i < num_stk_ports; i++) { 1877 if (forward) { 1878 /* Since the packet is being forwarded at the next hop 1879 * level, it doesn't need to be updated at all. We do 1880 * check for duplex source port */ 1881 if (pkt->rx_unit == nh_stk_ports[i].unit && 1882 pkt->rx_port == nh_stk_ports[i].port && 1883 nh_stk_ports[i].flags & NH_FLAGS_DUPLEX) { 1884 continue; /* Skip duplex source port */ 1885 } 1886 /* Need to do minimal packet setup, esp for SL tag */ 1887 nh_pkt_final_setup(pkt, nh_stk_ports[i].unit, 1888 nh_stk_ports[i].port); 1889 } else { 1890 /* Set up the packet now that we know the unit/port */ 1891 nh_pkt_local_setup(pkt, nh_stk_ports[i].unit, 1892 nh_stk_ports[i].port); 1893 } 1894 for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) { 1895 BCM_PBMP_PORT_SET(cur_pkt->tx_pbmp, nh_stk_ports[i].port); 1896 pkt->unit = nh_stk_ports[i].unit; 1897 } 1898 1899 tmp_rv = nh_pkt_tx(pkt, NULL, NULL); 1900 if (tmp_rv < 0) { 1901 LOG_INFO(BSL_LS_TKS_NH, 1902 (BSL_META("NEXT_HOP: Failed to send to unit %d, port %d\n"), 1903 nh_stk_ports[i].unit, nh_stk_ports[i].port)); 1904 rv = tmp_rv; 1905 ++tx_error_count; 1906 } 1907 } 1908 1909 /* See if there's a callback to make for the packet */ 1910 if (callback != NULL) { 1911 callback(rv, pkt->alloc_ptr, cookie); 1912 } 1913 1914 return rv; 1915 } 1916 1917 /**************************************************************** 1918 * 1919 * Send a packet up the local stack to callbacks registered for 1920 * the port number in the packet (or -1.) 1921 */ 1922 1923 static INLINE void 1924 _packet_up_stack(bcm_pkt_t *pkt) 1925 { 1926 cpudb_key_t src_key; 1927 int idx; 1928 int mplx_num; 1929 uint8 *pkt_data; 1930 bcm_rx_t rv; 1931 int len; 1932 1933 pkt_data = pkt->pkt_data[0].data; 1934 1935 NEXT_HOP_SRC_KEY_GET(pkt_data, src_key); 1936 NEXT_HOP_MPLX_NUM_GET(pkt_data, mplx_num); 1937 1938 len = pkt->pkt_len; /* Strip CRC if not already done */ 1939 if (!(pkt->flags & BCM_RX_CRC_STRIP)) { 1940 len -= 4; 1941 } 1942 1943 NEXT_HOP_REG_LOCK; 1944 #if defined(BCM_RXP_DEBUG) 1945 bcm_rx_pool_own(pkt->alloc_ptr, "nh_app"); 1946 #endif 1947 for (idx = 0; idx < num_cb_ctl; idx++) { 1948 if (cb_ctl[idx].mplx_num == mplx_num || 1949 cb_ctl[idx].mplx_num == -1) { 1950 rv = cb_ctl[idx].callback(src_key, mplx_num, 1951 pkt->rx_unit, 1952 pkt->rx_port, 1953 pkt_data, 1954 len, 1955 cb_ctl[idx].cookie); 1956 if (rv == BCM_RX_HANDLED) { 1957 break; 1958 } else if (rv == BCM_RX_HANDLED_OWNED) { 1959 pkt->alloc_ptr = NULL; 1960 break; 1961 } 1962 } 1963 } 1964 NEXT_HOP_REG_UNLOCK; 1965 } 1966 1967 1968 /**************************************************************** 1969 * 1970 * Callback function to register to receive packets 1971 * next_hop_rx_callback Get packets from RX 1972 * 1973 * Plus helper functions 1974 * _rx_key_seq_seen Handle seq num for the key 1975 * _nh_key_find Look for key in local DB 1976 * _nh_key_add Add a key to the local DB 1977 * _key_lru_replace Replace least recently used key 1978 */ 1979 1980 static INLINE int _rx_key_seq_seen(cpudb_key_t key, uint16 seq_num); 1981 static INLINE int _seq_num_seen(int cpu_idx, uint16 seq_num); 1982 static INLINE int _seq_num_add(int cpu_idx, uint16 seq_num); 1983 1984 /* 1985 * Function: 1986 * next_hop_rx_callback 1987 * Purpose: 1988 * Packet handler for unclassified RX packets 1989 * Parameters: 1990 * unit - On which unit was pkt received 1991 * pkt - The received packet 1992 * cookie - Ignored 1993 * Returns: 1994 * bcm_rx_t: Indication if packet handled/stolen 1995 * Notes: 1996 */ 1997 1998 STATIC bcm_rx_t 1999 next_hop_rx_callback(int unit, bcm_pkt_t *pkt, void *cookie) 2000 { 2001 uint16 pkt_type; /* From the lower level, extracted from packet */ 2002 cpudb_key_t key; /* From next hop header */ 2003 uint16 seq_num; /* next hop sequence for detecting "seen" pkts */ 2004 int seen; 2005 2006 LOG_DEBUG(BSL_LS_TKS_NH, 2007 (BSL_META("NEXT_HOP pkt in\n"))); 2008 2009 if (next_hop_lock == NULL || !setup_done || 2010 nh_thread_id == SAL_THREAD_ERROR) { 2011 LOG_INFO(BSL_LS_TKS_NH, 2012 (BSL_META_U(unit, 2013 "exit: %p, %d, %p\n"), 2014 next_hop_lock, setup_done, nh_thread_id)); 2015 return BCM_RX_NOT_HANDLED; 2016 } 2017 2018 /* Is this a next hop packet? */ 2019 if (!nh_tx_pkt_recognize(pkt->pkt_data[0].data, &pkt_type)) { 2020 LOG_DEBUG(BSL_LS_TKS_NH, 2021 (BSL_META("NEXT_HOP pkt not recognized\n"))); 2022 return BCM_RX_NOT_HANDLED; 2023 } 2024 if (pkt_type != NEXT_HOP_PKT_TYPE) { /* Is this an NEXT_HOP packet */ 2025 LOG_INFO(BSL_LS_TKS_NH, 2026 (BSL_META_U(unit, 2027 "NEXT_HOP pkt not proper type\n"))); 2028 return BCM_RX_NOT_HANDLED; 2029 } 2030 2031 /* Good packet, have we seen it before? */ 2032 NEXT_HOP_SRC_KEY_GET(pkt->pkt_data[0].data, key); 2033 /* Is local CPU the source of the packet? */ 2034 if (!CPUDB_KEY_COMPARE(key, next_hop_local_key)) { 2035 LOG_DEBUG(BSL_LS_TKS_NH, 2036 (BSL_META("NEXT_HOP source is local\n"))); 2037 return BCM_RX_HANDLED; 2038 } 2039 2040 /* Check for a seq num we've seen before. */ 2041 NEXT_HOP_SEQ_NUM_GET(pkt->pkt_data[0].data, seq_num); 2042 LOG_DEBUG(BSL_LS_TKS_NH, 2043 (BSL_META("From KEY " CPUDB_KEY_FMT "; seq %d\n"), 2044 CPUDB_KEY_DISP(key), seq_num)); 2045 NEXT_HOP_LOCK; 2046 seen = _rx_key_seq_seen(key, seq_num); 2047 NEXT_HOP_UNLOCK; 2048 if (seen) { /* Really, (seen == TRUE || seen < 0) */ 2049 return BCM_RX_HANDLED; 2050 } 2051 2052 /* Try to enqueue the packet for later retrx and passing up stack */ 2053 if (_rx_packet_enqueue(pkt) < 0) { 2054 LOG_DEBUG(BSL_LS_TKS_NH, 2055 (BSL_META("NH: Dropped RX pkt %d\n"), seq_num)); 2056 ++rx_pkt_drop_count; 2057 return BCM_RX_HANDLED; 2058 } 2059 2060 return BCM_RX_HANDLED_OWNED; 2061 } 2062 2063 /**************************************************************** 2064 * 2065 * Sequence number checking and CPU key management 2066 */ 2067 2068 /* 2069 * Check the CPU key/seq num pair; returns TRUE if seen before. 2070 */ 2071 static INLINE int 2072 _rx_key_seq_seen(cpudb_key_t key, uint16 seq_num) 2073 { 2074 int local_idx; /* Index in local DB of CPU key */ 2075 2076 local_idx = _nh_key_find(key); 2077 if (local_idx < 0) { /* New CPU key; hence new packet */ 2078 local_idx = _nh_key_add(key); 2079 if (local_idx < 0) { 2080 LOG_INFO(BSL_LS_TKS_NH, 2081 (BSL_META("NEXT_HOP key rsrc err\n"))); 2082 return BCM_E_RESOURCE; 2083 } 2084 } else { 2085 LOG_DEBUG(BSL_LS_TKS_NH, 2086 (BSL_META("Local idx %d\n"), local_idx)); 2087 if (_seq_num_seen(local_idx, seq_num)) { /* Seen it */ 2088 LOG_DEBUG(BSL_LS_TKS_NH, 2089 (BSL_META("NEXT_HOP pkt previously seen\n"))); 2090 return TRUE; 2091 } 2092 } 2093 2094 _seq_num_add(local_idx, seq_num); 2095 return FALSE; 2096 } 2097 2098 2099 /* 2100 * _seq_num_seen: 2101 * 2102 * Boolean: Returns true if the pair (cpu_idx, seq_num) has 2103 * been seen before; 2104 * 2105 * This is currently implemented as a simple circular queue. 2106 * The main assumption is that the queue is big enough that by 2107 * the time an entry is overwritten, the corresponding packet will 2108 * not be seen again. Also, that the queue is small enough that 2109 * wrap around of sequence numbers isn't a problem. 2110 */ 2111 2112 static INLINE int 2113 _seq_num_seen(int local_idx, uint16 seq_num) 2114 { 2115 int sn_idx; 2116 int i; 2117 2118 sn_idx = cpu_seq_list[local_idx]->seq_num_last; 2119 for (i = 0; i < NEXT_HOP_SEQ_NUM_TRACK; i++) { 2120 if (cpu_seq_list[local_idx]->seq_nums_seen[sn_idx] == seq_num) { 2121 return TRUE; 2122 } 2123 if (--sn_idx < 0) { 2124 sn_idx = NEXT_HOP_SEQ_NUM_TRACK - 1; 2125 } 2126 } 2127 2128 return FALSE; 2129 } 2130 2131 /* 2132 * _seq_num_add: 2133 * 2134 * Add a sequence number to the "seen" list for the given CPU index. 2135 * Assumes lock held. 2136 * 2137 * This is currently implemented as a simple circular queue. See above 2138 */ 2139 2140 static INLINE int 2141 _seq_num_add(int local_idx, uint16 seq_num) 2142 { 2143 int last; 2144 2145 if (++(cpu_seq_list[local_idx]->seq_num_last) >= 2146 NEXT_HOP_SEQ_NUM_TRACK) { 2147 cpu_seq_list[local_idx]->seq_num_last = 0; 2148 } 2149 2150 last = cpu_seq_list[local_idx]->seq_num_last; 2151 cpu_seq_list[local_idx]->seq_nums_seen[last] = seq_num; 2152 2153 return FALSE; 2154 } 2155 2156 2157 /* Look for CPU key in local DB */ 2158 2159 static INLINE int 2160 _nh_key_find(cpudb_key_t key) 2161 { 2162 int i; 2163 2164 for (i = 0; i < CPU_KEY_MAX; i++) { 2165 if (cpu_seq_list[i] != NULL && 2166 !CPUDB_KEY_COMPARE(cpu_seq_list[i]->key, key)) { 2167 cpu_seq_list[i]->last_ref = sal_time(); 2168 return i; 2169 } 2170 } 2171 2172 return BCM_E_NOT_FOUND; 2173 } 2174 2175 /* 2176 * Look for least recently used CPU key in DB and replace it; assumes all 2177 * entries are occupied 2178 */ 2179 2180 static INLINE int 2181 _key_lru_replace(cpudb_key_t key) 2182 { 2183 int idx, i; 2184 2185 for (idx = 0, i = 1; i < CPU_KEY_MAX; i++) { 2186 if (cpu_seq_list[i]->last_ref < cpu_seq_list[idx]->last_ref) { 2187 idx = i; 2188 } 2189 } 2190 2191 2192 sal_memset(cpu_seq_list[idx], 0, sizeof(cpu_seq_list_t)); 2193 CPUDB_KEY_COPY(cpu_seq_list[idx]->key, key); 2194 /* see _nh_key_add() below for seq_num_last initialization */ 2195 cpu_seq_list[idx]->seq_num_last = NEXT_HOP_SEQ_NUM_TRACK - 1; 2196 cpu_seq_list[idx]->last_ref = sal_time(); 2197 2198 return idx; 2199 } 2200 2201 /* Add a CPU key to local DB. Check LRU mode if table is full */ 2202 2203 static INLINE int 2204 _nh_key_add(cpudb_key_t key) 2205 { 2206 int i; 2207 int rv = BCM_E_NONE; 2208 2209 for (i = 0; i < CPU_KEY_MAX; i++) { 2210 if (cpu_seq_list[i] == NULL) { 2211 cpu_seq_list[i] = sal_alloc(sizeof(cpu_seq_list_t), "seq_num"); 2212 if (cpu_seq_list[i] == NULL) { 2213 rv = BCM_E_MEMORY; 2214 } else { 2215 sal_memset(cpu_seq_list[i], 0, sizeof(cpu_seq_list_t)); 2216 CPUDB_KEY_COPY(cpu_seq_list[i]->key, key); 2217 /* init seq_num_last to the end so the preincrement in 2218 _seq_num_add() starts at 0 */ 2219 cpu_seq_list[i]->seq_num_last = NEXT_HOP_SEQ_NUM_TRACK - 1; 2220 cpu_seq_list[i]->last_ref = sal_time(); 2221 } 2222 break; 2223 } 2224 } 2225 2226 /* Couldn't find an entry */ 2227 if (rv == BCM_E_NONE && i >= CPU_KEY_MAX) { 2228 if (key_lru_replace_enable) { 2229 i = _key_lru_replace(key); 2230 } else { 2231 rv = BCM_E_MEMORY; 2232 } 2233 } 2234 2235 return BCM_SUCCESS(rv) ? i : rv; 2236 } 2237 2238 /**************************************************************** 2239 * 2240 * Get a new sequence number to put in a packet 2241 */ 2242 2243 /* Get a sequence number for NEXT_HOP pkts; Will never return 0 */ 2244 static INLINE uint16 2245 next_hop_seq_num_get(void) 2246 { 2247 static uint16 _seq_num; 2248 uint16 new_seq_num; 2249 2250 NEXT_HOP_LOCK; 2251 if (++_seq_num >= NEXT_HOP_SEQ_NUM_MAX) { 2252 _seq_num = 1; 2253 } 2254 new_seq_num = _seq_num; 2255 NEXT_HOP_UNLOCK; 2256 2257 return new_seq_num; 2258 } 2259 2260 #if defined(BROADCOM_DEBUG) 2261 void 2262 next_hop_dump(void) 2263 { 2264 int cos, int_prio; 2265 2266 cos = CPUTRANS_COS_GET(nh_cos); 2267 if (nh_cos & CPUTRANS_INT_PRIO_VALID) { 2268 int_prio = CPUTRANS_INT_PRIO_GET(nh_cos); 2269 } else { 2270 int_prio = cos; 2271 } 2272 2273 LOG_CLI((BSL_META("Next Hop\n"))); 2274 2275 LOG_CLI((BSL_META(" %s. %s.\n"), 2276 (next_hop_lock != NULL) ? "Initialized":"Not initialized", 2277 setup_done ? "Running":"Not running")); 2278 LOG_CLI((BSL_META(" thread_priority = %d\n"), 2279 next_hop_thread_priority)); 2280 LOG_CLI((BSL_META(" rx_priority = %d\n"), 2281 next_hop_rx_priority)); 2282 LOG_CLI((BSL_META(" rx_queue_size = %d\n"), 2283 next_hop_rx_queue_size)); 2284 LOG_CLI((BSL_META(" tx_queue_size = %d\n"), 2285 next_hop_tx_queue_size)); 2286 LOG_CLI((BSL_META(" mtu = %d\n"), 2287 next_hop_mtu)); 2288 LOG_CLI((BSL_META(" vlan = %d\n"), 2289 nh_vlan)); 2290 LOG_CLI((BSL_META(" cos = %d\n"), 2291 cos)); 2292 LOG_CLI((BSL_META(" int_prio = %d\n"), 2293 int_prio)); 2294 LOG_CLI((BSL_META(" local_key = " CPUDB_KEY_FMT "\n"), 2295 CPUDB_KEY_DISP(next_hop_local_key))); 2296 LOG_CLI((BSL_META(" rx_pkt_drop_count = %d\n"), 2297 rx_pkt_drop_count)); 2298 LOG_CLI((BSL_META(" tx_error_count = %d\n"), 2299 tx_error_count)); 2300 2301 return; 2302 } 2303 #endif /* BROADCOM_DEBUG */