u-boot/drivers/net/npe/IxEthAccDataPlane.c
Marek Vasut 61e129885a dm: net: Move IXP NPE to drivers/net/
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Bryan Hundven <bryanhundven@gmail.com>
Cc: Michael Schwingen <rincewind@discworld.dascon.de>
Cc: Wolfgang Denk <wd@denx.de>
Cc: Albert Aribaud <albert.u.boot@aribaud.net>
Cc: U-Boot DM <u-boot-dm@lists.denx.de>
Cc: Joe Hershberger <joe.hershberger@ni.com>
2012-09-24 13:17:24 -05:00

2483 lines
68 KiB
C

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