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

cputrans.c (29683B)


      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:        cputrans.c
      8  * Purpose:     TX and RX packet management routines
      9  * Requires:    bcm_trans_ptr_t structure
     10  */
     11 
     12 #include <shared/bsl.h>
     13 
     14 #include <appl/cputrans/cputrans.h>
     15 
     16 #include <sal/core/sync.h>
     17 #include <sal/core/libc.h>
     18 #include <shared/alloc.h>
     19 
     20 /* Currently used for sal_dma_alloc */
     21 #include <sal/appl/sal.h>
     22 
     23 #include <bcm/error.h>
     24 #include <bcm/pkt.h>
     25 #include <bcm/rx.h>
     26 #include <bcm_int/control.h>
     27 
     28 #include "t_util.h"
     29 
     30 /* MUTEX used by both RX and TX allocation routines */
     31 
     32 static sal_mutex_t              ct_lock;
     33 #define CPUTRANS_LOCK           sal_mutex_take(ct_lock, sal_sem_FOREVER)
     34 #define CPUTRANS_UNLOCK         sal_mutex_give(ct_lock)
     35 
     36 /****************************************************************
     37  *
     38  * TX packet setup and allocation
     39  *
     40  * There is one free list of TX packets.  Each TX packet has two
     41  * data pointers allocated and one header buffer allocated.  If the
     42  * header buffer is not used, it is placed in the second pointer
     43  * block and the number of blocks for the packet (blk_count) is
     44  * set to 1.
     45  *
     46  *
     47  * Static variables:
     48  *     _tx_trans_ptr        Local transport driver list
     49  *     _tx_setup_done       Boolean; packets allocated?
     50  *     _tx_pkt_free_list    Head of TX packet free list
     51  *     _tx_pkts             Allocation pointer for all TX packets
     52  *     _tx_pkt_blks         Allocation pointer for all TX data pointer blocks
     53  *     _tx_header_data      Allocation pointer for all header data
     54  */
     55 
     56 static bcm_trans_ptr_t          _tx_trans_ptr;
     57 #define INIT_DONE               (ct_lock != NULL)
     58 #define CHECK_INIT if (!INIT_DONE) BCM_IF_ERROR_RETURN(_do_init())
     59 
     60 static int                      _tx_setup_done;
     61 static bcm_pkt_t               *_tx_pkt_free_list;
     62 static bcm_pkt_t               *_tx_pkts;
     63 static bcm_pkt_blk_t           *_tx_pkt_blks;
     64 static uint8                   *_tx_header_data;
     65 
     66 /* This indicates an error which may cause internal corruption has been seen */
     67 volatile int cputrans_error_count;
     68 #define CPUTRANS_ERROR_INCR (++cputrans_error_count)
     69 
     70 /* Do local initalization */
     71 STATIC int
     72 _do_init(void)
     73 {
     74     ct_lock = sal_mutex_create("ct_tx_lock");
     75     if (ct_lock == NULL) {
     76         return BCM_E_MEMORY;
     77     }
     78 
     79     return BCM_E_NONE;
     80 }
     81 
     82 
     83 /* Group allocate and free macros */
     84 
     85 #define _ALLOC_CHECK(id, bytes, _cleanup, _str)         \
     86     do {                                                \
     87         (id) = sal_alloc((bytes), (_str));              \
     88         if ((id) == NULL) {                             \
     89             (_cleanup)();                               \
     90             CPUTRANS_UNLOCK;                            \
     91             return BCM_E_MEMORY;                        \
     92         }                                               \
     93         sal_memset((id), 0, bytes);                     \
     94     } while (0)
     95 
     96 #define _FREE_CHECK(id)                                 \
     97     do {                                                \
     98         if ((id) != NULL) {                             \
     99             sal_free(id);                               \
    100             (id) = NULL;                                \
    101         }                                               \
    102     } while (0)
    103 
    104 /*
    105  * Function:
    106  *      cputrans_tx_setup_done
    107  * Purpose:
    108  *      Indicate if setup has been done for cputrans subsys
    109  * Returns:
    110  *      Boolean:  True if setup has been done
    111  */
    112 
    113 int
    114 cputrans_tx_setup_done(void)
    115 {
    116     return _tx_setup_done;
    117 }
    118 
    119 #define _TX_RESERVE_MAX   BCM_COS_MAX
    120 static int tx_reserve[_TX_RESERVE_MAX + 1] = CPUTRANS_TX_RESERVE_DEFAULT;
    121 static int tx_pkts_avail;
    122 static int tx_pool_size;
    123 
    124 /*
    125  * Function:
    126  *      cputrans_tx_pkt_setup
    127  * Purpose:
    128  *      Setup the CPU transport tx packet pool
    129  * Parameters:
    130  *      pkt_pool_size    - Number of packets to use in pool;
    131  *      trans_ptr        - Pointer to transport driver (for salloc/sfree).
    132  * Returns:
    133  *      BCM_E_XXX
    134  * Notes:
    135  *      If pkt_pool_size <= 0, use default value.
    136  */
    137 
    138 int
    139 cputrans_tx_pkt_setup(int pkt_pool_size, bcm_trans_ptr_t *trans_ptr)
    140 {
    141     int bytes;
    142     int i;
    143     bcm_pkt_t *pkt;
    144 
    145     CHECK_INIT;
    146     if (_tx_setup_done) {
    147         return BCM_E_BUSY;
    148     }
    149 
    150     if (trans_ptr->tp_data_alloc == NULL ||
    151                   trans_ptr->tp_data_free == NULL) {
    152         return BCM_E_PARAM;
    153     }
    154     sal_memcpy(&_tx_trans_ptr, trans_ptr, sizeof(bcm_trans_ptr_t));
    155 
    156     if (pkt_pool_size <= 0) {
    157         pkt_pool_size = CPUTRANS_PKT_POOL_SIZE_DEFAULT;
    158     }
    159     tx_pkts_avail = pkt_pool_size;
    160     tx_pool_size = pkt_pool_size;
    161 
    162     /* Allocate static data for TX queues */
    163     bytes = pkt_pool_size * CPUTRANS_HEADER_BYTES;
    164     _tx_header_data = sal_dma_alloc(bytes, "CT_HDR");
    165     if (_tx_header_data == NULL) {
    166         cputrans_tx_pkt_cleanup();
    167         return BCM_E_MEMORY;
    168     }
    169 
    170     /* Allocate tx pkts then data block ptrs, two per packet */
    171     bytes = pkt_pool_size * sizeof(bcm_pkt_t);
    172     _ALLOC_CHECK(_tx_pkts, bytes, cputrans_tx_pkt_cleanup, "ct_tx_pkts");
    173 
    174     bytes = pkt_pool_size * 2 * sizeof(bcm_pkt_blk_t);
    175     _ALLOC_CHECK(_tx_pkt_blks, bytes, cputrans_tx_pkt_cleanup, "ct_pkt_blks");
    176 
    177     /* Give each tx packet a header buffer and link to freelist */
    178     for (i = 0; i < pkt_pool_size; i++) {
    179         _tx_pkts[i].next = &_tx_pkts[i + 1];
    180         pkt = &_tx_pkts[i];
    181         pkt->pkt_data = &_tx_pkt_blks[2 * i];
    182         pkt->pkt_data[0].data = &_tx_header_data[i * CPUTRANS_HEADER_BYTES];
    183         pkt->pkt_data[0].len = CPUTRANS_HEADER_BYTES;
    184         pkt->flags = BCM_TX_CRC_APPEND;
    185     }
    186     _tx_pkts[i - 1].next = NULL;
    187     _tx_pkt_free_list = _tx_pkts;
    188 
    189     _tx_setup_done = TRUE;
    190 
    191     return BCM_E_NONE;
    192 }
    193 
    194 /*
    195  * Function:
    196  *      cputrans_tx_pkt_cleanup
    197  * Purpose:
    198  *      Cleanup and deallocate static packet data for CPU transport
    199  */
    200 
    201 void
    202 cputrans_tx_pkt_cleanup(void)
    203 {
    204     if (!INIT_DONE) {
    205         return;
    206     }
    207 
    208     CPUTRANS_LOCK;
    209 
    210     if (_tx_header_data != NULL) {
    211         sal_dma_free(_tx_header_data);
    212         _tx_header_data = NULL;
    213     }
    214 
    215     _FREE_CHECK(_tx_pkts);
    216     _FREE_CHECK(_tx_pkt_blks);
    217     _tx_setup_done = FALSE;
    218 
    219     CPUTRANS_UNLOCK;
    220 }
    221 
    222 /* Grab (extra_pkt_count + 1) packets from free list; return NULL if fails */
    223 static INLINE bcm_pkt_t *
    224 grab_packets(int extra_pkt_count, int cos, uint32 ct_flags)
    225 {
    226     int _i;
    227     bcm_pkt_t *_pkt;
    228     bcm_pkt_t *first_pkt;
    229 
    230     if (cos > _TX_RESERVE_MAX) {
    231         return NULL;
    232     }
    233 
    234     CPUTRANS_LOCK;
    235     if (_tx_pkt_free_list == NULL) {
    236         CPUTRANS_UNLOCK;
    237         return NULL;
    238     }
    239     if (cos >= 0) {
    240         if (tx_pkts_avail - (extra_pkt_count + 1) < tx_reserve[cos]) {
    241             CPUTRANS_UNLOCK;
    242             return NULL;
    243         }
    244     }
    245     _pkt = first_pkt = _tx_pkt_free_list;
    246     _pkt->flags = BCM_TX_CRC_APPEND;
    247     for (_i = 0; _i < extra_pkt_count; _i++) {
    248         if (_pkt->next == NULL) {
    249             CPUTRANS_UNLOCK;
    250             return NULL;
    251         }
    252         _pkt = _pkt->next;
    253         _pkt->flags = BCM_TX_CRC_APPEND;
    254     }
    255     /* pkt now points to last pkt in alloc chain. */
    256     _tx_pkt_free_list = _pkt->next;
    257     _pkt->next = NULL;
    258     first_pkt->_last_pkt = _pkt;
    259     tx_pkts_avail -= (extra_pkt_count + 1);
    260 
    261     if (ct_flags & CPUTRANS_CRC_REGEN) {
    262         _pkt->flags = BCM_TX_CRC_REGEN;
    263     } /* else, leave as append */
    264 
    265     CPUTRANS_UNLOCK;
    266 
    267     return first_pkt;
    268 }
    269 
    270 /* See notes for cputrans_tx_alloc_empty_set below */
    271 
    272 /*
    273  * Function:
    274  *      cputrans_tx_alloc_limit_set/get
    275  * Purpose:
    276  *      Set the level at which the tx pkt alloc pool appears empty to a COS
    277  * Parameters:
    278  *      cos         - The COS whose low marker should be set
    279  *      reserve     - Keep this many pkts available in pool; see Notes.
    280  * Returns:
    281  *      BCM_E_XXX
    282  * Notes:
    283  *      When an allocation request is made and the COS override flag
    284  *      is set in the ct_flags, the COS is extracted from the flags
    285  *      and a "pkt reserve" is checked.  This represents the number of
    286  *      packets that must remain in the queue after the allocation.  If
    287  *      not enough packets remain, the allocation will fail and return NULL.
    288  *
    289  *      Normally, reserve is set to 0 for the highest COS.  Allocations
    290  *      can be disabled for a COS by setting reserve >= pkt_pool_size
    291  *      (though this is not recommended).
    292  */
    293 
    294 int
    295 cputrans_tx_alloc_limit_set(int cos, int reserve)
    296 {
    297     if (cos < 0 || cos > _TX_RESERVE_MAX) {
    298         return BCM_E_PARAM;
    299     }
    300 
    301     tx_reserve[cos] = reserve;
    302 
    303     return BCM_E_NONE;
    304 }
    305 
    306 int
    307 cputrans_tx_alloc_limit_get(int cos, int *reserve)
    308 {
    309     if (cos < 0 || cos > _TX_RESERVE_MAX) {
    310         return BCM_E_PARAM;
    311     }
    312 
    313     *reserve = tx_reserve[cos];
    314 
    315     return BCM_E_NONE;
    316 }
    317 
    318 /*
    319  * Function:
    320  *      cputrans_tx_pkt_alloc
    321  * Purpose:
    322  *      Allocate a packet from the CPU trans pool
    323  * Parameters:
    324  *      pkt_buf     - Pointer to data to link to packet
    325  *      len         - Length of packet buffer
    326  *      ct_flags    - Flags for pkt:  Mainly
    327  *                    NO_HEADER_ALLOC:  The header space is in pkt_buf
    328  *                    CPUTRANS_COS_OVERRIDE: cos to check for packet allocation
    329  * Returns:
    330  *      Pointer to packet or NULL if failed.
    331  * Notes:
    332  *      Whether or not the header is in a separate block is indicated
    333  *      by the pkt->blk_count value.
    334  *         blk_count == 1 => Header is at start of pkt_buf in pkt_data[0]
    335  *         blk_count == 2 => Header is pkt_data[0], pkt_buf in pkt_data[1]
    336  */
    337 
    338 bcm_pkt_t *
    339 cputrans_tx_pkt_alloc(uint8 *pkt_buf, int len, uint32 ct_flags)
    340 {
    341     bcm_pkt_t *pkt;
    342     int pkt_blk;
    343     int cos = -1;
    344 
    345     if (!_tx_setup_done) {
    346         return NULL;
    347     }
    348 
    349     if (ct_flags & CPUTRANS_COS_OVERRIDE) {
    350         cos = CPUTRANS_COS_GET(ct_flags);
    351     }
    352     /* Will return NULL if error */
    353     pkt = grab_packets(0, cos, ct_flags);
    354     if (pkt == NULL) {
    355         return NULL;
    356     }
    357 
    358     if (ct_flags & CPUTRANS_NO_HEADER_ALLOC) {
    359         /* Save header buffer in data 1 */
    360         sal_memcpy(&pkt->pkt_data[1], &pkt->pkt_data[0],
    361                    sizeof(bcm_pkt_blk_t));
    362         pkt_blk = 0;
    363         pkt->blk_count = 1;
    364     } else {  /* Header in first block */
    365         pkt_blk = 1;
    366         pkt->blk_count = 2;
    367     }
    368 
    369     /* Put data pointers in proper place. */
    370     pkt->pkt_data[pkt_blk].data = pkt_buf;
    371     pkt->pkt_data[pkt_blk].len = len;
    372     /* Mark as a single packet, not in a linked list */
    373     pkt->next = NULL;
    374 
    375     return pkt;
    376 }
    377 
    378 /*
    379  * Function:
    380  *      cputrans_tx_pkt_list_alloc
    381  * Purpose:
    382  *      Allocate a list of packets and segment the packet buffer
    383  * Parameters:
    384  *      pkt_buf     - Pointer to data to link to packet
    385  *      len         - Length of packet buffer
    386  *      seg_len     - Number of bytes in each segment, not including header
    387  *      ct_flags    - Flags for pkt:  Mainly
    388  *                    NO_HEADER_ALLOC:  The header space is in pkt_buf
    389  *                    CPUTRANS_COS_OVERRIDE: cos to check for packet allocation
    390  *      num_pkt     - (OUT) How many packets allocated
    391  * Returns:
    392  *      BCM_E_XXX
    393  * Notes:
    394  *      The last packet pointer is stored in _last_pkt of the first
    395  *      packet.  The "next" pointers should not be
    396  *      altered in the list.  CPUTRANS headers are always provided
    397  *      for the second, third... packets.  The NO_HEADER_ALLOC flag
    398  *      only applies to the first buffer.
    399  *
    400  *      The MTU for the channel must be at least seg_len + header_bytes.
    401  *
    402  *      If NO_HEADER_ALLOC is set, then the length of the first chunk
    403  *      of data taken from pkt_buf is (seg_len + header_bytes).  This way,
    404  *      there are always seg_len bytes of payload per packet.
    405  */
    406 
    407 bcm_pkt_t *
    408 cputrans_tx_pkt_list_alloc(uint8 *pkt_buf, int len, int seg_len,
    409                            uint32 ct_flags, int *num_segs)
    410 {
    411     bcm_pkt_t *first_pkt;
    412     bcm_pkt_t *pkt;
    413     int extra_pkt_count;
    414     int offset;
    415     int i;
    416     int pkt_blk = 0;
    417     int len_fld_val = 0x601;  /* What goes in the pkt's length fld */
    418     int cos = -1;
    419 
    420     if (!_tx_setup_done) {
    421         return NULL;
    422     }
    423 
    424     extra_pkt_count = (len - 1) / seg_len;   /* Packets needed beyond first */
    425 
    426     if (ct_flags & CPUTRANS_COS_OVERRIDE) {
    427         cos = CPUTRANS_COS_GET(ct_flags);
    428     }
    429 
    430     /* Will return NULL if error */
    431     first_pkt = grab_packets(extra_pkt_count, cos, ct_flags);
    432     if (first_pkt == NULL) {
    433         return NULL;
    434     }
    435 
    436     if (num_segs != NULL) {
    437         *num_segs = extra_pkt_count + 1;
    438     }
    439 
    440     offset = seg_len;   /* Where second packet starts in payload */
    441     if (ct_flags & CPUTRANS_NO_HEADER_ALLOC) {
    442         /* Save header buffer in data 1 */
    443         sal_memcpy(&first_pkt->pkt_data[1], &first_pkt->pkt_data[0],
    444                    sizeof(bcm_pkt_blk_t));
    445         pkt_blk = 0;
    446         first_pkt->blk_count = 1;
    447         offset += CPUTRANS_HEADER_BYTES;
    448     } else {  /* Header in first block */
    449         pkt_blk = 1;
    450         first_pkt->blk_count = 2;
    451     }
    452 
    453     /* Put data pointers in proper place. */
    454     first_pkt->pkt_data[pkt_blk].data = pkt_buf;
    455     first_pkt->call_back = NULL;
    456     if (offset >= len) {  /* Only one segment needed */
    457         first_pkt->pkt_data[pkt_blk].len = len;
    458         PACK_SHORT(&first_pkt->pkt_data[0].data[CPUTRANS_LEN_OFS],
    459                    len_fld_val);
    460     } else {              /* Extra packets */
    461         first_pkt->pkt_data[pkt_blk].len = offset;
    462         PACK_SHORT(&first_pkt->pkt_data[0].data[CPUTRANS_LEN_OFS],
    463                    len_fld_val);
    464         /* Segment the rest of the packet */
    465         pkt = first_pkt->next;
    466         for (i = 0; i < extra_pkt_count - 1; i++) { /* Not first or last */
    467             pkt->pkt_data[1].data = &pkt_buf[offset];
    468             pkt->pkt_data[1].len = seg_len;
    469             PACK_SHORT(&first_pkt->pkt_data[0].data[CPUTRANS_LEN_OFS],
    470                        len_fld_val);
    471             pkt->blk_count = 2;
    472             offset += seg_len;
    473             pkt = pkt->next;
    474             pkt->call_back = NULL;
    475         }
    476 
    477         /* Take care of last pkt (pointed to by pkt) */
    478         pkt->pkt_data[1].data = &pkt_buf[offset];
    479         pkt->pkt_data[1].len = len - offset;
    480         PACK_SHORT(&first_pkt->pkt_data[0].data[CPUTRANS_LEN_OFS],
    481                    len_fld_val);
    482         pkt->blk_count = 2;
    483         pkt->call_back = NULL;
    484     }
    485     first_pkt->alloc_ptr = pkt_buf;
    486     if (first_pkt->next == first_pkt) {
    487         LOG_INFO(BSL_LS_TKS_CTPKT,
    488                  (BSL_META("CT pkt list alloc: Internal corruption in pointers\n")));
    489         CPUTRANS_ERROR_INCR;
    490     }
    491 
    492     return first_pkt;
    493 }
    494 
    495 
    496 /*
    497  * Function:
    498  *      cputrans_tx_pkt_list_free
    499  * Purpose:
    500  *      Free a list of packets allocated by cputrans_tx_pkt_list_alloc
    501  * Parameters:
    502  *      pkt         - Pointer to start of list
    503  * Returns:
    504  *      BCM_E_XXX
    505  * Notes:
    506  *      Will work for a single packet as well, as long as _last_pkt
    507  *      points to self.
    508  */
    509 
    510 void
    511 cputrans_tx_pkt_list_free(bcm_pkt_t *pkt)
    512 {
    513     bcm_pkt_t *last_pkt, *t_pkt;
    514     int count = 0;
    515 
    516     if (pkt == NULL) {
    517         LOG_INFO(BSL_LS_TKS_CTPKT,
    518                  (BSL_META("CT pkt list free: Packet NULL\n")));
    519         return;
    520     }
    521     if (pkt->next == pkt) {
    522         LOG_INFO(BSL_LS_TKS_CTPKT,
    523                  (BSL_META("CT pkt list free: Circular list\n")));
    524         pkt->next = NULL;
    525         pkt->_last_pkt = pkt;
    526         CPUTRANS_ERROR_INCR;
    527     }
    528     if (pkt->_last_pkt == NULL) {
    529         LOG_INFO(BSL_LS_TKS_CTPKT,
    530                  (BSL_META("CT pkt list free: No last pkt\n")));
    531         pkt->_last_pkt = pkt;
    532         CPUTRANS_ERROR_INCR;
    533     }
    534     /* Check if header is saved in pkt block 1 */
    535     if (pkt->blk_count == 1) {
    536         sal_memcpy(&pkt->pkt_data[0], &pkt->pkt_data[1],
    537                    sizeof(bcm_pkt_blk_t));
    538     }
    539 
    540     for (t_pkt = pkt; t_pkt != NULL; t_pkt = t_pkt->next) {
    541         if (count++ > tx_pool_size) {
    542             LOG_INFO(BSL_LS_TKS_CTPKT,
    543                      (BSL_META("CT pkt list free: List too long\n")));
    544             CPUTRANS_ERROR_INCR;
    545             return;
    546         }
    547     }
    548 
    549     last_pkt = pkt->_last_pkt;
    550     CPUTRANS_LOCK;
    551     last_pkt->next = _tx_pkt_free_list;
    552     _tx_pkt_free_list = pkt;
    553     tx_pkts_avail += count;
    554     CPUTRANS_UNLOCK;
    555 }
    556 
    557 
    558 /****************************************************************
    559  *
    560  * RX Packet Setup and Allocation
    561  *
    562  * RX packets are set up to hold multiple packet blocks for
    563  * reassembly.  RX packets are not given data pointers; it
    564  * is expected that the data will be gotten from the RX call
    565  * or allocated by the application using these packets.
    566  *
    567  * On setup, multiple packets are allocated with block numbers
    568  * at powers of two.  When rx_pkt_alloc is called, the smallest block
    569  * number large enough to satisfy the allocation and with free packets
    570  * is used.
    571  *
    572  * CPUTRANS uses pkt->cookie2 for RX packets to store the index
    573  * used, so pkt->cookie2 must NOT be used above this layer.
    574  *
    575  * CPUTRANS RX does not require a transport pointer; it only
    576  * uses sal_alloc and sal_free.
    577  *
    578  * CPUTRANS RX packets have an extra pointer allocate at the
    579  * beginning of the first block's packet buffer.  This is used
    580  * by ATP/BET and should not be used by applications.
    581  *
    582  * Static variables:
    583  *     _rx_setup_done       Boolean; packets allocated?
    584  *     _rx_lists            How many categories of packets set up
    585  *     _rx_free_lists       Pointer to array of free list head ptrs
    586  *     _rx_alloc_pkts       Allocation pointer for all RX packets
    587  *     _rx_pkt_blks         Allocation pointer for all data pointer blocks
    588  */
    589 
    590 static int                      _rx_setup_done;
    591 
    592 static int                      _rx_lists;
    593 static bcm_pkt_t              **_rx_free_lists;
    594 static bcm_pkt_t               *_rx_alloc_pkts;
    595 static bcm_pkt_blk_t           *_rx_pkt_blks;
    596 
    597 /*
    598  * Function:
    599  *      cputrans_rx_setup_done
    600  * Purpose:
    601  *      Indicate if setup has been done for RX part of cputrans subsys
    602  * Returns:
    603  *      Boolean:  True if setup has been done
    604  */
    605 
    606 int
    607 cputrans_rx_setup_done(void)
    608 {
    609     return _rx_setup_done;
    610 }
    611 
    612 
    613 /*
    614  * Function:
    615  *      cputrans_rx_pkt_setup
    616  * Purpose:
    617  *      Pre-allocate and set up RX packets
    618  * Parameters:
    619  *      list_count   - The number of lists to provide; if < 0, uses
    620  *                     default values.
    621  *      blk_cnts     - Pointer to an array with list_count entries. See notes.
    622  * Returns:
    623  *      BCM_E_XXX
    624  * Notes:
    625  *      Each entry is the number of packets to allocate with 2^n
    626  *      packet block pointers.  The number of blocks needed by a long
    627  *      packet is about 2 * (pkt_len/seg_len + 1).  With seg_len = 1400
    628  *      and pkt_len = 64K, this is about 94 blocks, so list count should
    629  *      generally be <= 8.
    630  *
    631  *      Currently, if a packet is requested with too many blocks,
    632  *      it fails (no on-the-fly allocation.)
    633  */
    634 
    635 int
    636 cputrans_rx_pkt_setup(int list_count, int *blk_cnts)
    637 {
    638     int tot_pkts = 0;   /* How many packets to allocate, total */
    639     int tot_blks = 0;   /* How many pkt_blk structs to allocate, total */
    640     int bytes;
    641     int pkt_cnt = 0;    /* How many pkts filled in so far */
    642     bcm_pkt_t *pkt;
    643     int blk_offset = 0; /* How many blocks used up so far */
    644     int i, j;
    645     int blks;
    646     static int deflt_blk_cnts[] = CPUTRANS_RX_BLK_CNTS_DEFAULT;
    647     int deflt_count = CPUTRANS_RX_LIST_COUNT_DEFAULT;
    648 
    649     if (_rx_setup_done) {
    650         return BCM_E_BUSY;
    651     }
    652 
    653     if (list_count <= 0 || blk_cnts == NULL) {
    654         list_count = deflt_count;
    655         blk_cnts = deflt_blk_cnts;
    656     }
    657     _rx_lists = list_count;
    658 
    659     for (i = 0; i < list_count; i++) {
    660         tot_pkts += blk_cnts[i];
    661         tot_blks += blk_cnts[i] * (1 << i);
    662     }
    663 
    664     bytes = tot_pkts * sizeof(bcm_pkt_t);
    665     _ALLOC_CHECK(_rx_alloc_pkts, bytes, cputrans_rx_pkt_cleanup, "CT_RX");
    666 
    667     bytes = list_count * sizeof(bcm_pkt_t *);
    668     _ALLOC_CHECK(_rx_free_lists, bytes, cputrans_rx_pkt_cleanup, "CT_RX");
    669 
    670     bytes = tot_blks * sizeof(bcm_pkt_blk_t);
    671     _ALLOC_CHECK(_rx_pkt_blks, bytes, cputrans_rx_pkt_cleanup, "CT_RX");
    672 
    673     /* Set up free lists and packet block pointers from the allocated data */
    674     pkt = &_rx_alloc_pkts[0];   /* Keep compiler happy */
    675     for (i = 0; i < list_count; i++) {
    676         blks = 1 << i;  /* Number of blocks this round */
    677         if (blk_cnts[i] > 0) {
    678             _rx_free_lists[i] = &_rx_alloc_pkts[pkt_cnt];
    679             for (j = 0; j < blk_cnts[i]; j++) {
    680                 pkt = &_rx_alloc_pkts[pkt_cnt + j];
    681                 pkt->blk_count = blks;
    682                 pkt->pkt_data = &_rx_pkt_blks[blk_offset];
    683                 pkt->cookie2 = INT_TO_PTR(i);  /* Record which free list */
    684                 blk_offset += blks;
    685                 pkt->next = &_rx_alloc_pkts[pkt_cnt + j + 1];
    686             }
    687             pkt->next = NULL;  /* Terminate list */
    688             pkt_cnt += blk_cnts[i];
    689         }
    690     }
    691 
    692     _rx_setup_done = TRUE;
    693 
    694     return BCM_E_NONE;
    695 }
    696 
    697 /*
    698  * Function:
    699  *      cputrans_rx_pkt_alloc
    700  * Purpose:
    701  *      Allocate a packet struct with sufficient RX block pointers.
    702  * Parameters:
    703  *      num_blks      - The number of blocks needed in the pkt_blk struct
    704  * Returns:
    705  *      BCM_E_XXX
    706  * Notes:
    707  *      Currently, will not allocate beyond the number
    708  *      set up in cputrans_rx_pkt_setup.  Nor will it allocate if there
    709  *      are no free buffers available.
    710  *
    711  *      Packets allocated with this routine must be freed by a call
    712  *      to cputrans_rx_pkt_free.
    713  */
    714 
    715 bcm_pkt_t *
    716 cputrans_rx_pkt_alloc(int num_blks)
    717 {
    718     int i;
    719     int blks = 1;
    720     bcm_pkt_t *pkt = NULL;
    721 
    722     if (!_rx_setup_done) {
    723         return NULL;
    724     }
    725 
    726     CPUTRANS_LOCK;
    727     for (i = 0; i < _rx_lists; i++) {
    728         if (num_blks <= blks && _rx_free_lists[i] != NULL) {
    729             pkt = _rx_free_lists[i];
    730             _rx_free_lists[i] = pkt->next;
    731             break;
    732         }
    733         blks <<= 1;
    734     }
    735     CPUTRANS_UNLOCK;
    736 
    737     if (pkt != NULL) {
    738         pkt->blk_count = num_blks;
    739     }
    740     return pkt;
    741 }
    742 
    743 
    744 /*
    745  * Function:
    746  *      cputrans_rx_pkt_free
    747  * Purpose:
    748  *      Free an RX packet allocated by cputrans_rx_pkt_alloc
    749  * Parameters:
    750  *      pkt          - pointer to packet to free
    751  * Returns:
    752  *      BCM_E_XXX
    753  * Notes:
    754  *      Should only be called on packets allocated by cputrans_rx_pkt_alloc.
    755  */
    756 
    757 void
    758 cputrans_rx_pkt_free(bcm_pkt_t *pkt)
    759 {
    760     int idx;
    761 
    762     if (pkt == NULL) {
    763         LOG_INFO(BSL_LS_TKS_CTPKT,
    764                  (BSL_META("CT free: Packet NULL\n")));
    765         return;
    766     }
    767 
    768     if (!_rx_setup_done) {
    769         LOG_INFO(BSL_LS_TKS_CTPKT,
    770                  (BSL_META("CT free: Not initialized\n")));
    771         return;
    772     }
    773 
    774     idx = PTR_TO_INT(pkt->cookie2);
    775     if (idx >= _rx_lists) {
    776         LOG_INFO(BSL_LS_TKS_CTPKT,
    777                  (BSL_META("CT free: bad CT index: %d > %d\n"),
    778                   idx, _rx_lists));
    779         return;
    780     }
    781 
    782     CPUTRANS_LOCK;
    783     pkt->next = _rx_free_lists[idx];
    784     _rx_free_lists[idx] = pkt;
    785     CPUTRANS_UNLOCK;
    786 }
    787 
    788 
    789 /*
    790  * Function:
    791  *      cputrans_rx_pkt_cleanup
    792  * Purpose:
    793  *      Deallocate RX packets for CPUTRANS
    794  */
    795 
    796 void
    797 cputrans_rx_pkt_cleanup(void)
    798 {
    799     if (!INIT_DONE) { /* Create lock */
    800         if (_do_init() < 0) {
    801             return;
    802         }
    803     }
    804 
    805     CPUTRANS_LOCK;
    806     _FREE_CHECK(_rx_free_lists);
    807     _FREE_CHECK(_rx_alloc_pkts);
    808     _FREE_CHECK(_rx_pkt_blks);
    809     _rx_lists = 0;
    810     _rx_setup_done = FALSE;
    811     CPUTRANS_UNLOCK;
    812 }
    813 
    814 
    815 #undef _FREE_CHECK
    816 #undef _ALLOC_CHECK
    817 
    818 #define CT_TRANS_MAX 10
    819 static bcm_trans_ptr_t *ct_trans[CT_TRANS_MAX];
    820 static int ct_trans_count;
    821 
    822 int
    823 cputrans_trans_count(void)
    824 {
    825     return ct_trans_count;
    826 }
    827 
    828 int
    829 cputrans_trans_add(bcm_trans_ptr_t *trans)
    830 {
    831     int i;
    832 
    833     for (i = 0; i < ct_trans_count; i++) {
    834         if (ct_trans[i] == trans) {
    835             return BCM_E_NONE;
    836         }
    837     }
    838 
    839     if (ct_trans_count + 1 > CT_TRANS_MAX) {
    840         return BCM_E_RESOURCE;
    841     }
    842     ct_trans[ct_trans_count++] = trans;
    843 
    844     return BCM_E_NONE;
    845 }
    846 
    847 int
    848 cputrans_trans_remove(bcm_trans_ptr_t *trans)
    849 {
    850     int i, j;
    851 
    852     for (i = 0; i < ct_trans_count; i++) {
    853         if (ct_trans[i] == trans) {
    854             for (j = i; j < ct_trans_count - 1; j++) {
    855                 ct_trans[j] = ct_trans[j + 1];
    856             }
    857             ct_trans_count--;
    858             return BCM_E_NONE;
    859         }
    860     }
    861 
    862     return BCM_E_NONE;
    863 }
    864 
    865 /*
    866  * RX register on all known transports
    867  *     rx_unit_register    -- on the specified unit
    868  *     rx_bmp_register     -- on the specified bitmap of units (unit < 32)
    869  *     rx_register         -- on all local units
    870  *
    871  * For rx_register and rx_bmp_register, if no units are found or
    872  * specified, calls on unit -1.
    873  */
    874 
    875 int
    876 cputrans_rx_unit_register(int unit,
    877                           const char *name,
    878                           bcm_rx_cb_f callback,
    879                           uint8 priority,
    880                           void *cookie,
    881                           uint32 flags)
    882 {
    883     int rv = BCM_E_NONE;
    884     int tmp_rv;
    885     int i;
    886 
    887     for (i = 0; i < ct_trans_count; i++) {
    888         if (ct_trans[i]->tp_rx_reg != NULL) {
    889             tmp_rv = ct_trans[i]->tp_rx_reg(unit, name, callback,
    890                                             priority, cookie, flags);
    891             if (tmp_rv < 0) {
    892                 rv = tmp_rv;
    893             }
    894         }
    895     }
    896 
    897     return rv;
    898 }
    899 
    900 int
    901 cputrans_rx_register(const char *name,
    902                      bcm_rx_cb_f callback,
    903                      uint8 priority,
    904                      void *cookie,
    905                      uint32 flags)
    906 {
    907     int rv = BCM_E_NONE;
    908     int tmp_rv;
    909     int found = FALSE;
    910     int unit;
    911 
    912     for (unit = 0; unit < BCM_CONTROL_MAX; unit++) {
    913         if (BCM_UNIT_VALID(unit) && BCM_IS_LOCAL(unit)) {
    914             found = TRUE;
    915             tmp_rv = cputrans_rx_unit_register(unit, name, callback,
    916                                                priority, cookie, flags);
    917             if (tmp_rv < 0) {
    918                 LOG_VERBOSE(BSL_LS_TKS_CTPKT,
    919                             (BSL_META("CPU Trans reg failed %d, unit %d: %s\n"),
    920                              rv, unit, bcm_errmsg(rv)));
    921                 rv = tmp_rv;
    922             }
    923         }
    924     }
    925 
    926     if (!found) { /* Call transport pointers on unit -1 */
    927         tmp_rv = cputrans_rx_unit_register(-1, name, callback, priority,
    928                                            cookie, flags);
    929         if (tmp_rv < 0) {
    930             LOG_VERBOSE(BSL_LS_TKS_CTPKT,
    931                         (BSL_META("CPU Trans reg failed %d, dflt unit: %s\n"),
    932                          rv, bcm_errmsg(rv)));
    933             rv = tmp_rv;
    934         }
    935     }
    936 
    937     return rv;
    938 }
    939 
    940 int
    941 cputrans_rx_bmp_register(uint32 unit_bmp,
    942                          const char *name,
    943                          bcm_rx_cb_f callback,
    944                          uint8 priority,
    945                          void *cookie,
    946                          uint32 flags)
    947 {
    948     int rv = BCM_E_NONE;
    949     int tmp_rv;
    950     int unit;
    951 
    952     if (unit_bmp == 0) {
    953         rv = cputrans_rx_unit_register(-1, name, callback, priority,
    954                                        cookie, flags);
    955     } else {
    956         for (unit = 0; unit < 32; unit++) {
    957             if (BCM_UNIT_VALID(unit) && BCM_IS_LOCAL(unit) &&
    958                     ((1 << unit) & unit_bmp)) {
    959                 tmp_rv = cputrans_rx_unit_register(unit, name,
    960                                                    callback, priority,
    961                                                    cookie, flags);
    962                 if (tmp_rv < 0) {
    963                     rv = tmp_rv;
    964                 }
    965             }
    966         }
    967     }
    968 
    969     return rv;
    970 }
    971 
    972 
    973 /*
    974  * RX unregister on all known transports
    975  */
    976 
    977 int
    978 cputrans_rx_unit_unregister(int unit, bcm_rx_cb_f callback, uint8 priority)
    979 {
    980     int rv = BCM_E_NONE;
    981     int tmp_rv;
    982     int i;
    983 
    984     for (i = 0; i < ct_trans_count; i++) {
    985         if (ct_trans[i]->tp_rx_unreg != NULL) {
    986             tmp_rv = ct_trans[i]->
    987                 tp_rx_unreg(unit, callback, priority);
    988             if (tmp_rv < 0) {
    989                 rv = tmp_rv;
    990             }
    991         }
    992     }
    993 
    994     return rv;
    995 }
    996 
    997 int
    998 cputrans_rx_unregister(bcm_rx_cb_f callback, uint8 priority)
    999 {
   1000     int rv = BCM_E_NONE;
   1001     int tmp_rv;
   1002     int unit;
   1003     int found = FALSE;
   1004 
   1005     for (unit = 0; unit < BCM_CONTROL_MAX; unit++) {
   1006         if (BCM_UNIT_VALID(unit) && BCM_IS_LOCAL(unit)) {
   1007             found = TRUE;
   1008             tmp_rv = cputrans_rx_unit_unregister(unit, callback, priority);
   1009             if (tmp_rv < 0) {
   1010                 rv = tmp_rv;
   1011             }
   1012         }
   1013     }
   1014 
   1015     if (!found) { /* Call transport pointers on unit -1 */
   1016         tmp_rv = cputrans_rx_unit_unregister(-1, callback, priority);
   1017         if (tmp_rv < 0) {
   1018             rv = tmp_rv;
   1019         }
   1020     }
   1021 
   1022     return rv;
   1023 }
   1024 
   1025 int
   1026 cputrans_rx_bmp_unregister(uint32 unit_bmp,
   1027                             bcm_rx_cb_f callback,
   1028                             uint8 priority)
   1029 {
   1030     int rv = BCM_E_NONE;
   1031     int tmp_rv;
   1032     int unit;
   1033 
   1034     if (unit_bmp == 0) {
   1035         rv = cputrans_rx_unit_unregister(-1, callback, priority);
   1036     } else {
   1037         for (unit = 0; unit < 32; unit++) {
   1038             if (BCM_UNIT_VALID(unit) && BCM_IS_LOCAL(unit) &&
   1039                     ((1 << unit) & unit_bmp)) {
   1040                 tmp_rv = cputrans_rx_unit_unregister(unit, callback, priority);
   1041                 if (tmp_rv < 0) {
   1042                     rv = tmp_rv;
   1043                 }
   1044             }
   1045         }
   1046     }
   1047 
   1048     return rv;
   1049 }
   1050 
   1051 
   1052 /*
   1053  * Function:
   1054  *      cputrans_error_count_get
   1055  * Purpose:
   1056  *      Get and optionally clear the error count in this layer
   1057  * Parameters:
   1058  *      clear     - Boolean: If set, will clear the error counter
   1059  * Returns:
   1060  *      BCM_E_XXX
   1061  */
   1062 
   1063 int
   1064 cputrans_error_count_get(int clear)
   1065 {
   1066     int cur_cnt;
   1067 
   1068     cur_cnt = cputrans_error_count;
   1069     if (clear) {
   1070         cputrans_error_count = 0;
   1071     }
   1072 
   1073     return cur_cnt;
   1074 }