diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/net/wireless/bcmdhd/hnd_pktpool.c | |
Diffstat (limited to 'drivers/net/wireless/bcmdhd/hnd_pktpool.c')
| -rw-r--r-- | drivers/net/wireless/bcmdhd/hnd_pktpool.c | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/hnd_pktpool.c b/drivers/net/wireless/bcmdhd/hnd_pktpool.c new file mode 100644 index 00000000..242f4322 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/hnd_pktpool.c @@ -0,0 +1,733 @@ +/* + * HND generic packet pool operation primitives + * + * $Copyright Open Broadcom Corporation$ + * + * $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; +} |
