summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/bcmdhd/hnd_pktpool.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/net/wireless/bcmdhd/hnd_pktpool.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/net/wireless/bcmdhd/hnd_pktpool.c')
-rw-r--r--drivers/net/wireless/bcmdhd/hnd_pktpool.c733
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;
+}