| /** |
| * @file IxEthDataPlane.c |
| * |
| * @author Intel Corporation |
| * @date 12-Feb-2002 |
| * |
| * @brief This file contains the implementation of the IXPxxx |
| * Ethernet Access Data plane component |
| * |
| * Design Notes: |
| * |
| * @par |
| * IXP400 SW Release version 2.0 |
| * |
| * -- Copyright Notice -- |
| * |
| * @par |
| * Copyright 2001-2005, Intel Corporation. |
| * All rights reserved. |
| * |
| * @par |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the Intel Corporation nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * @par |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * @par |
| * -- End of Copyright Notice -- |
| */ |
| |
| #include "IxNpeMh.h" |
| #include "IxEthAcc.h" |
| #include "IxEthDB.h" |
| #include "IxOsal.h" |
| #include "IxEthDBPortDefs.h" |
| #include "IxFeatureCtrl.h" |
| #include "IxEthAcc_p.h" |
| #include "IxEthAccQueueAssign_p.h" |
| |
| extern PUBLIC IxEthAccMacState ixEthAccMacState[]; |
| extern PUBLIC UINT32 ixEthAccNewSrcMask; |
| |
| /** |
| * private functions prototype |
| */ |
| PRIVATE IX_OSAL_MBUF * |
| ixEthAccEntryFromQConvert(UINT32 qEntry, UINT32 mask); |
| |
| PRIVATE UINT32 |
| ixEthAccMbufRxQPrepare(IX_OSAL_MBUF *mbuf); |
| |
| PRIVATE UINT32 |
| ixEthAccMbufTxQPrepare(IX_OSAL_MBUF *mbuf); |
| |
| PRIVATE IxEthAccStatus |
| ixEthAccTxSwQHighestPriorityGet(IxEthAccPortId portId, |
| IxEthAccTxPriority *priorityPtr); |
| |
| PRIVATE IxEthAccStatus |
| ixEthAccTxFromSwQ(IxEthAccPortId portId, |
| IxEthAccTxPriority priority); |
| |
| PRIVATE IxEthAccStatus |
| ixEthAccRxFreeFromSwQ(IxEthAccPortId portId); |
| |
| PRIVATE void |
| ixEthAccMbufFromTxQ(IX_OSAL_MBUF *mbuf); |
| |
| PRIVATE void |
| ixEthAccMbufFromRxQ(IX_OSAL_MBUF *mbuf); |
| |
| PRIVATE IX_STATUS |
| ixEthAccQmgrLockTxWrite(IxEthAccPortId portId, |
| UINT32 qBuffer); |
| |
| PRIVATE IX_STATUS |
| ixEthAccQmgrLockRxWrite(IxEthAccPortId portId, |
| UINT32 qBuffer); |
| |
| PRIVATE IX_STATUS |
| ixEthAccQmgrTxWrite(IxEthAccPortId portId, |
| UINT32 qBuffer, |
| UINT32 priority); |
| |
| /** |
| * @addtogroup IxEthAccPri |
| *@{ |
| */ |
| |
| /* increment a counter only when stats are enabled */ |
| #define TX_STATS_INC(port,field) \ |
| IX_ETH_ACC_STATS_INC(ixEthAccPortData[port].ixEthAccTxData.stats.field) |
| #define RX_STATS_INC(port,field) \ |
| IX_ETH_ACC_STATS_INC(ixEthAccPortData[port].ixEthAccRxData.stats.field) |
| |
| /* always increment the counter (mainly used for unexpected errors) */ |
| #define TX_INC(port,field) \ |
| ixEthAccPortData[port].ixEthAccTxData.stats.field++ |
| #define RX_INC(port,field) \ |
| ixEthAccPortData[port].ixEthAccRxData.stats.field++ |
| |
| PRIVATE IxEthAccDataPlaneStats ixEthAccDataStats; |
| |
| extern IxEthAccPortDataInfo ixEthAccPortData[]; |
| extern IxEthAccInfo ixEthAccDataInfo; |
| |
| PRIVATE IxOsalFastMutex txWriteMutex[IX_ETH_ACC_NUMBER_OF_PORTS]; |
| PRIVATE IxOsalFastMutex rxWriteMutex[IX_ETH_ACC_NUMBER_OF_PORTS]; |
| |
| /** |
| * |
| * @brief Mbuf header conversion macros : they implement the |
| * different conversions using a temporary value. They also double-check |
| * that the parameters can be converted to/from NPE format. |
| * |
| */ |
| #if defined(__wince) && !defined(IN_KERNEL) |
| #define PTR_VIRT2NPE(ptrSrc,dst) \ |
| do { UINT32 temp; \ |
| IX_OSAL_ENSURE(sizeof(ptrSrc) == sizeof(UINT32), "Wrong parameter type"); \ |
| IX_OSAL_ENSURE(sizeof(dst) == sizeof(UINT32), "Wrong parameter type"); \ |
| temp = (UINT32)IX_OSAL_MBUF_MBUF_VIRTUAL_TO_PHYSICAL_TRANSLATION((IX_OSAL_MBUF*)ptrSrc); \ |
| (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \ |
| while(0) |
| |
| #define PTR_NPE2VIRT(type,src,ptrDst) \ |
| do { void *temp; \ |
| IX_OSAL_ENSURE(sizeof(type) == sizeof(UINT32), "Wrong parameter type"); \ |
| IX_OSAL_ENSURE(sizeof(src) == sizeof(UINT32), "Wrong parameter type"); \ |
| IX_OSAL_ENSURE(sizeof(ptrDst) == sizeof(UINT32), "Wrong parameter type"); \ |
| temp = (void *)IX_OSAL_SWAP_BE_SHARED_LONG(src); \ |
| (ptrDst) = (type)IX_OSAL_MBUF_MBUF_PHYSICAL_TO_VIRTUAL_TRANSLATION(temp); } \ |
| while(0) |
| #else |
| #define PTR_VIRT2NPE(ptrSrc,dst) \ |
| do { UINT32 temp; \ |
| IX_OSAL_ENSURE(sizeof(ptrSrc) == sizeof(UINT32), "Wrong parameter type"); \ |
| IX_OSAL_ENSURE(sizeof(dst) == sizeof(UINT32), "Wrong parameter type"); \ |
| temp = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS(ptrSrc); \ |
| (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \ |
| while(0) |
| |
| #define PTR_NPE2VIRT(type,src,ptrDst) \ |
| do { void *temp; \ |
| IX_OSAL_ENSURE(sizeof(type) == sizeof(UINT32), "Wrong parameter type"); \ |
| IX_OSAL_ENSURE(sizeof(src) == sizeof(UINT32), "Wrong parameter type"); \ |
| IX_OSAL_ENSURE(sizeof(ptrDst) == sizeof(UINT32), "Wrong parameter type"); \ |
| temp = (void *)IX_OSAL_SWAP_BE_SHARED_LONG(src); \ |
| (ptrDst) = (type)IX_OSAL_MMU_PHYS_TO_VIRT(temp); } \ |
| while(0) |
| #endif |
| |
| /** |
| * |
| * @brief Mbuf payload pointer conversion macros : Wince has its own |
| * method to convert the buffer pointers |
| */ |
| #if defined(__wince) && !defined(IN_KERNEL) |
| #define DATAPTR_VIRT2NPE(ptrSrc,dst) \ |
| do { UINT32 temp; \ |
| temp = (UINT32)IX_OSAL_MBUF_DATA_VIRTUAL_TO_PHYSICAL_TRANSLATION(ptrSrc); \ |
| (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \ |
| while(0) |
| |
| #else |
| #define DATAPTR_VIRT2NPE(ptrSrc,dst) PTR_VIRT2NPE(IX_OSAL_MBUF_MDATA(ptrSrc),dst) |
| #endif |
| |
| |
| /* Flush the shared part of the mbuf header */ |
| #define IX_ETHACC_NE_CACHE_FLUSH(mbufPtr) \ |
| do { \ |
| IX_OSAL_CACHE_FLUSH(IX_ETHACC_NE_SHARED(mbufPtr), \ |
| sizeof(IxEthAccNe)); \ |
| } \ |
| while(0) |
| |
| /* Invalidate the shared part of the mbuf header */ |
| #define IX_ETHACC_NE_CACHE_INVALIDATE(mbufPtr) \ |
| do { \ |
| IX_OSAL_CACHE_INVALIDATE(IX_ETHACC_NE_SHARED(mbufPtr), \ |
| sizeof(IxEthAccNe)); \ |
| } \ |
| while(0) |
| |
| /* Preload one cache line (shared mbuf headers are aligned |
| * and their size is 1 cache line) |
| * |
| * IX_OSAL_CACHED is defined when the mbuf headers are |
| * allocated from cached memory. |
| * |
| * Other processor on emulation environment may not implement |
| * preload function |
| */ |
| #ifdef IX_OSAL_CACHED |
| #if (CPU!=SIMSPARCSOLARIS) && !defined (__wince) |
| #define IX_ACC_DATA_CACHE_PRELOAD(ptr) \ |
| do { /* preload a cache line (Xscale Processor) */ \ |
| __asm__ (" pld [%0]\n": : "r" (ptr)); \ |
| } \ |
| while(0) |
| #else |
| /* preload not implemented on different processor */ |
| #define IX_ACC_DATA_CACHE_PRELOAD(mbufPtr) \ |
| do { /* nothing */ } while (0) |
| #endif |
| #else |
| /* preload not needed if cache is not enabled */ |
| #define IX_ACC_DATA_CACHE_PRELOAD(mbufPtr) \ |
| do { /* nothing */ } while (0) |
| #endif |
| |
| /** |
| * |
| * @brief function to retrieve the correct pointer from |
| * a queue entry posted by the NPE |
| * |
| * @param qEntry : entry from qmgr queue |
| * mask : applicable mask for this queue |
| * (4 most significant bits are used for additional informations) |
| * |
| * @return IX_OSAL_MBUF * pointer to mbuf header |
| * |
| * @internal |
| */ |
| PRIVATE IX_OSAL_MBUF * |
| ixEthAccEntryFromQConvert(UINT32 qEntry, UINT32 mask) |
| { |
| IX_OSAL_MBUF *mbufPtr; |
| |
| if (qEntry != 0) |
| { |
| /* mask NPE bits (e.g. priority, port ...) */ |
| qEntry &= mask; |
| |
| #if IX_ACC_DRAM_PHYS_OFFSET != 0 |
| /* restore the original address pointer (if PHYS_OFFSET is not 0) */ |
| qEntry |= (IX_ACC_DRAM_PHYS_OFFSET & ~IX_ETHNPE_QM_Q_RXENET_ADDR_MASK); |
| #endif |
| /* get the mbuf pointer address from the npe-shared address */ |
| qEntry -= offsetof(IX_OSAL_MBUF,ix_ne); |
| |
| /* phys2virt mbuf */ |
| mbufPtr = (IX_OSAL_MBUF *)IX_OSAL_MMU_PHYS_TO_VIRT(qEntry); |
| |
| /* preload the cacheline shared with NPE */ |
| IX_ACC_DATA_CACHE_PRELOAD(IX_ETHACC_NE_SHARED(mbufPtr)); |
| |
| /* preload the cacheline used by xscale */ |
| IX_ACC_DATA_CACHE_PRELOAD(mbufPtr); |
| } |
| else |
| { |
| mbufPtr = NULL; |
| } |
| |
| return mbufPtr; |
| } |
| |
| /* Convert the mbuf header for NPE transmission */ |
| PRIVATE UINT32 |
| ixEthAccMbufTxQPrepare(IX_OSAL_MBUF *mbuf) |
| { |
| UINT32 qbuf; |
| UINT32 len; |
| |
| /* endianess swap for tci and flags |
| note: this is done only once, even for chained buffers */ |
| IX_ETHACC_NE_FLAGS(mbuf) = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_FLAGS(mbuf)); |
| IX_ETHACC_NE_VLANTCI(mbuf) = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_VLANTCI(mbuf)); |
| |
| /* test for unchained mbufs */ |
| if (IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) == NULL) |
| { |
| /* "best case" scenario : unchained mbufs */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedTxMBufs); |
| |
| /* payload pointer conversion */ |
| DATAPTR_VIRT2NPE(mbuf, IX_ETHACC_NE_DATA(mbuf)); |
| |
| /* unchained mbufs : the frame length is the mbuf length |
| * and the 2 identical lengths are stored in the same |
| * word. |
| */ |
| len = IX_OSAL_MBUF_MLEN(mbuf); |
| |
| /* set the length in both length and pktLen 16-bits fields */ |
| len |= (len << IX_ETHNPE_ACC_LENGTH_OFFSET); |
| IX_ETHACC_NE_LEN(mbuf) = IX_OSAL_SWAP_BE_SHARED_LONG(len); |
| |
| /* unchained mbufs : next contains 0 */ |
| IX_ETHACC_NE_NEXT(mbuf) = 0; |
| |
| /* flush shared header after all address conversions */ |
| IX_ETHACC_NE_CACHE_FLUSH(mbuf); |
| } |
| else |
| { |
| /* chained mbufs */ |
| IX_OSAL_MBUF *ptr = mbuf; |
| IX_OSAL_MBUF *nextPtr; |
| UINT32 frmLen; |
| |
| /* get the frame length from the header of the first buffer */ |
| frmLen = IX_OSAL_MBUF_PKT_LEN(mbuf); |
| |
| do |
| { |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedTxMBufs); |
| |
| /* payload pointer */ |
| DATAPTR_VIRT2NPE(ptr,IX_ETHACC_NE_DATA(ptr)); |
| /* Buffer length and frame length are stored in the same word */ |
| len = IX_OSAL_MBUF_MLEN(ptr); |
| len = frmLen | (len << IX_ETHNPE_ACC_LENGTH_OFFSET); |
| IX_ETHACC_NE_LEN(ptr) = IX_OSAL_SWAP_BE_SHARED_LONG(len); |
| |
| /* get the virtual next chain pointer */ |
| nextPtr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr); |
| if (nextPtr != NULL) |
| { |
| /* shared pointer of the next buffer is chained */ |
| PTR_VIRT2NPE(IX_ETHACC_NE_SHARED(nextPtr), |
| IX_ETHACC_NE_NEXT(ptr)); |
| } |
| else |
| { |
| IX_ETHACC_NE_NEXT(ptr) = 0; |
| } |
| |
| /* flush shared header after all address conversions */ |
| IX_ETHACC_NE_CACHE_FLUSH(ptr); |
| |
| /* move to next buffer */ |
| ptr = nextPtr; |
| |
| /* the frame length field is set only in the first buffer |
| * and is zeroed in the next buffers |
| */ |
| frmLen = 0; |
| } |
| while(ptr != NULL); |
| |
| } |
| |
| /* virt2phys mbuf itself */ |
| qbuf = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS( |
| IX_ETHACC_NE_SHARED(mbuf)); |
| |
| /* Ensure the bits which are reserved to exchange information with |
| * the NPE are cleared |
| * |
| * If the mbuf address is not correctly aligned, or from an |
| * incompatible memory range, there is no point to continue |
| */ |
| IX_OSAL_ENSURE(((qbuf & ~IX_ETHNPE_QM_Q_TXENET_ADDR_MASK) == 0), |
| "Invalid address range"); |
| |
| return qbuf; |
| } |
| |
| /* Convert the mbuf header for NPE reception */ |
| PRIVATE UINT32 |
| ixEthAccMbufRxQPrepare(IX_OSAL_MBUF *mbuf) |
| { |
| UINT32 len; |
| UINT32 qbuf; |
| |
| if (IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) == NULL) |
| { |
| /* "best case" scenario : unchained mbufs */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedRxFreeMBufs); |
| |
| /* unchained mbufs : payload pointer */ |
| DATAPTR_VIRT2NPE(mbuf, IX_ETHACC_NE_DATA(mbuf)); |
| |
| /* unchained mbufs : set the buffer length |
| * and the frame length field is zeroed |
| */ |
| len = (IX_OSAL_MBUF_MLEN(mbuf) << IX_ETHNPE_ACC_LENGTH_OFFSET); |
| IX_ETHACC_NE_LEN(mbuf) = IX_OSAL_SWAP_BE_SHARED_LONG(len); |
| |
| /* unchained mbufs : next pointer is null */ |
| IX_ETHACC_NE_NEXT(mbuf) = 0; |
| |
| /* flush shared header after all address conversions */ |
| IX_ETHACC_NE_CACHE_FLUSH(mbuf); |
| |
| /* remove shared header cache line */ |
| IX_ETHACC_NE_CACHE_INVALIDATE(mbuf); |
| } |
| else |
| { |
| /* chained mbufs */ |
| IX_OSAL_MBUF *ptr = mbuf; |
| IX_OSAL_MBUF *nextPtr; |
| |
| do |
| { |
| /* chained mbufs */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedRxFreeMBufs); |
| |
| /* we must save virtual next chain pointer */ |
| nextPtr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr); |
| |
| if (nextPtr != NULL) |
| { |
| /* chaining pointer for NPE */ |
| PTR_VIRT2NPE(IX_ETHACC_NE_SHARED(nextPtr), |
| IX_ETHACC_NE_NEXT(ptr)); |
| } |
| else |
| { |
| IX_ETHACC_NE_NEXT(ptr) = 0; |
| } |
| |
| /* payload pointer */ |
| DATAPTR_VIRT2NPE(ptr,IX_ETHACC_NE_DATA(ptr)); |
| |
| /* buffer length */ |
| len = (IX_OSAL_MBUF_MLEN(ptr) << IX_ETHNPE_ACC_LENGTH_OFFSET); |
| IX_ETHACC_NE_LEN(ptr) = IX_OSAL_SWAP_BE_SHARED_LONG(len); |
| |
| /* flush shared header after all address conversions */ |
| IX_ETHACC_NE_CACHE_FLUSH(ptr); |
| |
| /* remove shared header cache line */ |
| IX_ETHACC_NE_CACHE_INVALIDATE(ptr); |
| |
| /* next mbuf in the chain */ |
| ptr = nextPtr; |
| } |
| while(ptr != NULL); |
| } |
| |
| /* virt2phys mbuf itself */ |
| qbuf = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS( |
| IX_ETHACC_NE_SHARED(mbuf)); |
| |
| /* Ensure the bits which are reserved to exchange information with |
| * the NPE are cleared |
| * |
| * If the mbuf address is not correctly aligned, or from an |
| * incompatible memory range, there is no point to continue |
| */ |
| IX_OSAL_ENSURE(((qbuf & ~IX_ETHNPE_QM_Q_RXENET_ADDR_MASK) == 0), |
| "Invalid address range"); |
| |
| return qbuf; |
| } |
| |
| /* Convert the mbuf header after NPE transmission |
| * Since there is nothing changed by the NPE, there is no need |
| * to process anything but the update of internal stats |
| * when they are enabled |
| */ |
| PRIVATE void |
| ixEthAccMbufFromTxQ(IX_OSAL_MBUF *mbuf) |
| { |
| #ifndef NDEBUG |
| /* test for unchained mbufs */ |
| if (IX_ETHACC_NE_NEXT(mbuf) == 0) |
| { |
| /* unchained mbufs : update the stats */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedTxDoneMBufs); |
| } |
| else |
| { |
| /* chained mbufs : walk the chain and update the stats */ |
| IX_OSAL_MBUF *ptr = mbuf; |
| |
| do |
| { |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedTxDoneMBufs); |
| ptr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr); |
| } |
| while (ptr != NULL); |
| } |
| #endif |
| } |
| |
| /* Convert the mbuf header after NPE reception */ |
| PRIVATE void |
| ixEthAccMbufFromRxQ(IX_OSAL_MBUF *mbuf) |
| { |
| UINT32 len; |
| |
| /* endianess swap for tci and flags |
| note: this is done only once, even for chained buffers */ |
| IX_ETHACC_NE_FLAGS(mbuf) = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_FLAGS(mbuf)); |
| IX_ETHACC_NE_VLANTCI(mbuf) = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_VLANTCI(mbuf)); |
| |
| /* test for unchained mbufs */ |
| if (IX_ETHACC_NE_NEXT(mbuf) == 0) |
| { |
| /* unchained mbufs */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedRxMBufs); |
| |
| /* get the frame length. it is the same than the buffer length */ |
| len = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(mbuf)); |
| len &= IX_ETHNPE_ACC_PKTLENGTH_MASK; |
| IX_OSAL_MBUF_PKT_LEN(mbuf) = IX_OSAL_MBUF_MLEN(mbuf) = len; |
| |
| /* clears the next packet field */ |
| IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) = NULL; |
| } |
| else |
| { |
| IX_OSAL_MBUF *ptr = mbuf; |
| IX_OSAL_MBUF *nextPtr; |
| UINT32 frmLen; |
| |
| /* convert the frame length */ |
| frmLen = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(mbuf)); |
| IX_OSAL_MBUF_PKT_LEN(mbuf) = (frmLen & IX_ETHNPE_ACC_PKTLENGTH_MASK); |
| |
| /* chained mbufs */ |
| do |
| { |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedRxMBufs); |
| |
| /* convert the length */ |
| len = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(ptr)); |
| IX_OSAL_MBUF_MLEN(ptr) = (len >> IX_ETHNPE_ACC_LENGTH_OFFSET); |
| |
| /* get the next pointer */ |
| PTR_NPE2VIRT(IX_OSAL_MBUF *,IX_ETHACC_NE_NEXT(ptr), nextPtr); |
| if (nextPtr != NULL) |
| { |
| nextPtr = (IX_OSAL_MBUF *)((UINT8 *)nextPtr - offsetof(IX_OSAL_MBUF,ix_ne)); |
| } |
| /* set the next pointer */ |
| IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr) = nextPtr; |
| |
| /* move to the next buffer */ |
| ptr = nextPtr; |
| } |
| while (ptr != NULL); |
| } |
| } |
| |
| /* write to qmgr if possible and report an overflow if not possible |
| * Use a fast lock to protect the queue write. |
| * This way, the tx feature is reentrant. |
| */ |
| PRIVATE IX_STATUS |
| ixEthAccQmgrLockTxWrite(IxEthAccPortId portId, UINT32 qBuffer) |
| { |
| IX_STATUS qStatus; |
| if (ixOsalFastMutexTryLock(&txWriteMutex[portId]) == IX_SUCCESS) |
| { |
| qStatus = ixQMgrQWrite( |
| IX_ETH_ACC_PORT_TO_TX_Q_ID(portId), |
| &qBuffer); |
| #ifndef NDEBUG |
| if (qStatus != IX_SUCCESS) |
| { |
| TX_STATS_INC(portId, txOverflow); |
| } |
| #endif |
| ixOsalFastMutexUnlock(&txWriteMutex[portId]); |
| } |
| else |
| { |
| TX_STATS_INC(portId, txLock); |
| qStatus = IX_QMGR_Q_OVERFLOW; |
| } |
| return qStatus; |
| } |
| |
| /* write to qmgr if possible and report an overflow if not possible |
| * Use a fast lock to protect the queue write. |
| * This way, the Rx feature is reentrant. |
| */ |
| PRIVATE IX_STATUS |
| ixEthAccQmgrLockRxWrite(IxEthAccPortId portId, UINT32 qBuffer) |
| { |
| IX_STATUS qStatus; |
| if (ixOsalFastMutexTryLock(&rxWriteMutex[portId]) == IX_SUCCESS) |
| { |
| qStatus = ixQMgrQWrite( |
| IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId), |
| &qBuffer); |
| #ifndef NDEBUG |
| if (qStatus != IX_SUCCESS) |
| { |
| RX_STATS_INC(portId, rxFreeOverflow); |
| } |
| #endif |
| ixOsalFastMutexUnlock(&rxWriteMutex[portId]); |
| } |
| else |
| { |
| RX_STATS_INC(portId, rxFreeLock); |
| qStatus = IX_QMGR_Q_OVERFLOW; |
| } |
| return qStatus; |
| } |
| |
| /* |
| * Set the priority and write to a qmgr queue. |
| */ |
| PRIVATE IX_STATUS |
| ixEthAccQmgrTxWrite(IxEthAccPortId portId, UINT32 qBuffer, UINT32 priority) |
| { |
| /* fill the priority field */ |
| qBuffer |= (priority << IX_ETHNPE_QM_Q_FIELD_PRIOR_R); |
| |
| return ixEthAccQmgrLockTxWrite(portId, qBuffer); |
| } |
| |
| /** |
| * |
| * @brief This function will discover the highest priority S/W Tx Q that |
| * has entries in it |
| * |
| * @param portId - (in) the id of the port whose S/W Tx queues are to be searched |
| * priorityPtr - (out) the priority of the highest priority occupied q will be written |
| * here |
| * |
| * @return IX_ETH_ACC_SUCCESS if an occupied Q is found |
| * IX_ETH_ACC_FAIL if no Q has entries |
| * |
| * @internal |
| */ |
| PRIVATE IxEthAccStatus |
| ixEthAccTxSwQHighestPriorityGet(IxEthAccPortId portId, |
| IxEthAccTxPriority *priorityPtr) |
| { |
| if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline |
| == FIFO_NO_PRIORITY) |
| { |
| if(IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId]. |
| ixEthAccTxData.txQ[IX_ETH_ACC_TX_DEFAULT_PRIORITY])) |
| { |
| return IX_ETH_ACC_FAIL; |
| } |
| else |
| { |
| *priorityPtr = IX_ETH_ACC_TX_DEFAULT_PRIORITY; |
| TX_STATS_INC(portId,txPriority[*priorityPtr]); |
| return IX_ETH_ACC_SUCCESS; |
| } |
| } |
| else |
| { |
| IxEthAccTxPriority highestPriority = IX_ETH_ACC_TX_PRIORITY_7; |
| while(1) |
| { |
| if(!IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId]. |
| ixEthAccTxData.txQ[highestPriority])) |
| { |
| |
| *priorityPtr = highestPriority; |
| TX_STATS_INC(portId,txPriority[highestPriority]); |
| return IX_ETH_ACC_SUCCESS; |
| |
| } |
| if (highestPriority == IX_ETH_ACC_TX_PRIORITY_0) |
| { |
| return IX_ETH_ACC_FAIL; |
| } |
| highestPriority--; |
| } |
| } |
| } |
| |
| /** |
| * |
| * @brief This function will take a buffer from a TX S/W Q and attempt |
| * to add it to the relevant TX H/W Q |
| * |
| * @param portId - the port whose TX queue is to be written to |
| * priority - identifies the queue from which the entry is to be read |
| * |
| * @internal |
| */ |
| PRIVATE IxEthAccStatus |
| ixEthAccTxFromSwQ(IxEthAccPortId portId, |
| IxEthAccTxPriority priority) |
| { |
| IX_OSAL_MBUF *mbuf; |
| IX_STATUS qStatus; |
| |
| IX_OSAL_ENSURE((UINT32)priority <= (UINT32)7, "Invalid priority"); |
| |
| IX_ETH_ACC_DATAPLANE_REMOVE_MBUF_FROM_Q_HEAD( |
| ixEthAccPortData[portId].ixEthAccTxData.txQ[priority], |
| mbuf); |
| |
| if (mbuf != NULL) |
| { |
| /* |
| * Add the Tx buffer to the H/W Tx Q |
| * We do not need to flush here as it is already done |
| * in TxFrameSubmit(). |
| */ |
| qStatus = ixEthAccQmgrTxWrite( |
| portId, |
| IX_OSAL_MMU_VIRT_TO_PHYS((UINT32)IX_ETHACC_NE_SHARED(mbuf)), |
| priority); |
| |
| if (qStatus == IX_SUCCESS) |
| { |
| TX_STATS_INC(portId,txFromSwQOK); |
| return IX_SUCCESS; |
| } |
| else if (qStatus == IX_QMGR_Q_OVERFLOW) |
| { |
| /* |
| * H/W Q overflow, need to save the buffer |
| * back on the s/w Q. |
| * we must put it back on the head of the q to avoid |
| * reordering packet tx |
| */ |
| TX_STATS_INC(portId,txFromSwQDelayed); |
| IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD( |
| ixEthAccPortData[portId].ixEthAccTxData.txQ[priority], |
| mbuf); |
| |
| /*enable Q notification*/ |
| qStatus = ixQMgrNotificationEnable( |
| IX_ETH_ACC_PORT_TO_TX_Q_ID(portId), |
| IX_ETH_ACC_PORT_TO_TX_Q_SOURCE(portId)); |
| |
| if (qStatus != IX_SUCCESS && qStatus != IX_QMGR_WARNING) |
| { |
| TX_INC(portId,txUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthAccTxFromSwQ:Unexpected Error: %u\n", |
| qStatus, 0, 0, 0, 0, 0); |
| } |
| } |
| else |
| { |
| TX_INC(portId,txUnexpectedError); |
| |
| /* recovery attempt */ |
| IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD( |
| ixEthAccPortData[portId].ixEthAccTxData.txQ[priority], |
| mbuf); |
| |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthAccTxFromSwQ:Error: unexpected QM status 0x%08X\n", |
| qStatus, 0, 0, 0, 0, 0); |
| } |
| } |
| else |
| { |
| /* sw queue is empty */ |
| } |
| return IX_ETH_ACC_FAIL; |
| } |
| |
| /** |
| * |
| * @brief This function will take a buffer from a RXfree S/W Q and attempt |
| * to add it to the relevant RxFree H/W Q |
| * |
| * @param portId - the port whose RXFree queue is to be written to |
| * |
| * @internal |
| */ |
| PRIVATE IxEthAccStatus |
| ixEthAccRxFreeFromSwQ(IxEthAccPortId portId) |
| { |
| IX_OSAL_MBUF *mbuf; |
| IX_STATUS qStatus = IX_SUCCESS; |
| |
| IX_ETH_ACC_DATAPLANE_REMOVE_MBUF_FROM_Q_HEAD( |
| ixEthAccPortData[portId].ixEthAccRxData.freeBufferList, |
| mbuf); |
| if (mbuf != NULL) |
| { |
| /* |
| * Add The Rx Buffer to the H/W Free buffer Q if possible |
| */ |
| qStatus = ixEthAccQmgrLockRxWrite(portId, |
| IX_OSAL_MMU_VIRT_TO_PHYS( |
| (UINT32)IX_ETHACC_NE_SHARED(mbuf))); |
| |
| if (qStatus == IX_SUCCESS) |
| { |
| RX_STATS_INC(portId,rxFreeRepFromSwQOK); |
| /* |
| * Buffer added to h/w Q. |
| */ |
| return IX_SUCCESS; |
| } |
| else if (qStatus == IX_QMGR_Q_OVERFLOW) |
| { |
| /* |
| * H/W Q overflow, need to save the buffer back on the s/w Q. |
| */ |
| RX_STATS_INC(portId,rxFreeRepFromSwQDelayed); |
| |
| IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD( |
| ixEthAccPortData[portId].ixEthAccRxData.freeBufferList, |
| mbuf); |
| } |
| else |
| { |
| /* unexpected qmgr error */ |
| RX_INC(portId,rxUnexpectedError); |
| |
| IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD( |
| ixEthAccPortData[portId].ixEthAccRxData.freeBufferList, |
| mbuf); |
| |
| IX_ETH_ACC_FATAL_LOG("IxEthAccRxFreeFromSwQ:Error: unexpected QM status 0x%08X\n", |
| qStatus, 0, 0, 0, 0, 0); |
| } |
| } |
| else |
| { |
| /* sw queue is empty */ |
| } |
| return IX_ETH_ACC_FAIL; |
| } |
| |
| |
| IX_ETH_ACC_PUBLIC |
| IxEthAccStatus ixEthAccInitDataPlane() |
| { |
| UINT32 portId; |
| |
| /* |
| * Initialize the service and register callback to other services. |
| */ |
| |
| IX_ETH_ACC_MEMSET(&ixEthAccDataStats, |
| 0, |
| sizeof(ixEthAccDataStats)); |
| |
| for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++) |
| { |
| ixOsalFastMutexInit(&txWriteMutex[portId]); |
| ixOsalFastMutexInit(&rxWriteMutex[portId]); |
| |
| IX_ETH_ACC_MEMSET(&ixEthAccPortData[portId], |
| 0, |
| sizeof(ixEthAccPortData[portId])); |
| |
| ixEthAccPortData[portId].ixEthAccTxData.schDiscipline = FIFO_NO_PRIORITY; |
| } |
| |
| return (IX_ETH_ACC_SUCCESS); |
| } |
| |
| |
| IX_ETH_ACC_PUBLIC |
| IxEthAccStatus ixEthAccPortTxDoneCallbackRegister(IxEthAccPortId portId, |
| IxEthAccPortTxDoneCallback |
| txCallbackFn, |
| UINT32 callbackTag) |
| { |
| if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED()) |
| { |
| return (IX_ETH_ACC_FAIL); |
| } |
| if (!IX_ETH_ACC_IS_PORT_VALID(portId)) |
| { |
| return (IX_ETH_ACC_INVALID_PORT); |
| } |
| |
| /* HACK: removing this code to enable NPE-A preliminary testing |
| * if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId)) |
| * { |
| * IX_ETH_ACC_WARNING_LOG("ixEthAccPortTxDoneCallbackRegister: Unavailable Eth %d: Cannot register TxDone Callback.\n",(INT32)portId,0,0,0,0,0); |
| * return IX_ETH_ACC_SUCCESS ; |
| * } |
| */ |
| |
| if (!IX_ETH_IS_PORT_INITIALIZED(portId)) |
| { |
| return (IX_ETH_ACC_PORT_UNINITIALIZED); |
| } |
| if (txCallbackFn == 0) |
| /* Check for null function pointer here. */ |
| { |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| ixEthAccPortData[portId].ixEthAccTxData.txBufferDoneCallbackFn = txCallbackFn; |
| ixEthAccPortData[portId].ixEthAccTxData.txCallbackTag = callbackTag; |
| return (IX_ETH_ACC_SUCCESS); |
| } |
| |
| |
| IX_ETH_ACC_PUBLIC |
| IxEthAccStatus ixEthAccPortRxCallbackRegister(IxEthAccPortId portId, |
| IxEthAccPortRxCallback |
| rxCallbackFn, |
| UINT32 callbackTag) |
| { |
| IxEthAccPortId port; |
| |
| if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED()) |
| { |
| return (IX_ETH_ACC_FAIL); |
| } |
| if (!IX_ETH_ACC_IS_PORT_VALID(portId)) |
| { |
| return (IX_ETH_ACC_INVALID_PORT); |
| } |
| |
| if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId)) |
| { |
| IX_ETH_ACC_WARNING_LOG("ixEthAccPortRxCallbackRegister: Unavailable Eth %d: Cannot register Rx Callback.\n",(INT32)portId,0,0,0,0,0); |
| return IX_ETH_ACC_SUCCESS ; |
| } |
| |
| if (!IX_ETH_IS_PORT_INITIALIZED(portId)) |
| { |
| return (IX_ETH_ACC_PORT_UNINITIALIZED); |
| } |
| |
| /* Check for null function pointer here. */ |
| if (rxCallbackFn == NULL) |
| { |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| |
| /* Check the user is not changing the callback type |
| * when the port is enabled. |
| */ |
| if (ixEthAccMacState[portId].portDisableState == ACTIVE) |
| { |
| for (port = 0; port < IX_ETH_ACC_NUMBER_OF_PORTS; port++) |
| { |
| if ((ixEthAccMacState[port].portDisableState == ACTIVE) |
| && (ixEthAccPortData[port].ixEthAccRxData.rxMultiBufferCallbackInUse == true)) |
| { |
| /* one of the active ports has a different rx callback type. |
| * Changing the callback type when the port is enabled |
| * is not safe |
| */ |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| } |
| } |
| |
| /* update the callback pointer : this is done before |
| * registering the new qmgr callback |
| */ |
| ixEthAccPortData[portId].ixEthAccRxData.rxCallbackFn = rxCallbackFn; |
| ixEthAccPortData[portId].ixEthAccRxData.rxCallbackTag = callbackTag; |
| |
| /* update the qmgr callback for rx queues */ |
| if (ixEthAccQMgrRxCallbacksRegister(ixEthRxFrameQMCallback) |
| != IX_ETH_ACC_SUCCESS) |
| { |
| /* unexpected qmgr error */ |
| IX_ETH_ACC_FATAL_LOG("ixEthAccPortRxCallbackRegister: unexpected QMgr error, " \ |
| "could not register Rx single-buffer callback\n", 0, 0, 0, 0, 0, 0); |
| |
| RX_INC(portId,rxUnexpectedError); |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| |
| ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackInUse = false; |
| |
| return (IX_ETH_ACC_SUCCESS); |
| } |
| |
| IX_ETH_ACC_PUBLIC |
| IxEthAccStatus ixEthAccPortMultiBufferRxCallbackRegister( |
| IxEthAccPortId portId, |
| IxEthAccPortMultiBufferRxCallback |
| rxCallbackFn, |
| UINT32 callbackTag) |
| { |
| IxEthAccPortId port; |
| |
| if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED()) |
| { |
| return (IX_ETH_ACC_FAIL); |
| } |
| if (!IX_ETH_ACC_IS_PORT_VALID(portId)) |
| { |
| return (IX_ETH_ACC_INVALID_PORT); |
| } |
| |
| if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId)) |
| { |
| IX_ETH_ACC_WARNING_LOG("ixEthAccPortMultiBufferRxCallbackRegister: Unavailable Eth %d: Cannot register Rx Callback.\n",(INT32)portId,0,0,0,0,0); |
| return IX_ETH_ACC_SUCCESS ; |
| } |
| |
| if (!IX_ETH_IS_PORT_INITIALIZED(portId)) |
| { |
| return (IX_ETH_ACC_PORT_UNINITIALIZED); |
| } |
| |
| /* Check for null function pointer here. */ |
| if (rxCallbackFn == NULL) |
| { |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| |
| /* Check the user is not changing the callback type |
| * when the port is enabled. |
| */ |
| if (ixEthAccMacState[portId].portDisableState == ACTIVE) |
| { |
| for (port = 0; port < IX_ETH_ACC_NUMBER_OF_PORTS; port++) |
| { |
| if ((ixEthAccMacState[port].portDisableState == ACTIVE) |
| && (ixEthAccPortData[port].ixEthAccRxData.rxMultiBufferCallbackInUse == false)) |
| { |
| /* one of the active ports has a different rx callback type. |
| * Changing the callback type when the port is enabled |
| * is not safe |
| */ |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| } |
| } |
| |
| /* update the callback pointer : this is done before |
| * registering the new qmgr callback |
| */ |
| ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackFn = rxCallbackFn; |
| ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackTag = callbackTag; |
| |
| /* update the qmgr callback for rx queues */ |
| if (ixEthAccQMgrRxCallbacksRegister(ixEthRxMultiBufferQMCallback) |
| != IX_ETH_ACC_SUCCESS) |
| { |
| /* unexpected qmgr error */ |
| RX_INC(portId,rxUnexpectedError); |
| |
| IX_ETH_ACC_FATAL_LOG("ixEthAccPortMultiBufferRxCallbackRegister: unexpected QMgr error, " \ |
| "could not register Rx multi-buffer callback\n", 0, 0, 0, 0, 0, 0); |
| |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| |
| ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackInUse = true; |
| |
| return (IX_ETH_ACC_SUCCESS); |
| } |
| |
| IX_ETH_ACC_PUBLIC |
| IxEthAccStatus ixEthAccPortTxFrameSubmit(IxEthAccPortId portId, |
| IX_OSAL_MBUF *buffer, |
| IxEthAccTxPriority priority) |
| { |
| IX_STATUS qStatus = IX_SUCCESS; |
| UINT32 qBuffer; |
| IxEthAccTxPriority highestPriority; |
| IxQMgrQStatus txQStatus; |
| |
| #ifndef NDEBUG |
| if (buffer == NULL) |
| { |
| return (IX_ETH_ACC_FAIL); |
| } |
| if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED()) |
| { |
| return (IX_ETH_ACC_FAIL); |
| } |
| if (!IX_ETH_ACC_IS_PORT_VALID(portId)) |
| { |
| return (IX_ETH_ACC_INVALID_PORT); |
| } |
| |
| if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId)) |
| { |
| IX_ETH_ACC_FATAL_LOG("ixEthAccPortTxFrameSubmit: Unavailable Eth %d: Cannot submit Tx Frame.\n", |
| (INT32)portId,0,0,0,0,0); |
| return IX_ETH_ACC_PORT_UNINITIALIZED ; |
| } |
| |
| if (!IX_ETH_IS_PORT_INITIALIZED(portId)) |
| { |
| return (IX_ETH_ACC_PORT_UNINITIALIZED); |
| } |
| if ((UINT32)priority > (UINT32)IX_ETH_ACC_TX_PRIORITY_7) |
| { |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| #endif |
| |
| /* |
| * Need to Flush the MBUF and its contents (data) as it may be |
| * read from the NPE. Convert virtual addresses to physical addresses also. |
| */ |
| qBuffer = ixEthAccMbufTxQPrepare(buffer); |
| |
| /* |
| * If no fifo priority set on Xscale ... |
| */ |
| if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline == |
| FIFO_NO_PRIORITY) |
| { |
| /* |
| * Add The Tx Buffer to the H/W Tx Q if possible |
| * (the priority is passed to the NPE, because |
| * the NPE is able to reorder the frames |
| * before transmission to the underlying hardware) |
| */ |
| qStatus = ixEthAccQmgrTxWrite(portId, |
| qBuffer, |
| IX_ETH_ACC_TX_DEFAULT_PRIORITY); |
| |
| if (qStatus == IX_SUCCESS) |
| { |
| TX_STATS_INC(portId,txQOK); |
| |
| /* |
| * "best case" scenario : Buffer added to h/w Q. |
| */ |
| return (IX_SUCCESS); |
| } |
| else if (qStatus == IX_QMGR_Q_OVERFLOW) |
| { |
| /* |
| * We were unable to write the buffer to the |
| * appropriate H/W Q, Save it in the sw Q. |
| * (use the default priority queue regardless of |
| * input parameter) |
| */ |
| priority = IX_ETH_ACC_TX_DEFAULT_PRIORITY; |
| } |
| else |
| { |
| /* unexpected qmgr error */ |
| TX_INC(portId,txUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthAccPortTxFrameSubmit:Error: qStatus = %u\n", |
| (UINT32)qStatus, 0, 0, 0, 0, 0); |
| return (IX_ETH_ACC_FAIL); |
| } |
| } |
| else if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline == |
| FIFO_PRIORITY) |
| { |
| |
| /* |
| * For priority transmission, put the frame directly on the H/W queue |
| * if the H/W queue is empty, otherwise, put it in a S/W Q |
| */ |
| ixQMgrQStatusGet(IX_ETH_ACC_PORT_TO_TX_Q_ID(portId), &txQStatus); |
| if((txQStatus & IX_QMGR_Q_STATUS_E_BIT_MASK) != 0) |
| { |
| /*The tx queue is empty, check whether there are buffers on the s/w queues*/ |
| if(ixEthAccTxSwQHighestPriorityGet(portId, &highestPriority) |
| !=IX_ETH_ACC_FAIL) |
| { |
| /*there are buffers on the s/w queues, submit them*/ |
| ixEthAccTxFromSwQ(portId, highestPriority); |
| |
| /* the queue was empty, 1 buffer is already supplied |
| * but is likely to be immediately transmitted and the |
| * hw queue is likely to be empty again, so submit |
| * more from the sw queues |
| */ |
| if(ixEthAccTxSwQHighestPriorityGet(portId, &highestPriority) |
| !=IX_ETH_ACC_FAIL) |
| { |
| ixEthAccTxFromSwQ(portId, highestPriority); |
| /* |
| * and force the buffer supplied to be placed |
| * on a priority queue |
| */ |
| qStatus = IX_QMGR_Q_OVERFLOW; |
| } |
| else |
| { |
| /*there are no buffers in the s/w queues, submit directly*/ |
| qStatus = ixEthAccQmgrTxWrite(portId, qBuffer, priority); |
| } |
| } |
| else |
| { |
| /*there are no buffers in the s/w queues, submit directly*/ |
| qStatus = ixEthAccQmgrTxWrite(portId, qBuffer, priority); |
| } |
| } |
| else |
| { |
| qStatus = IX_QMGR_Q_OVERFLOW; |
| } |
| } |
| else |
| { |
| TX_INC(portId,txUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthAccPortTxFrameSubmit:Error: wrong schedule discipline setup\n", |
| 0, 0, 0, 0, 0, 0); |
| return (IX_ETH_ACC_FAIL); |
| } |
| |
| if(qStatus == IX_SUCCESS ) |
| { |
| TX_STATS_INC(portId,txQOK); |
| return IX_ETH_ACC_SUCCESS; |
| } |
| else if(qStatus == IX_QMGR_Q_OVERFLOW) |
| { |
| TX_STATS_INC(portId,txQDelayed); |
| /* |
| * We were unable to write the buffer to the |
| * appropriate H/W Q, Save it in a s/w Q. |
| */ |
| IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_TAIL( |
| ixEthAccPortData[portId]. |
| ixEthAccTxData.txQ[priority], |
| buffer); |
| |
| qStatus = ixQMgrNotificationEnable( |
| IX_ETH_ACC_PORT_TO_TX_Q_ID(portId), |
| IX_ETH_ACC_PORT_TO_TX_Q_SOURCE(portId)); |
| |
| if (qStatus != IX_SUCCESS) |
| { |
| if (qStatus == IX_QMGR_WARNING) |
| { |
| /* notification is enabled for a queue |
| * which is already empty (the condition is already met) |
| * and there will be no more queue event to drain the sw queue |
| */ |
| TX_STATS_INC(portId,txLateNotificationEnabled); |
| |
| /* pull a buffer from the sw queue */ |
| if(ixEthAccTxSwQHighestPriorityGet(portId, &highestPriority) |
| !=IX_ETH_ACC_FAIL) |
| { |
| /*there are buffers on the s/w queues, submit from them*/ |
| ixEthAccTxFromSwQ(portId, highestPriority); |
| } |
| } |
| else |
| { |
| TX_INC(portId,txUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthAccPortTxFrameSubmit: unexpected Error: %u\n", |
| qStatus, 0, 0, 0, 0, 0); |
| } |
| } |
| } |
| else |
| { |
| TX_INC(portId,txUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthAccPortTxFrameSubmit: unexpected Error: %u\n", |
| qStatus, 0, 0, 0, 0, 0); |
| return (IX_ETH_ACC_FAIL); |
| } |
| |
| return (IX_ETH_ACC_SUCCESS); |
| } |
| |
| |
| /** |
| * |
| * @brief replenish: convert a chain of mbufs to the format |
| * expected by the NPE |
| * |
| */ |
| |
| IX_ETH_ACC_PUBLIC |
| IxEthAccStatus ixEthAccPortRxFreeReplenish(IxEthAccPortId portId, |
| IX_OSAL_MBUF *buffer) |
| { |
| IX_STATUS qStatus = IX_SUCCESS; |
| UINT32 qBuffer; |
| |
| /* |
| * Check buffer is valid. |
| */ |
| |
| #ifndef NDEBUG |
| /* check parameter value */ |
| if (buffer == 0) |
| { |
| return (IX_ETH_ACC_FAIL); |
| } |
| if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED()) |
| { |
| return (IX_ETH_ACC_FAIL); |
| } |
| if (!IX_ETH_ACC_IS_PORT_VALID(portId)) |
| { |
| return (IX_ETH_ACC_INVALID_PORT); |
| } |
| |
| /* check initialisation is done */ |
| if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId)) |
| { |
| IX_ETH_ACC_FATAL_LOG(" ixEthAccPortRxFreeReplenish: Unavailable Eth %d: Cannot replenish Rx Free Q.\n",(INT32)portId,0,0,0,0,0); |
| return IX_ETH_ACC_PORT_UNINITIALIZED ; |
| } |
| |
| if (!IX_ETH_IS_PORT_INITIALIZED(portId)) |
| { |
| return (IX_ETH_ACC_PORT_UNINITIALIZED); |
| } |
| /* check boundaries and constraints */ |
| if (IX_OSAL_MBUF_MLEN(buffer) < IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN) |
| { |
| return (IX_ETH_ACC_FAIL); |
| } |
| #endif |
| |
| qBuffer = ixEthAccMbufRxQPrepare(buffer); |
| |
| /* |
| * Add The Rx Buffer to the H/W Free buffer Q if possible |
| */ |
| qStatus = ixEthAccQmgrLockRxWrite(portId, qBuffer); |
| |
| if (qStatus == IX_SUCCESS) |
| { |
| RX_STATS_INC(portId,rxFreeRepOK); |
| /* |
| * Buffer added to h/w Q. |
| */ |
| return (IX_SUCCESS); |
| } |
| else if (qStatus == IX_QMGR_Q_OVERFLOW) |
| { |
| RX_STATS_INC(portId,rxFreeRepDelayed); |
| /* |
| * We were unable to write the buffer to the approprate H/W Q, |
| * Save it in a s/w Q. |
| */ |
| IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_TAIL( |
| ixEthAccPortData[portId].ixEthAccRxData.freeBufferList, |
| buffer); |
| |
| qStatus = ixQMgrNotificationEnable( |
| IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId), |
| IX_ETH_ACC_PORT_TO_RX_FREE_Q_SOURCE(portId)); |
| |
| if (qStatus != IX_SUCCESS) |
| { |
| if (qStatus == IX_QMGR_WARNING) |
| { |
| /* notification is enabled for a queue |
| * which is already empty (the condition is already met) |
| * and there will be no more queue event to drain the sw queue |
| * move an entry from the sw queue to the hw queue */ |
| RX_STATS_INC(portId,rxFreeLateNotificationEnabled); |
| ixEthAccRxFreeFromSwQ(portId); |
| } |
| else |
| { |
| RX_INC(portId,rxUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthAccRxPortFreeReplenish:Error: %u\n", |
| qStatus, 0, 0, 0, 0, 0); |
| } |
| } |
| } |
| else |
| { |
| RX_INC(portId,rxUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthAccRxPortFreeReplenish:Error: qStatus = %u\n", |
| (UINT32)qStatus, 0, 0, 0, 0, 0); |
| return(IX_ETH_ACC_FAIL); |
| } |
| return (IX_ETH_ACC_SUCCESS); |
| } |
| |
| |
| IX_ETH_ACC_PUBLIC |
| IxEthAccStatus ixEthAccTxSchedulingDisciplineSetPriv(IxEthAccPortId portId, |
| IxEthAccSchedulerDiscipline |
| sched) |
| { |
| if (!IX_ETH_ACC_IS_PORT_VALID(portId)) |
| { |
| return (IX_ETH_ACC_INVALID_PORT); |
| } |
| |
| if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId)) |
| { |
| IX_ETH_ACC_WARNING_LOG("ixEthAccTxSchedulingDisciplineSet: Unavailable Eth %d: Cannot set Tx Scheduling Discipline.\n",(INT32)portId,0,0,0,0,0); |
| return IX_ETH_ACC_SUCCESS ; |
| } |
| |
| if (!IX_ETH_IS_PORT_INITIALIZED(portId)) |
| { |
| return (IX_ETH_ACC_PORT_UNINITIALIZED); |
| } |
| |
| if (sched != FIFO_PRIORITY && sched != FIFO_NO_PRIORITY) |
| { |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| |
| ixEthAccPortData[portId].ixEthAccTxData.schDiscipline = sched; |
| return (IX_ETH_ACC_SUCCESS); |
| } |
| |
| IX_ETH_ACC_PUBLIC |
| IxEthAccStatus ixEthAccRxSchedulingDisciplineSetPriv(IxEthAccSchedulerDiscipline |
| sched) |
| { |
| if (sched != FIFO_PRIORITY && sched != FIFO_NO_PRIORITY) |
| { |
| return (IX_ETH_ACC_INVALID_ARG); |
| } |
| |
| ixEthAccDataInfo.schDiscipline = sched; |
| |
| return (IX_ETH_ACC_SUCCESS); |
| } |
| |
| |
| /** |
| * @fn ixEthRxFrameProcess(IxEthAccPortId portId, IX_OSAL_MBUF *mbufPtr) |
| * |
| * @brief process incoming frame : |
| * |
| * @param @ref IxQMgrCallback IxQMgrMultiBufferCallback |
| * |
| * @return none |
| * |
| * @internal |
| * |
| */ |
| IX_ETH_ACC_PRIVATE BOOL |
| ixEthRxFrameProcess(IxEthAccPortId portId, IX_OSAL_MBUF *mbufPtr) |
| { |
| UINT32 flags; |
| IxEthDBStatus result; |
| |
| #ifndef NDEBUG |
| /* Prudent to at least check the port is within range */ |
| if (portId >= IX_ETH_ACC_NUMBER_OF_PORTS) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthRxFrameProcess: Illegal port: %u\n", |
| (UINT32)portId, 0, 0, 0, 0, 0); |
| return false; |
| } |
| #endif |
| |
| /* convert fields from mbuf header */ |
| ixEthAccMbufFromRxQ(mbufPtr); |
| |
| /* check about any special processing for this frame */ |
| flags = IX_ETHACC_NE_FLAGS(mbufPtr); |
| if ((flags & (IX_ETHACC_NE_FILTERMASK | IX_ETHACC_NE_NEWSRCMASK)) == 0) |
| { |
| /* "best case" scenario : nothing special to do for this frame */ |
| return true; |
| } |
| |
| #ifdef CONFIG_IXP425_COMPONENT_ETHDB |
| /* if a new source MAC address is detected by the NPE, |
| * update IxEthDB with the portId and the MAC address. |
| */ |
| if ((flags & IX_ETHACC_NE_NEWSRCMASK & ixEthAccNewSrcMask) != 0) |
| { |
| result = ixEthDBFilteringDynamicEntryProvision(portId, |
| (IxEthDBMacAddr *) IX_ETHACC_NE_SOURCEMAC(mbufPtr)); |
| |
| if (result != IX_ETH_DB_SUCCESS && result != IX_ETH_DB_FEATURE_UNAVAILABLE) |
| { |
| if ((ixEthAccMacState[portId].portDisableState == ACTIVE) && (result != IX_ETH_DB_BUSY)) |
| { |
| RX_STATS_INC(portId, rxUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG("ixEthRxFrameProcess: Failed to add source MAC \ |
| to the Learning/Filtering database\n", 0, 0, 0, 0, 0, 0); |
| } |
| else |
| { |
| /* we expect this to fail during PortDisable, as EthDB is disabled for |
| * that port and will refuse to learn new addresses |
| */ |
| } |
| } |
| else |
| { |
| RX_STATS_INC(portId, rxUnlearnedMacAddress); |
| } |
| } |
| #endif |
| |
| /* check if this frame should have been filtered |
| * by the NPE and take the appropriate action |
| */ |
| if (((flags & IX_ETHACC_NE_FILTERMASK) != 0) |
| && (ixEthAccMacState[portId].portDisableState == ACTIVE)) |
| { |
| /* If the mbuf was allocated with a small data size, or the current data pointer is not |
| * within the allocated data area, then the buffer is non-standard and has to be |
| * replenished with the minimum size only |
| */ |
| if( (IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr) < IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN) |
| || ((UINT8 *)IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr) > IX_OSAL_MBUF_MDATA(mbufPtr)) |
| || ((UINT8 *)(IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr) + |
| IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr)) |
| < IX_OSAL_MBUF_MDATA(mbufPtr)) ) |
| { |
| /* set to minimum length */ |
| IX_OSAL_MBUF_MLEN(mbufPtr) = IX_OSAL_MBUF_PKT_LEN(mbufPtr) = |
| IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN; |
| } |
| else |
| { |
| /* restore original length */ |
| IX_OSAL_MBUF_MLEN(mbufPtr) = IX_OSAL_MBUF_PKT_LEN(mbufPtr) = |
| ( IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr) - |
| (IX_OSAL_MBUF_MDATA(mbufPtr) - (UINT8 *)IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr)) ); |
| } |
| |
| /* replenish from here */ |
| if (ixEthAccPortRxFreeReplenish(portId, mbufPtr) != IX_ETH_ACC_SUCCESS) |
| { |
| IX_ETH_ACC_FATAL_LOG("ixEthRxFrameProcess: Failed to replenish with filtered frame\ |
| on port %d\n", portId, 0, 0, 0, 0, 0); |
| } |
| |
| RX_STATS_INC(portId, rxFiltered); |
| |
| /* indicate that frame should not be subjected to further processing */ |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| /** |
| * @fn ixEthRxFrameQMCallback |
| * |
| * @brief receive callback for Frame receive Q from NPE |
| * |
| * Frames are passed one-at-a-time to the user |
| * |
| * @param @ref IxQMgrCallback |
| * |
| * @return none |
| * |
| * @internal |
| * |
| * Design note : while processing the entry X, entry X+1 is preloaded |
| * into memory to reduce the number of stall cycles |
| * |
| */ |
| void ixEthRxFrameQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId) |
| { |
| IX_OSAL_MBUF *mbufPtr; |
| IX_OSAL_MBUF *nextMbufPtr; |
| UINT32 qEntry; |
| UINT32 nextQEntry; |
| UINT32 *qEntryPtr; |
| UINT32 portId; |
| UINT32 destPortId; |
| UINT32 npeId; |
| UINT32 rxQReadStatus; |
| |
| /* |
| * Design note : entries are read in a buffer, This buffer contains |
| * an extra zeroed entry so the loop will |
| * always terminate on a null entry, whatever the result of Burst read is. |
| */ |
| UINT32 rxQEntry[IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK + 1]; |
| |
| /* |
| * Indication of the number of times the callback is used. |
| */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackCounter); |
| |
| do |
| { |
| /* |
| * Indication of the number of times the queue is drained |
| */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackBurstRead); |
| |
| /* ensure the last entry of the array contains a zeroed value */ |
| qEntryPtr = rxQEntry; |
| qEntryPtr[IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK] = 0; |
| |
| rxQReadStatus = ixQMgrQBurstRead(qId, |
| IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK, |
| qEntryPtr); |
| |
| #ifndef NDEBUG |
| if ((rxQReadStatus != IX_QMGR_Q_UNDERFLOW) |
| && (rxQReadStatus != IX_SUCCESS)) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| /*major error*/ |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthRxFrameQMCallback:Error: %u\n", |
| (UINT32)rxQReadStatus, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| |
| /* convert and preload the next entry |
| * (the conversion function takes care about null pointers which |
| * are used to mark the end of the loop) |
| */ |
| nextQEntry = *qEntryPtr; |
| nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, |
| IX_ETHNPE_QM_Q_RXENET_ADDR_MASK); |
| |
| while(nextQEntry != 0) |
| { |
| /* get the next entry */ |
| qEntry = nextQEntry; |
| mbufPtr = nextMbufPtr; |
| |
| #ifndef NDEBUG |
| if (mbufPtr == NULL) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthRxFrameQMCallback: Null Mbuf Ptr\n", |
| 0, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| |
| /* convert the next entry |
| * (the conversion function takes care about null pointers which |
| * are used to mark the end of the loop) |
| */ |
| nextQEntry = *(++qEntryPtr); |
| nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, |
| IX_ETHNPE_QM_Q_RXENET_ADDR_MASK); |
| |
| /* |
| * Get Port and Npe ID from message. |
| */ |
| npeId = ((IX_ETHNPE_QM_Q_RXENET_NPEID_MASK & |
| qEntry) >> IX_ETHNPE_QM_Q_FIELD_NPEID_R); |
| portId = IX_ETH_ACC_NPE_TO_PORT_ID(npeId); |
| |
| /* process frame, check the return code and skip the remaining of |
| * the loop if the frame is to be filtered out |
| */ |
| if (ixEthRxFrameProcess(portId, mbufPtr)) |
| { |
| /* destination portId for this packet */ |
| destPortId = IX_ETHACC_NE_DESTPORTID(mbufPtr); |
| |
| if (destPortId != IX_ETH_DB_UNKNOWN_PORT) |
| { |
| destPortId = IX_ETH_DB_NPE_LOGICAL_ID_TO_PORT_ID(destPortId); |
| } |
| |
| /* test if QoS is enabled in ethAcc |
| */ |
| if (ixEthAccDataInfo.schDiscipline == FIFO_PRIORITY) |
| { |
| /* check if there is a higher priority queue |
| * which may require processing and then process it. |
| */ |
| if (ixEthAccDataInfo.higherPriorityQueue[qId] < IX_QMGR_MAX_NUM_QUEUES) |
| { |
| ixEthRxFrameQMCallback(ixEthAccDataInfo.higherPriorityQueue[qId], |
| callbackId); |
| } |
| } |
| |
| /* |
| * increment priority stats |
| */ |
| RX_STATS_INC(portId,rxPriority[IX_ETHACC_NE_QOS(mbufPtr)]); |
| |
| /* |
| * increment callback count stats |
| */ |
| RX_STATS_INC(portId,rxFrameClientCallback); |
| |
| /* |
| * Call user level callback. |
| */ |
| ixEthAccPortData[portId].ixEthAccRxData.rxCallbackFn( |
| ixEthAccPortData[portId].ixEthAccRxData.rxCallbackTag, |
| mbufPtr, |
| destPortId); |
| } |
| } |
| } while (rxQReadStatus == IX_SUCCESS); |
| } |
| |
| /** |
| * @fn ixEthRxMultiBufferQMCallback |
| * |
| * @brief receive callback for Frame receive Q from NPE |
| * |
| * Frames are passed as an array to the user |
| * |
| * @param @ref IxQMgrCallback |
| * |
| * @return none |
| * |
| * @internal |
| * |
| * Design note : while processing the entry X, entry X+1 is preloaded |
| * into memory to reduce the number of stall cycles |
| * |
| */ |
| void ixEthRxMultiBufferQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId) |
| { |
| IX_OSAL_MBUF *mbufPtr; |
| IX_OSAL_MBUF *nextMbufPtr; |
| UINT32 qEntry; |
| UINT32 nextQEntry; |
| UINT32 *qEntryPtr; |
| UINT32 portId; |
| UINT32 npeId; |
| UINT32 rxQReadStatus; |
| /* |
| * Design note : entries are read in a static buffer, This buffer contains |
| * an extra zeroed entry so the loop will |
| * always terminate on a null entry, whatever the result of Burst read is. |
| */ |
| static UINT32 rxQEntry[IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK + 1]; |
| static IX_OSAL_MBUF *rxMbufPortArray[IX_ETH_ACC_NUMBER_OF_PORTS][IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK + 1]; |
| IX_OSAL_MBUF **rxMbufPtr[IX_ETH_ACC_NUMBER_OF_PORTS]; |
| |
| for (portId = 0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++) |
| { |
| rxMbufPtr[portId] = rxMbufPortArray[portId]; |
| } |
| |
| /* |
| * Indication of the number of times the callback is used. |
| */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackCounter); |
| |
| do |
| { |
| /* |
| * Indication of the number of times the queue is drained |
| */ |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackBurstRead); |
| |
| /* ensure the last entry of the array contains a zeroed value */ |
| qEntryPtr = rxQEntry; |
| qEntryPtr[IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK] = 0; |
| |
| rxQReadStatus = ixQMgrQBurstRead(qId, |
| IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK, |
| qEntryPtr); |
| |
| #ifndef NDEBUG |
| if ((rxQReadStatus != IX_QMGR_Q_UNDERFLOW) |
| && (rxQReadStatus != IX_SUCCESS)) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| /*major error*/ |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthRxFrameMultiBufferQMCallback:Error: %u\n", |
| (UINT32)rxQReadStatus, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| |
| /* convert and preload the next entry |
| * (the conversion function takes care about null pointers which |
| * are used to mark the end of the loop) |
| */ |
| nextQEntry = *qEntryPtr; |
| nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, |
| IX_ETHNPE_QM_Q_RXENET_ADDR_MASK); |
| |
| while(nextQEntry != 0) |
| { |
| /* get the next entry */ |
| qEntry = nextQEntry; |
| mbufPtr = nextMbufPtr; |
| |
| #ifndef NDEBUG |
| if (mbufPtr == NULL) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthRxFrameMultiBufferQMCallback:Error: Null Mbuf Ptr\n", |
| 0, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| |
| /* convert the next entry |
| * (the conversion function takes care about null pointers which |
| * are used to mark the end of the loop) |
| */ |
| nextQEntry = *(++qEntryPtr); |
| nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, |
| IX_ETHNPE_QM_Q_RXENET_ADDR_MASK); |
| |
| /* |
| * Get Port and Npe ID from message. |
| */ |
| npeId = ((IX_ETHNPE_QM_Q_RXENET_NPEID_MASK & |
| qEntry) >> |
| IX_ETHNPE_QM_Q_FIELD_NPEID_R); |
| portId = IX_ETH_ACC_NPE_TO_PORT_ID(npeId); |
| |
| /* skip the remaining of the loop if the frame is |
| * to be filtered out |
| */ |
| if (ixEthRxFrameProcess(portId, mbufPtr)) |
| { |
| /* store a mbuf pointer in an array */ |
| *rxMbufPtr[portId]++ = mbufPtr; |
| |
| /* |
| * increment priority stats |
| */ |
| RX_STATS_INC(portId,rxPriority[IX_ETHACC_NE_QOS(mbufPtr)]); |
| } |
| |
| /* test for QoS enabled in ethAcc */ |
| if (ixEthAccDataInfo.schDiscipline == FIFO_PRIORITY) |
| { |
| /* check if there is a higher priority queue |
| * which may require processing and then process it. |
| */ |
| if (ixEthAccDataInfo.higherPriorityQueue[qId] < IX_QMGR_MAX_NUM_QUEUES) |
| { |
| ixEthRxMultiBufferQMCallback(ixEthAccDataInfo.higherPriorityQueue[qId], |
| callbackId); |
| } |
| } |
| } |
| |
| /* check if any of the the arrays contains any entry */ |
| for (portId = 0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++) |
| { |
| if (rxMbufPtr[portId] != rxMbufPortArray[portId]) |
| { |
| /* add a last NULL pointer at the end of the |
| * array of mbuf pointers |
| */ |
| *rxMbufPtr[portId] = NULL; |
| |
| /* |
| * increment callback count stats |
| */ |
| RX_STATS_INC(portId,rxFrameClientCallback); |
| |
| /* |
| * Call user level callback with an array of |
| * buffers (NULL terminated) |
| */ |
| ixEthAccPortData[portId].ixEthAccRxData. |
| rxMultiBufferCallbackFn( |
| ixEthAccPortData[portId].ixEthAccRxData. |
| rxMultiBufferCallbackTag, |
| rxMbufPortArray[portId]); |
| |
| /* reset the buffer pointer to the beginning of |
| * the array |
| */ |
| rxMbufPtr[portId] = rxMbufPortArray[portId]; |
| } |
| } |
| |
| } while (rxQReadStatus == IX_SUCCESS); |
| } |
| |
| |
| /** |
| * @brief rxFree low event handler |
| * |
| */ |
| void ixEthRxFreeQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId) |
| { |
| IxEthAccPortId portId = (IxEthAccPortId) callbackId; |
| int lockVal; |
| UINT32 maxQWritesToPerform = IX_ETH_ACC_MAX_RX_FREE_BUFFERS_LOAD; |
| IX_STATUS qStatus = IX_SUCCESS; |
| |
| /* |
| * We have reached a low threshold on one of the Rx Free Qs |
| */ |
| |
| /*note that due to the fact that we are working off an Empty threshold, this callback |
| need only write a single entry to the Rx Free queue in order to re-arm the notification |
| */ |
| |
| RX_STATS_INC(portId,rxFreeLowCallback); |
| |
| /* |
| * Get buffers from approprite S/W Rx freeBufferList Q. |
| */ |
| |
| #ifndef NDEBUG |
| if (!IX_ETH_ACC_IS_PORT_VALID(portId)) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthRxFreeQMCallback:Error: Invalid Port 0x%08X\n", |
| portId, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| IX_ETH_ACC_DATA_PLANE_LOCK(lockVal); |
| if (IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId]. |
| ixEthAccRxData.freeBufferList)) |
| { |
| /* |
| * Turn off Q callback notification for Q in Question. |
| */ |
| qStatus = ixQMgrNotificationDisable( |
| IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId)); |
| |
| |
| IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal); |
| |
| if (qStatus != IX_SUCCESS) |
| { |
| RX_INC(portId,rxUnexpectedError); |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthRxFreeQMCallback:Error: unexpected QM status 0x%08X\n", |
| qStatus, 0, 0, 0, 0, 0); |
| return; |
| } |
| } |
| else |
| { |
| IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal); |
| /* |
| * Load the H/W Q with buffers from the s/w Q. |
| */ |
| |
| do |
| { |
| /* |
| * Consume Q entries. - Note Q contains Physical addresss, |
| * and have already been flushed to memory, |
| * And endianess converted if required. |
| */ |
| if (ixEthAccRxFreeFromSwQ(portId) != IX_SUCCESS) |
| { |
| /* |
| * No more entries in s/w Q. |
| * Turn off Q callback indication |
| */ |
| |
| IX_ETH_ACC_DATA_PLANE_LOCK(lockVal); |
| if (IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId]. |
| ixEthAccRxData.freeBufferList)) |
| { |
| qStatus = ixQMgrNotificationDisable( |
| IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId)); |
| } |
| IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal); |
| break; |
| } |
| } |
| while (--maxQWritesToPerform); |
| } |
| } |
| /** |
| * @fn Tx queue low event handler |
| * |
| */ |
| void |
| ixEthTxFrameQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId) |
| { |
| IxEthAccPortId portId = (IxEthAccPortId) callbackId; |
| int lockVal; |
| UINT32 maxQWritesToPerform = IX_ETH_ACC_MAX_TX_FRAME_TX_CONSUME_PER_CALLBACK; |
| IX_STATUS qStatus = IX_SUCCESS; |
| IxEthAccTxPriority highestPriority; |
| |
| |
| /* |
| * We have reached a low threshold on the Tx Q, and are being asked to |
| * supply a buffer for transmission from our S/W TX queues |
| */ |
| TX_STATS_INC(portId,txLowThreshCallback); |
| |
| /* |
| * Get buffers from approprite Q. |
| */ |
| |
| #ifndef NDEBUG |
| if (!IX_ETH_ACC_IS_PORT_VALID(portId)) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthTxFrameQMCallback:Error: Invalid Port 0x%08X\n", |
| portId, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| |
| do |
| { |
| /* |
| * Consume Q entries. - Note Q contains Physical addresss, |
| * and have already been flushed to memory, |
| * and endianess already sone if required. |
| */ |
| |
| IX_ETH_ACC_DATA_PLANE_LOCK(lockVal); |
| |
| if(ixEthAccTxSwQHighestPriorityGet(portId, &highestPriority) == |
| IX_ETH_ACC_FAIL) |
| { |
| /* |
| * No more entries in s/w Q. |
| * Turn off Q callback indication |
| */ |
| qStatus = ixQMgrNotificationDisable( |
| IX_ETH_ACC_PORT_TO_TX_Q_ID(portId)); |
| |
| IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal); |
| |
| if (qStatus != IX_SUCCESS) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthTxFrameQMCallback:Error: unexpected QM status 0x%08X\n", |
| qStatus, 0, 0, 0, 0, 0); |
| } |
| |
| return; |
| } |
| else |
| { |
| IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal); |
| if (ixEthAccTxFromSwQ(portId,highestPriority)!=IX_SUCCESS) |
| { |
| /* nothing left in the sw queue or the hw queues are |
| * full. There is no point to continue to drain the |
| * sw queues |
| */ |
| return; |
| } |
| } |
| } |
| while (--maxQWritesToPerform); |
| } |
| |
| /** |
| * @brief TxDone event handler |
| * |
| * Design note : while processing the entry X, entry X+1 is preloaded |
| * into memory to reduce the number of stall cycles |
| * |
| */ |
| |
| void |
| ixEthTxFrameDoneQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId) |
| { |
| IX_OSAL_MBUF *mbufPtr; |
| UINT32 qEntry; |
| UINT32 *qEntryPtr; |
| UINT32 txDoneQReadStatus; |
| UINT32 portId; |
| UINT32 npeId; |
| |
| /* |
| * Design note : entries are read in a static buffer, This buffer contains |
| * an extra entyry (which is zeroed by the compiler), so the loop will |
| * always terminate on a null entry, whatever the result of Burst read is. |
| */ |
| static UINT32 txDoneQEntry[IX_ETH_ACC_MAX_TX_FRAME_DONE_CONSUME_PER_CALLBACK + 1]; |
| |
| /* |
| * Indication that Tx frames have been transmitted from the NPE. |
| */ |
| |
| IX_ETH_ACC_STATS_INC(ixEthAccDataStats.txDoneCallbackCounter); |
| |
| do{ |
| qEntryPtr = txDoneQEntry; |
| txDoneQReadStatus = ixQMgrQBurstRead(IX_ETH_ACC_TX_FRAME_DONE_ETH_Q, |
| IX_ETH_ACC_MAX_TX_FRAME_DONE_CONSUME_PER_CALLBACK, |
| qEntryPtr); |
| |
| #ifndef NDEBUG |
| if (txDoneQReadStatus != IX_QMGR_Q_UNDERFLOW |
| && (txDoneQReadStatus != IX_SUCCESS)) |
| { |
| /*major error*/ |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthTxFrameDoneQMCallback:Error: %u\n", |
| (UINT32)txDoneQReadStatus, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| |
| qEntry = *qEntryPtr; |
| |
| while(qEntry != 0) |
| { |
| mbufPtr = ixEthAccEntryFromQConvert(qEntry, |
| IX_ETHNPE_QM_Q_TXENET_ADDR_MASK); |
| |
| #ifndef NDEBUG |
| if (mbufPtr == NULL) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthTxFrameDoneQMCallback:Error: Null Mbuf Ptr\n", |
| 0, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| |
| /* endianness conversions and stats updates */ |
| ixEthAccMbufFromTxQ(mbufPtr); |
| |
| /* |
| * Get NPE id from message, then convert to portId. |
| */ |
| npeId = ((IX_ETHNPE_QM_Q_TXENETDONE_NPEID_MASK & |
| qEntry) >> |
| IX_ETHNPE_QM_Q_FIELD_NPEID_R); |
| portId = IX_ETH_ACC_NPE_TO_PORT_ID(npeId); |
| |
| #ifndef NDEBUG |
| /* Prudent to at least check the port is within range */ |
| if (portId >= IX_ETH_ACC_NUMBER_OF_PORTS) |
| { |
| ixEthAccDataStats.unexpectedError++; |
| IX_ETH_ACC_FATAL_LOG( |
| "ixEthTxFrameDoneQMCallback: Illegal port: %u\n", |
| (UINT32)portId, 0, 0, 0, 0, 0); |
| return; |
| } |
| #endif |
| |
| TX_STATS_INC(portId,txDoneClientCallback); |
| |
| /* |
| * Call user level callback. |
| */ |
| ixEthAccPortData[portId].ixEthAccTxData.txBufferDoneCallbackFn( |
| ixEthAccPortData[portId].ixEthAccTxData.txCallbackTag, |
| mbufPtr); |
| |
| /* move to next queue entry */ |
| qEntry = *(++qEntryPtr); |
| |
| } |
| } while( txDoneQReadStatus == IX_SUCCESS ); |
| } |
| |
| IX_ETH_ACC_PUBLIC |
| void ixEthAccDataPlaneShow(void) |
| { |
| UINT32 numTx0Entries; |
| UINT32 numTx1Entries; |
| UINT32 numTxDoneEntries; |
| UINT32 numRxEntries; |
| UINT32 numRxFree0Entries; |
| UINT32 numRxFree1Entries; |
| UINT32 portId; |
| #ifdef __ixp46X |
| UINT32 numTx2Entries; |
| UINT32 numRxFree2Entries; |
| #endif |
| #ifndef NDEBUG |
| UINT32 priority; |
| UINT32 numBuffersInRx=0; |
| UINT32 numBuffersInTx=0; |
| UINT32 numBuffersInSwQ=0; |
| UINT32 totalBuffers=0; |
| UINT32 rxFreeCallbackCounter = 0; |
| UINT32 txCallbackCounter = 0; |
| #endif |
| UINT32 key; |
| |
| /* snapshot of stats */ |
| IxEthAccTxDataStats tx[IX_ETH_ACC_NUMBER_OF_PORTS]; |
| IxEthAccRxDataStats rx[IX_ETH_ACC_NUMBER_OF_PORTS]; |
| IxEthAccDataPlaneStats stats; |
| |
| if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED()) |
| { |
| return; |
| } |
| |
| /* get a reliable snapshot */ |
| key = ixOsalIrqLock(); |
| |
| numTx0Entries = 0; |
| ixQMgrQNumEntriesGet(IX_ETH_ACC_TX_FRAME_ENET0_Q, &numTx0Entries); |
| numTx1Entries = 0; |
| ixQMgrQNumEntriesGet(IX_ETH_ACC_TX_FRAME_ENET1_Q, &numTx1Entries); |
| numTxDoneEntries = 0; |
| ixQMgrQNumEntriesGet( IX_ETH_ACC_TX_FRAME_DONE_ETH_Q, &numTxDoneEntries); |
| numRxEntries = 0; |
| ixEthAccQMgrRxQEntryGet(&numRxEntries); |
| numRxFree0Entries = 0; |
| ixQMgrQNumEntriesGet(IX_ETH_ACC_RX_FREE_BUFF_ENET0_Q, &numRxFree0Entries); |
| numRxFree1Entries = 0; |
| ixQMgrQNumEntriesGet(IX_ETH_ACC_RX_FREE_BUFF_ENET1_Q, &numRxFree1Entries); |
| |
| #ifdef __ixp46X |
| numTx2Entries = 0; |
| ixQMgrQNumEntriesGet(IX_ETH_ACC_TX_FRAME_ENET2_Q, &numTx2Entries); |
| numRxFree2Entries = 0; |
| ixQMgrQNumEntriesGet(IX_ETH_ACC_RX_FREE_BUFF_ENET2_Q, &numRxFree2Entries); |
| #endif |
| |
| for(portId=IX_ETH_PORT_1; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++) |
| { |
| memcpy(&tx[portId], |
| &ixEthAccPortData[portId].ixEthAccTxData.stats, |
| sizeof(tx[portId])); |
| memcpy(&rx[portId], |
| &ixEthAccPortData[portId].ixEthAccRxData.stats, |
| sizeof(rx[portId])); |
| } |
| memcpy(&stats, &ixEthAccDataStats, sizeof(stats)); |
| |
| ixOsalIrqUnlock(key); |
| |
| #ifdef NDEBUG |
| printf("Detailed statistics collection not supported in this load\n"); |
| #endif |
| |
| /* print snapshot */ |
| for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++) |
| { |
| /* If not IXP42X A0 stepping, proceed to check for existence of coprocessors */ |
| if ((IX_FEATURE_CTRL_SILICON_TYPE_A0 != |
| (ixFeatureCtrlProductIdRead() & IX_FEATURE_CTRL_SILICON_STEPPING_MASK)) |
| || (IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X != ixFeatureCtrlDeviceRead ())) |
| { |
| if ((IX_ETH_PORT_1 == portId) && |
| (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH0) == |
| IX_FEATURE_CTRL_COMPONENT_DISABLED)) |
| { |
| continue ; |
| } |
| if ((IX_ETH_PORT_2 == portId) && |
| (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH1) == |
| IX_FEATURE_CTRL_COMPONENT_DISABLED)) |
| { |
| continue ; |
| } |
| if ((IX_ETH_PORT_3 == portId) && |
| (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_NPEA_ETH) == |
| IX_FEATURE_CTRL_COMPONENT_DISABLED)) |
| { |
| continue ; |
| } |
| } |
| |
| printf("PORT %u --------------------------------\n", |
| portId); |
| #ifndef NDEBUG |
| printf("Tx Done Frames : %u\n", |
| tx[portId].txDoneClientCallback + |
| tx[portId].txDoneSwQDuringDisable + |
| tx[portId].txDoneDuringDisable); |
| printf("Tx Frames : %u\n", |
| tx[portId].txQOK + tx[portId].txQDelayed); |
| printf("Tx H/W Q Added OK : %u\n", |
| tx[portId].txQOK); |
| printf("Tx H/W Q Delayed : %u\n", |
| tx[portId].txQDelayed); |
| printf("Tx From S/W Q Added OK : %u\n", |
| tx[portId].txFromSwQOK); |
| printf("Tx From S/W Q Delayed : %u\n", |
| tx[portId].txFromSwQDelayed); |
| printf("Tx Overflow : %u\n", |
| tx[portId].txOverflow); |
| printf("Tx Mutual Lock : %u\n", |
| tx[portId].txLock); |
| printf("Tx Late Ntf Enabled : %u\n", |
| tx[portId].txLateNotificationEnabled); |
| printf("Tx Low Thresh CB : %u\n", |
| tx[portId].txLowThreshCallback); |
| printf("Tx Done from H/W Q (Disable) : %u\n", |
| tx[portId].txDoneDuringDisable); |
| printf("Tx Done from S/W Q (Disable) : %u\n", |
| tx[portId].txDoneSwQDuringDisable); |
| for (priority = IX_ETH_ACC_TX_PRIORITY_0; |
| priority <= IX_ETH_ACC_TX_PRIORITY_7; |
| priority++) |
| { |
| if (tx[portId].txPriority[priority]) |
| { |
| printf("Tx Priority %u : %u\n", |
| priority, |
| tx[portId].txPriority[priority]); |
| } |
| } |
| #endif |
| printf("Tx unexpected errors : %u (should be 0)\n", |
| tx[portId].txUnexpectedError); |
| |
| #ifndef NDEBUG |
| printf("Rx Frames : %u\n", |
| rx[portId].rxFrameClientCallback + |
| rx[portId].rxSwQDuringDisable+ |
| rx[portId].rxDuringDisable); |
| printf("Rx Free Replenish : %u\n", |
| rx[portId].rxFreeRepOK + rx[portId].rxFreeRepDelayed); |
| printf("Rx Free H/W Q Added OK : %u\n", |
| rx[portId].rxFreeRepOK); |
| printf("Rx Free H/W Q Delayed : %u\n", |
| rx[portId].rxFreeRepDelayed); |
| printf("Rx Free From S/W Q Added OK : %u\n", |
| rx[portId].rxFreeRepFromSwQOK); |
| printf("Rx Free From S/W Q Delayed : %u\n", |
| rx[portId].rxFreeRepFromSwQDelayed); |
| printf("Rx Free Overflow : %u\n", |
| rx[portId].rxFreeOverflow); |
| printf("Rx Free Mutual Lock : %u\n", |
| rx[portId].rxFreeLock); |
| printf("Rx Free Late Ntf Enabled : %u\n", |
| rx[portId].rxFreeLateNotificationEnabled); |
| printf("Rx Free Low CB : %u\n", |
| rx[portId].rxFreeLowCallback); |
| printf("Rx From H/W Q (Disable) : %u\n", |
| rx[portId].rxDuringDisable); |
| printf("Rx From S/W Q (Disable) : %u\n", |
| rx[portId].rxSwQDuringDisable); |
| printf("Rx unlearned Mac Address : %u\n", |
| rx[portId].rxUnlearnedMacAddress); |
| printf("Rx Filtered (Rx => RxFree) : %u\n", |
| rx[portId].rxFiltered); |
| |
| for (priority = IX_ETH_ACC_TX_PRIORITY_0; |
| priority <= IX_ETH_ACC_TX_PRIORITY_7; |
| priority++) |
| { |
| if (rx[portId].rxPriority[priority]) |
| { |
| printf("Rx Priority %u : %u\n", |
| priority, |
| rx[portId].rxPriority[priority]); |
| } |
| } |
| #endif |
| printf("Rx unexpected errors : %u (should be 0)\n", |
| rx[portId].rxUnexpectedError); |
| |
| #ifndef NDEBUG |
| numBuffersInTx = tx[portId].txQOK + |
| tx[portId].txQDelayed - |
| tx[portId].txDoneClientCallback - |
| tx[portId].txDoneSwQDuringDisable - |
| tx[portId].txDoneDuringDisable; |
| |
| printf("# Tx Buffers currently for transmission : %u\n", |
| numBuffersInTx); |
| |
| numBuffersInRx = rx[portId].rxFreeRepOK + |
| rx[portId].rxFreeRepDelayed - |
| rx[portId].rxFrameClientCallback - |
| rx[portId].rxSwQDuringDisable - |
| rx[portId].rxDuringDisable; |
| |
| printf("# Rx Buffers currently for reception : %u\n", |
| numBuffersInRx); |
| |
| totalBuffers += numBuffersInRx + numBuffersInTx; |
| #endif |
| } |
| |
| printf("---------------------------------------\n"); |
| |
| #ifndef NDEBUG |
| printf("\n"); |
| printf("Mbufs :\n"); |
| printf("Tx Unchained mbufs : %u\n", |
| stats.unchainedTxMBufs); |
| printf("Tx Chained bufs : %u\n", |
| stats.chainedTxMBufs); |
| printf("TxDone Unchained mbufs : %u\n", |
| stats.unchainedTxDoneMBufs); |
| printf("TxDone Chained bufs : %u\n", |
| stats.chainedTxDoneMBufs); |
| printf("RxFree Unchained mbufs : %u\n", |
| stats.unchainedRxFreeMBufs); |
| printf("RxFree Chained bufs : %u\n", |
| stats.chainedRxFreeMBufs); |
| printf("Rx Unchained mbufs : %u\n", |
| stats.unchainedRxMBufs); |
| printf("Rx Chained bufs : %u\n", |
| stats.chainedRxMBufs); |
| |
| printf("\n"); |
| printf("Software queue usage :\n"); |
| printf("Buffers added to S/W Q : %u\n", |
| stats.addToSwQ); |
| printf("Buffers removed from S/W Q : %u\n", |
| stats.removeFromSwQ); |
| |
| printf("\n"); |
| printf("Hardware queues callbacks :\n"); |
| |
| for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++) |
| { |
| rxFreeCallbackCounter += rx[portId].rxFreeLowCallback; |
| txCallbackCounter += tx[portId].txLowThreshCallback; |
| } |
| printf("Tx Done QM Callback invoked : %u\n", |
| stats.txDoneCallbackCounter); |
| printf("Tx QM Callback invoked : %u\n", |
| txCallbackCounter); |
| printf("Rx QM Callback invoked : %u\n", |
| stats.rxCallbackCounter); |
| printf("Rx QM Callback burst read : %u\n", |
| stats.rxCallbackBurstRead); |
| printf("Rx Free QM Callback invoked : %u\n", |
| rxFreeCallbackCounter); |
| #endif |
| printf("Unexpected errors in CB : %u (should be 0)\n", |
| stats.unexpectedError); |
| printf("\n"); |
| |
| printf("Hardware queues levels :\n"); |
| printf("Transmit Port 1 Q : %u \n",numTx0Entries); |
| printf("Transmit Port 2 Q : %u \n",numTx1Entries); |
| #ifdef __ixp46X |
| printf("Transmit Port 3 Q : %u \n",numTx2Entries); |
| #endif |
| printf("Transmit Done Q : %u \n",numTxDoneEntries); |
| printf("Receive Q : %u \n",numRxEntries); |
| printf("Receive Free Port 1 Q : %u \n",numRxFree0Entries); |
| printf("Receive Free Port 2 Q : %u \n",numRxFree1Entries); |
| #ifdef __ixp46X |
| printf("Receive Free Port 3 Q : %u \n",numRxFree2Entries); |
| #endif |
| |
| #ifndef NDEBUG |
| printf("\n"); |
| printf("# Total Buffers accounted for : %u\n", |
| totalBuffers); |
| |
| numBuffersInSwQ = ixEthAccDataStats.addToSwQ - |
| ixEthAccDataStats.removeFromSwQ; |
| |
| printf(" Buffers in S/W Qs : %u\n", |
| numBuffersInSwQ); |
| printf(" Buffers in H/W Qs or NPEs : %u\n", |
| totalBuffers - numBuffersInSwQ); |
| #endif |
| |
| printf("Rx QoS Discipline : %s\n", |
| (ixEthAccDataInfo.schDiscipline == |
| FIFO_PRIORITY ) ? "Enabled" : "Disabled"); |
| |
| for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++) |
| { |
| printf("Tx QoS Discipline port %u : %s\n", |
| portId, |
| (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline == |
| FIFO_PRIORITY ) ? "Enabled" : "Disabled"); |
| } |
| printf("\n"); |
| } |
| |
| |
| |
| |
| |