/** * @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"); }