openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

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, &reg);
   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 */