/* * HND generic pktq operation primitives * * Copyright (C) 1999-2017, 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: hnd_pktq.c 657686 2016-09-02 06:39:10Z $ */ #include #include #include #include #include /* mutex macros for thread safe */ #ifdef HND_PKTQ_THREAD_SAFE #define HND_PKTQ_MUTEX_CREATE(name, mutex) osl_ext_mutex_create(name, mutex) #define HND_PKTQ_MUTEX_DELETE(mutex) osl_ext_mutex_delete(mutex) #define HND_PKTQ_MUTEX_ACQUIRE(mutex, msec) osl_ext_mutex_acquire(mutex, msec) #define HND_PKTQ_MUTEX_RELEASE(mutex) osl_ext_mutex_release(mutex) #else #define HND_PKTQ_MUTEX_CREATE(name, mutex) OSL_EXT_SUCCESS #define HND_PKTQ_MUTEX_DELETE(mutex) OSL_EXT_SUCCESS #define HND_PKTQ_MUTEX_ACQUIRE(mutex, msec) OSL_EXT_SUCCESS #define HND_PKTQ_MUTEX_RELEASE(mutex) OSL_EXT_SUCCESS #endif /* * osl multiple-precedence packet queue * hi_prec is always >= the number of the highest non-empty precedence */ void * BCMFASTPATH pktq_penq(struct pktq *pq, int prec, void *p) { struct pktq_prec *q; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; ASSERT(prec >= 0 && prec < pq->num_prec); ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */ ASSERT(!pktq_full(pq)); ASSERT(!pktq_pfull(pq, prec)); q = &pq->q[prec]; if (q->head) PKTSETLINK(q->tail, p); else q->head = p; q->tail = p; q->len++; pq->len++; if (pq->hi_prec < prec) pq->hi_prec = (uint8)prec; /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void * BCMFASTPATH pktq_penq_head(struct pktq *pq, int prec, void *p) { struct pktq_prec *q; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; ASSERT(prec >= 0 && prec < pq->num_prec); ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */ ASSERT(!pktq_full(pq)); ASSERT(!pktq_pfull(pq, prec)); q = &pq->q[prec]; if (q->head == NULL) q->tail = p; PKTSETLINK(p, q->head); q->head = p; q->len++; pq->len++; if (pq->hi_prec < prec) pq->hi_prec = (uint8)prec; /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } /* * Append spktq 'list' to the tail of pktq 'pq' */ void BCMFASTPATH pktq_append(struct pktq *pq, int prec, struct spktq *list) { struct pktq_prec *q; struct pktq_prec *list_q; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return; list_q = &list->q[0]; /* empty list check */ if (list_q->head == NULL) goto done; ASSERT(prec >= 0 && prec < pq->num_prec); ASSERT(PKTLINK(list_q->tail) == NULL); /* terminated list */ ASSERT(!pktq_full(pq)); ASSERT(!pktq_pfull(pq, prec)); q = &pq->q[prec]; if (q->head) PKTSETLINK(q->tail, list_q->head); else q->head = list_q->head; q->tail = list_q->tail; q->len += list_q->len; pq->len += list_q->len; if (pq->hi_prec < prec) pq->hi_prec = (uint8)prec; list_q->head = NULL; list_q->tail = NULL; list_q->len = 0; list->len = 0; done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return; } /* * Prepend spktq 'list' to the head of pktq 'pq' */ void BCMFASTPATH pktq_prepend(struct pktq *pq, int prec, struct spktq *list) { struct pktq_prec *q; struct pktq_prec *list_q; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return; list_q = &list->q[0]; /* empty list check */ if (list_q->head == NULL) goto done; ASSERT(prec >= 0 && prec < pq->num_prec); ASSERT(PKTLINK(list_q->tail) == NULL); /* terminated list */ ASSERT(!pktq_full(pq)); ASSERT(!pktq_pfull(pq, prec)); q = &pq->q[prec]; /* set the tail packet of list to point at the former pq head */ PKTSETLINK(list_q->tail, q->head); /* the new q head is the head of list */ q->head = list_q->head; /* If the q tail was non-null, then it stays as is. * If the q tail was null, it is now the tail of list */ if (q->tail == NULL) { q->tail = list_q->tail; } q->len += list_q->len; pq->len += list_q->len; if (pq->hi_prec < prec) pq->hi_prec = (uint8)prec; list_q->head = NULL; list_q->tail = NULL; list_q->len = 0; list->len = 0; done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return; } void * BCMFASTPATH pktq_pdeq(struct pktq *pq, int prec) { struct pktq_prec *q; void *p; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; ASSERT(prec >= 0 && prec < pq->num_prec); q = &pq->q[prec]; if ((p = q->head) == NULL) goto done; if ((q->head = PKTLINK(p)) == NULL) q->tail = NULL; q->len--; pq->len--; PKTSETLINK(p, NULL); done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void * BCMFASTPATH pktq_pdeq_prev(struct pktq *pq, int prec, void *prev_p) { struct pktq_prec *q; void *p = NULL; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; ASSERT(prec >= 0 && prec < pq->num_prec); q = &pq->q[prec]; if (prev_p == NULL) goto done; if ((p = PKTLINK(prev_p)) == NULL) goto done; q->len--; pq->len--; PKTSETLINK(prev_p, PKTLINK(p)); PKTSETLINK(p, NULL); done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void * BCMFASTPATH pktq_pdeq_with_fn(struct pktq *pq, int prec, ifpkt_cb_t fn, int arg) { struct pktq_prec *q; void *p, *prev = NULL; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; ASSERT(prec >= 0 && prec < pq->num_prec); q = &pq->q[prec]; p = q->head; while (p) { if (fn == NULL || (*fn)(p, arg)) { break; } else { prev = p; p = PKTLINK(p); } } if (p == NULL) goto done; if (prev == NULL) { if ((q->head = PKTLINK(p)) == NULL) { q->tail = NULL; } } else { PKTSETLINK(prev, PKTLINK(p)); if (q->tail == p) { q->tail = prev; } } q->len--; pq->len--; PKTSETLINK(p, NULL); done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void * BCMFASTPATH pktq_pdeq_tail(struct pktq *pq, int prec) { struct pktq_prec *q; void *p, *prev; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; ASSERT(prec >= 0 && prec < pq->num_prec); q = &pq->q[prec]; if ((p = q->head) == NULL) goto done; for (prev = NULL; p != q->tail; p = PKTLINK(p)) prev = p; if (prev) PKTSETLINK(prev, NULL); else q->head = NULL; q->tail = prev; q->len--; pq->len--; done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ifpkt_cb_t fn, int arg) { struct pktq_prec *q; void *p, *next, *prev = NULL; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return; q = &pq->q[prec]; p = q->head; while (p) { next = PKTLINK(p); if (fn == NULL || (*fn)(p, arg)) { bool head = (p == q->head); if (head) q->head = next; else PKTSETLINK(prev, next); PKTSETLINK(p, NULL); PKTFREE(osh, p, dir); q->len--; pq->len--; } else { prev = p; } p = next; } q->tail = prev; if (q->head == NULL) { ASSERT(q->len == 0); ASSERT(q->tail == NULL); } /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return; } bool BCMFASTPATH pktq_pdel(struct pktq *pq, void *pktbuf, int prec) { bool ret = FALSE; struct pktq_prec *q; void *p = NULL; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return FALSE; ASSERT(prec >= 0 && prec < pq->num_prec); /* Should this just assert pktbuf? */ if (!pktbuf) goto done; q = &pq->q[prec]; if (q->head == pktbuf) { if ((q->head = PKTLINK(pktbuf)) == NULL) q->tail = NULL; } else { for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p)) ; if (p == NULL) goto done; PKTSETLINK(p, PKTLINK(pktbuf)); if (q->tail == pktbuf) q->tail = p; } q->len--; pq->len--; PKTSETLINK(pktbuf, NULL); ret = TRUE; done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return FALSE; return ret; } bool pktq_init(struct pktq *pq, int num_prec, int max_len) { int prec; if (HND_PKTQ_MUTEX_CREATE("pktq", &pq->mutex) != OSL_EXT_SUCCESS) return FALSE; ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC); /* pq is variable size; only zero out what's requested */ bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); pq->num_prec = (uint16)num_prec; pq->max = (uint16)max_len; for (prec = 0; prec < num_prec; prec++) pq->q[prec].max = pq->max; return TRUE; } bool pktq_deinit(struct pktq *pq) { if (HND_PKTQ_MUTEX_DELETE(&pq->mutex) != OSL_EXT_SUCCESS) return FALSE; return TRUE; } void pktq_set_max_plen(struct pktq *pq, int prec, int max_len) { ASSERT(prec >= 0 && prec < pq->num_prec); /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return; if (prec < pq->num_prec) pq->q[prec].max = (uint16)max_len; /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return; } void * BCMFASTPATH pktq_deq(struct pktq *pq, int *prec_out) { struct pktq_prec *q; void *p = NULL; int prec; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; if (pq->len == 0) goto done; while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) pq->hi_prec--; q = &pq->q[prec]; if ((p = q->head) == NULL) goto done; if ((q->head = PKTLINK(p)) == NULL) q->tail = NULL; q->len--; pq->len--; if (prec_out) *prec_out = prec; PKTSETLINK(p, NULL); done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void * BCMFASTPATH pktq_deq_tail(struct pktq *pq, int *prec_out) { struct pktq_prec *q; void *p = NULL, *prev; int prec; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; if (pq->len == 0) goto done; for (prec = 0; prec < pq->hi_prec; prec++) if (pq->q[prec].head) break; q = &pq->q[prec]; if ((p = q->head) == NULL) goto done; for (prev = NULL; p != q->tail; p = PKTLINK(p)) prev = p; if (prev) PKTSETLINK(prev, NULL); else q->head = NULL; q->tail = prev; q->len--; pq->len--; if (prec_out) *prec_out = prec; PKTSETLINK(p, NULL); done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void * pktq_peek(struct pktq *pq, int *prec_out) { int prec; void *p = NULL; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; if (pq->len == 0) goto done; while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) pq->hi_prec--; if (prec_out) *prec_out = prec; p = pq->q[prec].head; done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void * pktq_peek_tail(struct pktq *pq, int *prec_out) { int prec; void *p = NULL; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; if (pq->len == 0) goto done; for (prec = 0; prec < pq->hi_prec; prec++) if (pq->q[prec].head) break; if (prec_out) *prec_out = prec; p = pq->q[prec].tail; done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } void pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg) { int prec; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return; /* Optimize flush, if pktq len = 0, just return. * pktq len of 0 means pktq's prec q's are all empty. */ if (pq->len == 0) goto done; for (prec = 0; prec < pq->num_prec; prec++) pktq_pflush(osh, pq, prec, dir, fn, arg); if (fn == NULL) ASSERT(pq->len == 0); done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return; } /* Return sum of lengths of a specific set of precedences */ int pktq_mlen(struct pktq *pq, uint prec_bmp) { int prec, len; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return 0; len = 0; for (prec = 0; prec <= pq->hi_prec; prec++) if (prec_bmp & (1 << prec)) len += pq->q[prec].len; /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return 0; return len; } /* Priority peek from a specific set of precedences */ void * BCMFASTPATH pktq_mpeek(struct pktq *pq, uint prec_bmp, int *prec_out) { struct pktq_prec *q; void *p = NULL; int prec; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; if (pq->len == 0) goto done; while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) pq->hi_prec--; while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL) if (prec-- == 0) goto done; q = &pq->q[prec]; if ((p = q->head) == NULL) goto done; if (prec_out) *prec_out = prec; done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } /* Priority dequeue from a specific set of precedences */ void * BCMFASTPATH pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out) { struct pktq_prec *q; void *p = NULL; int prec; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return NULL; if (pq->len == 0) goto done; while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) pq->hi_prec--; while ((pq->q[prec].head == NULL) || ((prec_bmp & (1 << prec)) == 0)) if (prec-- == 0) goto done; q = &pq->q[prec]; if ((p = q->head) == NULL) goto done; if ((q->head = PKTLINK(p)) == NULL) q->tail = NULL; q->len--; if (prec_out) *prec_out = prec; pq->len--; PKTSETLINK(p, NULL); done: /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return NULL; return p; } #ifdef HND_PKTQ_THREAD_SAFE int pktq_pavail(struct pktq *pq, int prec) { int ret; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return 0; ASSERT(prec >= 0 && prec < pq->num_prec); ret = pq->q[prec].max - pq->q[prec].len; /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return 0; return ret; } bool pktq_pfull(struct pktq *pq, int prec) { bool ret; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return FALSE; ASSERT(prec >= 0 && prec < pq->num_prec); ret = pq->q[prec].len >= pq->q[prec].max; /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return FALSE; return ret; } int pktq_avail(struct pktq *pq) { int ret; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return 0; ret = pq->max - pq->len; /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return 0; return ret; } bool pktq_full(struct pktq *pq) { bool ret; /* protect shared resource */ if (HND_PKTQ_MUTEX_ACQUIRE(&pq->mutex, OSL_EXT_TIME_FOREVER) != OSL_EXT_SUCCESS) return FALSE; ret = pq->len >= pq->max; /* protect shared resource */ if (HND_PKTQ_MUTEX_RELEASE(&pq->mutex) != OSL_EXT_SUCCESS) return FALSE; return ret; } #endif /* HND_PKTQ_THREAD_SAFE */