752 lines
16 KiB
C
752 lines
16 KiB
C
|
/*
|
||
|
* HND generic packet pool operation primitives
|
||
|
*
|
||
|
* Copyright (C) 1999-2015, Broadcom Corporation
|
||
|
*
|
||
|
* Unless you and Broadcom execute a separate written software license
|
||
|
* agreement governing use of this software, this software is licensed to you
|
||
|
* under the terms of the GNU General Public License version 2 (the "GPL"),
|
||
|
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
|
||
|
* following added to such license:
|
||
|
*
|
||
|
* As a special exception, the copyright holders of this software give you
|
||
|
* permission to link this software with independent modules, and to copy and
|
||
|
* distribute the resulting executable under terms of your choice, provided that
|
||
|
* you also meet, for each linked independent module, the terms and conditions of
|
||
|
* the license of that module. An independent module is a module which is not
|
||
|
* derived from this software. The special exception does not apply to any
|
||
|
* modifications of the software.
|
||
|
*
|
||
|
* Notwithstanding the above, under no circumstances may you combine this
|
||
|
* software in any way with any other Broadcom software provided under a license
|
||
|
* other than the GPL, without Broadcom's express prior written consent.
|
||
|
*
|
||
|
* $Id: $
|
||
|
*/
|
||
|
|
||
|
#include <typedefs.h>
|
||
|
#include <osl.h>
|
||
|
#include <bcmutils.h>
|
||
|
#include <hnd_pktpool.h>
|
||
|
|
||
|
/* Registry size is one larger than max pools, as slot #0 is reserved */
|
||
|
#define PKTPOOLREG_RSVD_ID (0U)
|
||
|
#define PKTPOOLREG_RSVD_PTR (POOLPTR(0xdeaddead))
|
||
|
#define PKTPOOLREG_FREE_PTR (POOLPTR(NULL))
|
||
|
|
||
|
#define PKTPOOL_REGISTRY_SET(id, pp) (pktpool_registry_set((id), (pp)))
|
||
|
#define PKTPOOL_REGISTRY_CMP(id, pp) (pktpool_registry_cmp((id), (pp)))
|
||
|
|
||
|
/* Tag a registry entry as free for use */
|
||
|
#define PKTPOOL_REGISTRY_CLR(id) \
|
||
|
PKTPOOL_REGISTRY_SET((id), PKTPOOLREG_FREE_PTR)
|
||
|
#define PKTPOOL_REGISTRY_ISCLR(id) \
|
||
|
(PKTPOOL_REGISTRY_CMP((id), PKTPOOLREG_FREE_PTR))
|
||
|
|
||
|
/* Tag registry entry 0 as reserved */
|
||
|
#define PKTPOOL_REGISTRY_RSV() \
|
||
|
PKTPOOL_REGISTRY_SET(PKTPOOLREG_RSVD_ID, PKTPOOLREG_RSVD_PTR)
|
||
|
#define PKTPOOL_REGISTRY_ISRSVD() \
|
||
|
(PKTPOOL_REGISTRY_CMP(PKTPOOLREG_RSVD_ID, PKTPOOLREG_RSVD_PTR))
|
||
|
|
||
|
/* Walk all un-reserved entries in registry */
|
||
|
#define PKTPOOL_REGISTRY_FOREACH(id) \
|
||
|
for ((id) = 1U; (id) <= pktpools_max; (id)++)
|
||
|
|
||
|
uint32 pktpools_max = 0U; /* maximum number of pools that may be initialized */
|
||
|
pktpool_t *pktpools_registry[PKTPOOL_MAXIMUM_ID + 1]; /* Pktpool registry */
|
||
|
|
||
|
/* Register/Deregister a pktpool with registry during pktpool_init/deinit */
|
||
|
static int pktpool_register(pktpool_t * poolptr);
|
||
|
static int pktpool_deregister(pktpool_t * poolptr);
|
||
|
|
||
|
/** accessor functions required when ROMming this file, forced into RAM */
|
||
|
static void
|
||
|
BCMRAMFN(pktpool_registry_set)(int id, pktpool_t *pp)
|
||
|
{
|
||
|
pktpools_registry[id] = pp;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
BCMRAMFN(pktpool_registry_cmp)(int id, pktpool_t *pp)
|
||
|
{
|
||
|
return pktpools_registry[id] == pp;
|
||
|
}
|
||
|
|
||
|
int /* Construct a pool registry to serve a maximum of total_pools */
|
||
|
pktpool_attach(osl_t *osh, uint32 total_pools)
|
||
|
{
|
||
|
uint32 poolid;
|
||
|
|
||
|
if (pktpools_max != 0U) {
|
||
|
return BCME_ERROR;
|
||
|
}
|
||
|
|
||
|
ASSERT(total_pools <= PKTPOOL_MAXIMUM_ID);
|
||
|
|
||
|
/* Initialize registry: reserve slot#0 and tag others as free */
|
||
|
PKTPOOL_REGISTRY_RSV(); /* reserve slot#0 */
|
||
|
|
||
|
PKTPOOL_REGISTRY_FOREACH(poolid) { /* tag all unreserved entries as free */
|
||
|
PKTPOOL_REGISTRY_CLR(poolid);
|
||
|
}
|
||
|
|
||
|
pktpools_max = total_pools;
|
||
|
|
||
|
return (int)pktpools_max;
|
||
|
}
|
||
|
|
||
|
int /* Destruct the pool registry. Ascertain all pools were first de-inited */
|
||
|
pktpool_dettach(osl_t *osh)
|
||
|
{
|
||
|
uint32 poolid;
|
||
|
|
||
|
if (pktpools_max == 0U) {
|
||
|
return BCME_OK;
|
||
|
}
|
||
|
|
||
|
/* Ascertain that no pools are still registered */
|
||
|
ASSERT(PKTPOOL_REGISTRY_ISRSVD()); /* assert reserved slot */
|
||
|
|
||
|
PKTPOOL_REGISTRY_FOREACH(poolid) { /* ascertain all others are free */
|
||
|
ASSERT(PKTPOOL_REGISTRY_ISCLR(poolid));
|
||
|
}
|
||
|
|
||
|
pktpools_max = 0U; /* restore boot state */
|
||
|
|
||
|
return BCME_OK;
|
||
|
}
|
||
|
|
||
|
static int /* Register a pool in a free slot; return the registry slot index */
|
||
|
pktpool_register(pktpool_t * poolptr)
|
||
|
{
|
||
|
uint32 poolid;
|
||
|
|
||
|
if (pktpools_max == 0U) {
|
||
|
return PKTPOOL_INVALID_ID; /* registry has not yet been constructed */
|
||
|
}
|
||
|
|
||
|
ASSERT(pktpools_max != 0U);
|
||
|
|
||
|
/* find an empty slot in pktpools_registry */
|
||
|
PKTPOOL_REGISTRY_FOREACH(poolid) {
|
||
|
if (PKTPOOL_REGISTRY_ISCLR(poolid)) {
|
||
|
PKTPOOL_REGISTRY_SET(poolid, POOLPTR(poolptr)); /* register pool */
|
||
|
return (int)poolid; /* return pool ID */
|
||
|
}
|
||
|
} /* FOREACH */
|
||
|
|
||
|
return PKTPOOL_INVALID_ID; /* error: registry is full */
|
||
|
}
|
||
|
|
||
|
static int /* Deregister a pktpool, given the pool pointer; tag slot as free */
|
||
|
pktpool_deregister(pktpool_t * poolptr)
|
||
|
{
|
||
|
uint32 poolid;
|
||
|
|
||
|
ASSERT(POOLPTR(poolptr) != POOLPTR(NULL));
|
||
|
|
||
|
poolid = POOLID(poolptr);
|
||
|
ASSERT(poolid <= pktpools_max);
|
||
|
|
||
|
/* Asertain that a previously registered poolptr is being de-registered */
|
||
|
if (PKTPOOL_REGISTRY_CMP(poolid, POOLPTR(poolptr))) {
|
||
|
PKTPOOL_REGISTRY_CLR(poolid); /* mark as free */
|
||
|
} else {
|
||
|
ASSERT(0);
|
||
|
return BCME_ERROR; /* mismatch in registry */
|
||
|
}
|
||
|
|
||
|
return BCME_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* pktpool_init:
|
||
|
* User provides a pktpool_t sturcture and specifies the number of packets to
|
||
|
* be pre-filled into the pool (pplen). The size of all packets in a pool must
|
||
|
* be the same and is specified by plen.
|
||
|
* pktpool_init first attempts to register the pool and fetch a unique poolid.
|
||
|
* If registration fails, it is considered an BCME_ERR, caused by either the
|
||
|
* registry was not pre-created (pktpool_attach) or the registry is full.
|
||
|
* If registration succeeds, then the requested number of packets will be filled
|
||
|
* into the pool as part of initialization. In the event that there is no
|
||
|
* available memory to service the request, then BCME_NOMEM will be returned
|
||
|
* along with the count of how many packets were successfully allocated.
|
||
|
* In dongle builds, prior to memory reclaimation, one should limit the number
|
||
|
* of packets to be allocated during pktpool_init and fill the pool up after
|
||
|
* reclaim stage.
|
||
|
*/
|
||
|
int
|
||
|
pktpool_init(osl_t *osh, pktpool_t *pktp, int *pplen, int plen, bool istx, uint8 type)
|
||
|
{
|
||
|
int i, err = BCME_OK;
|
||
|
int pktplen;
|
||
|
uint8 pktp_id;
|
||
|
|
||
|
ASSERT(pktp != NULL);
|
||
|
ASSERT(osh != NULL);
|
||
|
ASSERT(pplen != NULL);
|
||
|
|
||
|
pktplen = *pplen;
|
||
|
|
||
|
bzero(pktp, sizeof(pktpool_t));
|
||
|
|
||
|
/* assign a unique pktpool id */
|
||
|
if ((pktp_id = (uint8) pktpool_register(pktp)) == PKTPOOL_INVALID_ID) {
|
||
|
return BCME_ERROR;
|
||
|
}
|
||
|
POOLSETID(pktp, pktp_id);
|
||
|
|
||
|
pktp->inited = TRUE;
|
||
|
pktp->istx = istx ? TRUE : FALSE;
|
||
|
pktp->plen = (uint16)plen;
|
||
|
pktp->type = type;
|
||
|
|
||
|
pktp->maxlen = PKTPOOL_LEN_MAX;
|
||
|
pktplen = LIMIT_TO_MAX(pktplen, pktp->maxlen);
|
||
|
|
||
|
for (i = 0; i < pktplen; i++) {
|
||
|
void *p;
|
||
|
p = PKTGET(osh, plen, TRUE);
|
||
|
|
||
|
if (p == NULL) {
|
||
|
/* Not able to allocate all requested pkts
|
||
|
* so just return what was actually allocated
|
||
|
* We can add to the pool later
|
||
|
*/
|
||
|
if (pktp->freelist == NULL) /* pktpool free list is empty */
|
||
|
err = BCME_NOMEM;
|
||
|
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
PKTSETPOOL(osh, p, TRUE, pktp); /* Tag packet with pool ID */
|
||
|
|
||
|
PKTSETFREELIST(p, pktp->freelist); /* insert p at head of free list */
|
||
|
pktp->freelist = p;
|
||
|
|
||
|
pktp->avail++;
|
||
|
|
||
|
#ifdef BCMDBG_POOL
|
||
|
pktp->dbg_q[pktp->dbg_qlen++].p = p;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
pktp->len = pktp->avail;
|
||
|
|
||
|
*pplen = pktp->len;
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* pktpool_deinit:
|
||
|
* Prior to freeing a pktpool, all packets must be first freed into the pktpool.
|
||
|
* Upon pktpool_deinit, all packets in the free pool will be freed to the heap.
|
||
|
* An assert is in place to ensure that there are no packets still lingering
|
||
|
* around. Packets freed to a pool after the deinit will cause a memory
|
||
|
* corruption as the pktpool_t structure no longer exists.
|
||
|
*/
|
||
|
int
|
||
|
pktpool_deinit(osl_t *osh, pktpool_t *pktp)
|
||
|
{
|
||
|
uint16 freed = 0;
|
||
|
|
||
|
ASSERT(osh != NULL);
|
||
|
ASSERT(pktp != NULL);
|
||
|
|
||
|
#ifdef BCMDBG_POOL
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i <= pktp->len; i++) {
|
||
|
pktp->dbg_q[i].p = NULL;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
while (pktp->freelist != NULL) {
|
||
|
void * p = pktp->freelist;
|
||
|
|
||
|
pktp->freelist = PKTFREELIST(p); /* unlink head packet from free list */
|
||
|
PKTSETFREELIST(p, NULL);
|
||
|
|
||
|
PKTSETPOOL(osh, p, FALSE, NULL); /* clear pool ID tag in pkt */
|
||
|
|
||
|
PKTFREE(osh, p, pktp->istx); /* free the packet */
|
||
|
|
||
|
freed++;
|
||
|
ASSERT(freed <= pktp->len);
|
||
|
}
|
||
|
|
||
|
pktp->avail -= freed;
|
||
|
ASSERT(pktp->avail == 0);
|
||
|
|
||
|
pktp->len -= freed;
|
||
|
|
||
|
pktpool_deregister(pktp); /* release previously acquired unique pool id */
|
||
|
POOLSETID(pktp, PKTPOOL_INVALID_ID);
|
||
|
|
||
|
pktp->inited = FALSE;
|
||
|
|
||
|
/* Are there still pending pkts? */
|
||
|
ASSERT(pktp->len == 0);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
pktpool_fill(osl_t *osh, pktpool_t *pktp, bool minimal)
|
||
|
{
|
||
|
void *p;
|
||
|
int err = 0;
|
||
|
int len, psize, maxlen;
|
||
|
|
||
|
ASSERT(pktp->plen != 0);
|
||
|
|
||
|
maxlen = pktp->maxlen;
|
||
|
psize = minimal ? (maxlen >> 2) : maxlen;
|
||
|
for (len = (int)pktp->len; len < psize; len++) {
|
||
|
|
||
|
p = PKTGET(osh, pktp->len, TRUE);
|
||
|
|
||
|
if (p == NULL) {
|
||
|
err = BCME_NOMEM;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pktpool_add(pktp, p) != BCME_OK) {
|
||
|
PKTFREE(osh, p, FALSE);
|
||
|
err = BCME_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static void *
|
||
|
pktpool_deq(pktpool_t *pktp)
|
||
|
{
|
||
|
void *p;
|
||
|
|
||
|
if (pktp->avail == 0)
|
||
|
return NULL;
|
||
|
|
||
|
ASSERT(pktp->freelist != NULL);
|
||
|
|
||
|
p = pktp->freelist; /* dequeue packet from head of pktpool free list */
|
||
|
pktp->freelist = PKTFREELIST(p); /* free list points to next packet */
|
||
|
PKTSETFREELIST(p, NULL);
|
||
|
|
||
|
pktp->avail--;
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pktpool_enq(pktpool_t *pktp, void *p)
|
||
|
{
|
||
|
ASSERT(p != NULL);
|
||
|
|
||
|
PKTSETFREELIST(p, pktp->freelist); /* insert at head of pktpool free list */
|
||
|
pktp->freelist = p; /* free list points to newly inserted packet */
|
||
|
|
||
|
pktp->avail++;
|
||
|
ASSERT(pktp->avail <= pktp->len);
|
||
|
}
|
||
|
|
||
|
/* utility for registering host addr fill function called from pciedev */
|
||
|
int
|
||
|
/* BCMATTACHFN */
|
||
|
(pktpool_hostaddr_fill_register)(pktpool_t *pktp, pktpool_cb_extn_t cb, void *arg)
|
||
|
{
|
||
|
|
||
|
ASSERT(cb != NULL);
|
||
|
|
||
|
ASSERT(pktp->cbext.cb == NULL);
|
||
|
pktp->cbext.cb = cb;
|
||
|
pktp->cbext.arg = arg;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
pktpool_rxcplid_fill_register(pktpool_t *pktp, pktpool_cb_extn_t cb, void *arg)
|
||
|
{
|
||
|
|
||
|
ASSERT(cb != NULL);
|
||
|
|
||
|
ASSERT(pktp->rxcplidfn.cb == NULL);
|
||
|
pktp->rxcplidfn.cb = cb;
|
||
|
pktp->rxcplidfn.arg = arg;
|
||
|
return 0;
|
||
|
}
|
||
|
/* Callback functions for split rx modes */
|
||
|
/* when evr host posts rxbuffer, invike dma_rxfill from pciedev layer */
|
||
|
void
|
||
|
pktpool_invoke_dmarxfill(pktpool_t *pktp)
|
||
|
{
|
||
|
ASSERT(pktp->dmarxfill.cb);
|
||
|
ASSERT(pktp->dmarxfill.arg);
|
||
|
|
||
|
if (pktp->dmarxfill.cb)
|
||
|
pktp->dmarxfill.cb(pktp, pktp->dmarxfill.arg);
|
||
|
}
|
||
|
int
|
||
|
pkpool_haddr_avail_register_cb(pktpool_t *pktp, pktpool_cb_t cb, void *arg)
|
||
|
{
|
||
|
|
||
|
ASSERT(cb != NULL);
|
||
|
|
||
|
pktp->dmarxfill.cb = cb;
|
||
|
pktp->dmarxfill.arg = arg;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
/* No BCMATTACHFN as it is used in xdc_enable_ep which is not an attach function */
|
||
|
int
|
||
|
pktpool_avail_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
ASSERT(cb != NULL);
|
||
|
|
||
|
i = pktp->cbcnt;
|
||
|
if (i == PKTPOOL_CB_MAX)
|
||
|
return BCME_ERROR;
|
||
|
|
||
|
ASSERT(pktp->cbs[i].cb == NULL);
|
||
|
pktp->cbs[i].cb = cb;
|
||
|
pktp->cbs[i].arg = arg;
|
||
|
pktp->cbcnt++;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
pktpool_empty_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
ASSERT(cb != NULL);
|
||
|
|
||
|
i = pktp->ecbcnt;
|
||
|
if (i == PKTPOOL_CB_MAX)
|
||
|
return BCME_ERROR;
|
||
|
|
||
|
ASSERT(pktp->ecbs[i].cb == NULL);
|
||
|
pktp->ecbs[i].cb = cb;
|
||
|
pktp->ecbs[i].arg = arg;
|
||
|
pktp->ecbcnt++;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
pktpool_empty_notify(pktpool_t *pktp)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
pktp->empty = TRUE;
|
||
|
for (i = 0; i < pktp->ecbcnt; i++) {
|
||
|
ASSERT(pktp->ecbs[i].cb != NULL);
|
||
|
pktp->ecbs[i].cb(pktp, pktp->ecbs[i].arg);
|
||
|
}
|
||
|
pktp->empty = FALSE;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef BCMDBG_POOL
|
||
|
int
|
||
|
pktpool_dbg_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
ASSERT(cb);
|
||
|
|
||
|
i = pktp->dbg_cbcnt;
|
||
|
if (i == PKTPOOL_CB_MAX)
|
||
|
return BCME_ERROR;
|
||
|
|
||
|
ASSERT(pktp->dbg_cbs[i].cb == NULL);
|
||
|
pktp->dbg_cbs[i].cb = cb;
|
||
|
pktp->dbg_cbs[i].arg = arg;
|
||
|
pktp->dbg_cbcnt++;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int pktpool_dbg_notify(pktpool_t *pktp);
|
||
|
|
||
|
int
|
||
|
pktpool_dbg_notify(pktpool_t *pktp)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < pktp->dbg_cbcnt; i++) {
|
||
|
ASSERT(pktp->dbg_cbs[i].cb);
|
||
|
pktp->dbg_cbs[i].cb(pktp, pktp->dbg_cbs[i].arg);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
pktpool_dbg_dump(pktpool_t *pktp)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
printf("pool len=%d maxlen=%d\n", pktp->dbg_qlen, pktp->maxlen);
|
||
|
for (i = 0; i < pktp->dbg_qlen; i++) {
|
||
|
ASSERT(pktp->dbg_q[i].p);
|
||
|
printf("%d, p: 0x%x dur:%lu us state:%d\n", i,
|
||
|
pktp->dbg_q[i].p, pktp->dbg_q[i].dur/100, PKTPOOLSTATE(pktp->dbg_q[i].p));
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
pktpool_stats_dump(pktpool_t *pktp, pktpool_stats_t *stats)
|
||
|
{
|
||
|
int i;
|
||
|
int state;
|
||
|
|
||
|
bzero(stats, sizeof(pktpool_stats_t));
|
||
|
for (i = 0; i < pktp->dbg_qlen; i++) {
|
||
|
ASSERT(pktp->dbg_q[i].p != NULL);
|
||
|
|
||
|
state = PKTPOOLSTATE(pktp->dbg_q[i].p);
|
||
|
switch (state) {
|
||
|
case POOL_TXENQ:
|
||
|
stats->enq++; break;
|
||
|
case POOL_TXDH:
|
||
|
stats->txdh++; break;
|
||
|
case POOL_TXD11:
|
||
|
stats->txd11++; break;
|
||
|
case POOL_RXDH:
|
||
|
stats->rxdh++; break;
|
||
|
case POOL_RXD11:
|
||
|
stats->rxd11++; break;
|
||
|
case POOL_RXFILL:
|
||
|
stats->rxfill++; break;
|
||
|
case POOL_IDLE:
|
||
|
stats->idle++; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
pktpool_start_trigger(pktpool_t *pktp, void *p)
|
||
|
{
|
||
|
uint32 cycles, i;
|
||
|
|
||
|
if (!PKTPOOL(OSH_NULL, p))
|
||
|
return 0;
|
||
|
|
||
|
OSL_GETCYCLES(cycles);
|
||
|
|
||
|
for (i = 0; i < pktp->dbg_qlen; i++) {
|
||
|
ASSERT(pktp->dbg_q[i].p != NULL);
|
||
|
|
||
|
if (pktp->dbg_q[i].p == p) {
|
||
|
pktp->dbg_q[i].cycles = cycles;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int pktpool_stop_trigger(pktpool_t *pktp, void *p);
|
||
|
int
|
||
|
pktpool_stop_trigger(pktpool_t *pktp, void *p)
|
||
|
{
|
||
|
uint32 cycles, i;
|
||
|
|
||
|
if (!PKTPOOL(OSH_NULL, p))
|
||
|
return 0;
|
||
|
|
||
|
OSL_GETCYCLES(cycles);
|
||
|
|
||
|
for (i = 0; i < pktp->dbg_qlen; i++) {
|
||
|
ASSERT(pktp->dbg_q[i].p != NULL);
|
||
|
|
||
|
if (pktp->dbg_q[i].p == p) {
|
||
|
if (pktp->dbg_q[i].cycles == 0)
|
||
|
break;
|
||
|
|
||
|
if (cycles >= pktp->dbg_q[i].cycles)
|
||
|
pktp->dbg_q[i].dur = cycles - pktp->dbg_q[i].cycles;
|
||
|
else
|
||
|
pktp->dbg_q[i].dur =
|
||
|
(((uint32)-1) - pktp->dbg_q[i].cycles) + cycles + 1;
|
||
|
|
||
|
pktp->dbg_q[i].cycles = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* BCMDBG_POOL */
|
||
|
|
||
|
int
|
||
|
pktpool_avail_notify_normal(osl_t *osh, pktpool_t *pktp)
|
||
|
{
|
||
|
ASSERT(pktp);
|
||
|
pktp->availcb_excl = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
pktpool_avail_notify_exclusive(osl_t *osh, pktpool_t *pktp, pktpool_cb_t cb)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
ASSERT(pktp);
|
||
|
ASSERT(pktp->availcb_excl == NULL);
|
||
|
for (i = 0; i < pktp->cbcnt; i++) {
|
||
|
if (cb == pktp->cbs[i].cb) {
|
||
|
pktp->availcb_excl = &pktp->cbs[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pktp->availcb_excl == NULL)
|
||
|
return BCME_ERROR;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
pktpool_avail_notify(pktpool_t *pktp)
|
||
|
{
|
||
|
int i, k, idx;
|
||
|
int avail;
|
||
|
|
||
|
ASSERT(pktp);
|
||
|
if (pktp->availcb_excl != NULL) {
|
||
|
pktp->availcb_excl->cb(pktp, pktp->availcb_excl->arg);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
k = pktp->cbcnt - 1;
|
||
|
for (i = 0; i < pktp->cbcnt; i++) {
|
||
|
avail = pktp->avail;
|
||
|
|
||
|
if (avail) {
|
||
|
if (pktp->cbtoggle)
|
||
|
idx = i;
|
||
|
else
|
||
|
idx = k--;
|
||
|
|
||
|
ASSERT(pktp->cbs[idx].cb != NULL);
|
||
|
pktp->cbs[idx].cb(pktp, pktp->cbs[idx].arg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Alternate between filling from head or tail
|
||
|
*/
|
||
|
pktp->cbtoggle ^= 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void *
|
||
|
pktpool_get(pktpool_t *pktp)
|
||
|
{
|
||
|
void *p;
|
||
|
|
||
|
p = pktpool_deq(pktp);
|
||
|
|
||
|
if (p == NULL) {
|
||
|
/* Notify and try to reclaim tx pkts */
|
||
|
if (pktp->ecbcnt)
|
||
|
pktpool_empty_notify(pktp);
|
||
|
|
||
|
p = pktpool_deq(pktp);
|
||
|
if (p == NULL)
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
pktpool_free(pktpool_t *pktp, void *p)
|
||
|
{
|
||
|
ASSERT(p != NULL);
|
||
|
#ifdef BCMDBG_POOL
|
||
|
/* pktpool_stop_trigger(pktp, p); */
|
||
|
#endif
|
||
|
|
||
|
pktpool_enq(pktp, p);
|
||
|
|
||
|
if (pktp->emptycb_disable)
|
||
|
return;
|
||
|
|
||
|
if (pktp->cbcnt) {
|
||
|
if (pktp->empty == FALSE)
|
||
|
pktpool_avail_notify(pktp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
pktpool_add(pktpool_t *pktp, void *p)
|
||
|
{
|
||
|
ASSERT(p != NULL);
|
||
|
|
||
|
if (pktp->len == pktp->maxlen)
|
||
|
return BCME_RANGE;
|
||
|
|
||
|
/* pkts in pool have same length */
|
||
|
ASSERT(pktp->plen == PKTLEN(OSH_NULL, p));
|
||
|
PKTSETPOOL(OSH_NULL, p, TRUE, pktp);
|
||
|
|
||
|
pktp->len++;
|
||
|
pktpool_enq(pktp, p);
|
||
|
|
||
|
#ifdef BCMDBG_POOL
|
||
|
pktp->dbg_q[pktp->dbg_qlen++].p = p;
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Force pktpool_setmaxlen () into RAM as it uses a constant
|
||
|
* (PKTPOOL_LEN_MAX) that may be changed post tapeout for ROM-based chips.
|
||
|
*/
|
||
|
int
|
||
|
BCMRAMFN(pktpool_setmaxlen)(pktpool_t *pktp, uint16 maxlen)
|
||
|
{
|
||
|
if (maxlen > PKTPOOL_LEN_MAX)
|
||
|
maxlen = PKTPOOL_LEN_MAX;
|
||
|
|
||
|
/* if pool is already beyond maxlen, then just cap it
|
||
|
* since we currently do not reduce the pool len
|
||
|
* already allocated
|
||
|
*/
|
||
|
pktp->maxlen = (pktp->len > maxlen) ? pktp->len : maxlen;
|
||
|
|
||
|
return pktp->maxlen;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
pktpool_emptycb_disable(pktpool_t *pktp, bool disable)
|
||
|
{
|
||
|
ASSERT(pktp);
|
||
|
|
||
|
pktp->emptycb_disable = disable;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
pktpool_emptycb_disabled(pktpool_t *pktp)
|
||
|
{
|
||
|
ASSERT(pktp);
|
||
|
return pktp->emptycb_disable;
|
||
|
}
|