mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-28 07:31:15 +00:00
61e129885a
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>
2483 lines
68 KiB
C
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");
|
|
}
|
|
|
|
|
|
|
|
|
|
|