diff options
| author | Luigi Rizzo <luigi@FreeBSD.org> | 2012-02-10 21:03:04 +0000 |
|---|---|---|
| committer | Luigi Rizzo <luigi@FreeBSD.org> | 2012-02-10 21:03:04 +0000 |
| commit | 2f345d8ed55d5dd29023c1f0e6b6a396e1bf2770 (patch) | |
| tree | 3063d4c50b218a2983c6311cbe50ce7d0c9b8c52 /sys/dev/oce/oce_queue.c | |
| parent | 9b4f2514846a6232f9b04644c1b0e1d0f0d14392 (diff) | |
Notes
Diffstat (limited to 'sys/dev/oce/oce_queue.c')
| -rw-r--r-- | sys/dev/oce/oce_queue.c | 1213 |
1 files changed, 1213 insertions, 0 deletions
diff --git a/sys/dev/oce/oce_queue.c b/sys/dev/oce/oce_queue.c new file mode 100644 index 000000000000..c59dc5067c44 --- /dev/null +++ b/sys/dev/oce/oce_queue.c @@ -0,0 +1,1213 @@ +/*- + * Copyright (C) 2012 Emulex + * All rights reserved. + * + * 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 Emulex Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 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. + * + * Contact Information: + * freebsd-drivers@emulex.com + * + * Emulex + * 3333 Susan Street + * Costa Mesa, CA 92626 + */ + + + +/* $FreeBSD$ */ + + +#include "oce_if.h" + +/***************************************************** + * local queue functions + *****************************************************/ + +static struct oce_wq *oce_wq_init(POCE_SOFTC sc, + uint32_t q_len, uint32_t wq_type); +static int oce_wq_create(struct oce_wq *wq, struct oce_eq *eq); +static void oce_wq_free(struct oce_wq *wq); +static void oce_wq_del(struct oce_wq *wq); +static struct oce_rq *oce_rq_init(POCE_SOFTC sc, + uint32_t q_len, + uint32_t frag_size, + uint32_t mtu, uint32_t rss); +static int oce_rq_create(struct oce_rq *rq, uint32_t if_id, struct oce_eq *eq); +static void oce_rq_free(struct oce_rq *rq); +static void oce_rq_del(struct oce_rq *rq); +static struct oce_eq *oce_eq_create(POCE_SOFTC sc, + uint32_t q_len, + uint32_t item_size, + uint32_t eq_delay, + uint32_t vector); +static void oce_eq_del(struct oce_eq *eq); +static struct oce_mq *oce_mq_create(POCE_SOFTC sc, + struct oce_eq *eq, uint32_t q_len); +static void oce_mq_free(struct oce_mq *mq); +static int oce_destroy_q(POCE_SOFTC sc, struct oce_mbx + *mbx, size_t req_size, enum qtype qtype); +struct oce_cq *oce_cq_create(POCE_SOFTC sc, + struct oce_eq *eq, + uint32_t q_len, + uint32_t item_size, + uint32_t sol_event, + uint32_t is_eventable, + uint32_t nodelay, uint32_t ncoalesce); +static void oce_cq_del(POCE_SOFTC sc, struct oce_cq *cq); + + + +/** + * @brief Create and initialize all the queues on the board + * @param sc software handle to the device + * @returns 0 if successful, or error + **/ +int +oce_queue_init_all(POCE_SOFTC sc) +{ + int rc = 0, i, vector; + struct oce_wq *wq; + struct oce_rq *rq; + + /* alloc TX/RX queues */ + for_all_wq_queues(sc, wq, i) { + sc->wq[i] = oce_wq_init(sc, sc->tx_ring_size, + NIC_WQ_TYPE_STANDARD); + if (!sc->wq[i]) + goto error; + + } + + for_all_rq_queues(sc, rq, i) { + sc->rq[i] = oce_rq_init(sc, sc->rx_ring_size, sc->rq_frag_size, + OCE_MAX_JUMBO_FRAME_SIZE, + (i == 0) ? 0 : sc->rss_enable); + if (!sc->rq[i]) + goto error; + } + + /* Create network interface on card */ + if (oce_create_nw_interface(sc)) + goto error; + + /* create all of the event queues */ + for (vector = 0; vector < sc->intr_count; vector++) { + sc->eq[vector] = oce_eq_create(sc, EQ_LEN_1024, EQE_SIZE_4, + 0, vector); + if (!sc->eq[vector]) + goto error; + } + + /* create Tx, Rx and mcc queues */ + for_all_wq_queues(sc, wq, i) { + rc = oce_wq_create(wq, sc->eq[i]); + if (rc) + goto error; + wq->queue_index = i; + TASK_INIT(&wq->txtask, 1, oce_tx_task, wq); + } + + for_all_rq_queues(sc, rq, i) { + rc = oce_rq_create(rq, sc->if_id, + sc->eq[(i == 0) ? 0:(i-1)]); + if (rc) + goto error; + rq->queue_index = i; + } + + sc->mq = oce_mq_create(sc, sc->eq[0], 64); + if (!sc->mq) + goto error; + + return rc; + +error: + oce_queue_release_all(sc); + return 1; +} + + + +/** + * @brief Releases all mailbox queues created + * @param sc software handle to the device + */ +void +oce_queue_release_all(POCE_SOFTC sc) +{ + int i = 0; + struct oce_wq *wq; + struct oce_rq *rq; + struct oce_eq *eq; + + for_all_rq_queues(sc, rq, i) { + if (rq) { + oce_rq_del(sc->rq[i]); + oce_rq_free(sc->rq[i]); + } + } + + for_all_wq_queues(sc, wq, i) { + if (wq) { + oce_wq_del(sc->wq[i]); + oce_wq_free(sc->wq[i]); + } + } + + if (sc->mq) + oce_mq_free(sc->mq); + + for_all_evnt_queues(sc, eq, i) { + if (eq) + oce_eq_del(sc->eq[i]); + } +} + + + +/** + * @brief Function to create a WQ for NIC Tx + * @param sc software handle to the device + * @param qlen number of entries in the queue + * @param wq_type work queue type + * @returns the pointer to the WQ created or NULL on failure + */ +static struct +oce_wq *oce_wq_init(POCE_SOFTC sc, uint32_t q_len, uint32_t wq_type) +{ + struct oce_wq *wq; + int rc = 0, i; + + /* q_len must be min 256 and max 2k */ + if (q_len < 256 || q_len > 2048) { + device_printf(sc->dev, + "Invalid q length. Must be " + "[256, 2000]: 0x%x\n", q_len); + return NULL; + } + + /* allocate wq */ + wq = malloc(sizeof(struct oce_wq), M_DEVBUF, M_NOWAIT | M_ZERO); + if (!wq) + return NULL; + + /* Set the wq config */ + wq->cfg.q_len = q_len; + wq->cfg.wq_type = (uint8_t) wq_type; + wq->cfg.eqd = OCE_DEFAULT_WQ_EQD; + wq->cfg.nbufs = 2 * wq->cfg.q_len; + wq->cfg.nhdl = 2 * wq->cfg.q_len; + + wq->parent = (void *)sc; + + rc = bus_dma_tag_create(bus_get_dma_tag(sc->dev), + 1, 0, + BUS_SPACE_MAXADDR, + BUS_SPACE_MAXADDR, + NULL, NULL, + OCE_MAX_TX_SIZE, + OCE_MAX_TX_ELEMENTS, + PAGE_SIZE, 0, NULL, NULL, &wq->tag); + + if (rc) + goto free_wq; + + + for (i = 0; i < OCE_WQ_PACKET_ARRAY_SIZE; i++) { + rc = bus_dmamap_create(wq->tag, 0, &wq->pckts[i].map); + if (rc) + goto free_wq; + } + + wq->ring = oce_create_ring_buffer(sc, q_len, NIC_WQE_SIZE); + if (!wq->ring) + goto free_wq; + + + LOCK_CREATE(&wq->tx_lock, "TX_lock"); + +#if __FreeBSD_version >= 800000 + /* Allocate buf ring for multiqueue*/ + wq->br = buf_ring_alloc(4096, M_DEVBUF, + M_WAITOK, &wq->tx_lock.mutex); + if (!wq->br) + goto free_wq; +#endif + return wq; + + +free_wq: + device_printf(sc->dev, "Create WQ failed\n"); + oce_wq_free(wq); + return NULL; +} + + + +/** + * @brief Frees the work queue + * @param wq pointer to work queue to free + */ +static void +oce_wq_free(struct oce_wq *wq) +{ + POCE_SOFTC sc = (POCE_SOFTC) wq->parent; + int i; + + taskqueue_drain(taskqueue_swi, &wq->txtask); + + if (wq->ring != NULL) { + oce_destroy_ring_buffer(sc, wq->ring); + wq->ring = NULL; + } + + for (i = 0; i < OCE_WQ_PACKET_ARRAY_SIZE; i++) { + if (wq->pckts[i].map != NULL) { + bus_dmamap_unload(wq->tag, wq->pckts[i].map); + bus_dmamap_destroy(wq->tag, wq->pckts[i].map); + wq->pckts[i].map = NULL; + } + } + + if (wq->tag != NULL) + bus_dma_tag_destroy(wq->tag); + if (wq->br != NULL) + buf_ring_free(wq->br, M_DEVBUF); + + LOCK_DESTROY(&wq->tx_lock); + free(wq, M_DEVBUF); +} + + + +/** + * @brief Create a work queue + * @param wq pointer to work queue + * @param eq pointer to associated event queue + */ +static int +oce_wq_create(struct oce_wq *wq, struct oce_eq *eq) +{ + POCE_SOFTC sc = wq->parent; + struct oce_cq *cq; + int rc = 0; + + /* create the CQ */ + cq = oce_cq_create(sc, + eq, + CQ_LEN_1024, + sizeof(struct oce_nic_tx_cqe), 0, 1, 0, 3); + if (!cq) + return ENXIO; + + + wq->cq = cq; + + rc = oce_mbox_create_wq(wq); + if (rc) + goto error; + + wq->qstate = QCREATED; + wq->wq_free = wq->cfg.q_len; + wq->ring->cidx = 0; + wq->ring->pidx = 0; + + eq->cq[eq->cq_valid] = cq; + eq->cq_valid++; + cq->cb_arg = wq; + cq->cq_handler = oce_wq_handler; + + return 0; + +error: + device_printf(sc->dev, "WQ create failed\n"); + oce_wq_del(wq); + return rc; +} + + + + +/** + * @brief Delete a work queue + * @param wq pointer to work queue + */ +static void +oce_wq_del(struct oce_wq *wq) +{ + struct oce_mbx mbx; + struct mbx_delete_nic_wq *fwcmd; + POCE_SOFTC sc = (POCE_SOFTC) wq->parent; + + if (wq->qstate == QCREATED) { + bzero(&mbx, sizeof(struct oce_mbx)); + /* now fill the command */ + fwcmd = (struct mbx_delete_nic_wq *)&mbx.payload; + fwcmd->params.req.wq_id = wq->wq_id; + (void)oce_destroy_q(sc, &mbx, + sizeof(struct mbx_delete_nic_wq), QTYPE_WQ); + wq->qstate = QDELETED; + } + + if (wq->cq != NULL) { + oce_cq_del(sc, wq->cq); + wq->cq = NULL; + } +} + + + +/** + * @brief function to allocate receive queue resources + * @param sc software handle to the device + * @param q_len length of receive queue + * @param frag_size size of an receive queue fragment + * @param mtu maximum transmission unit + * @param rss is-rss-queue flag + * @returns the pointer to the RQ created or NULL on failure + */ +static struct +oce_rq *oce_rq_init(POCE_SOFTC sc, + uint32_t q_len, + uint32_t frag_size, + uint32_t mtu, uint32_t rss) +{ + struct oce_rq *rq; + int rc = 0, i; + + if (OCE_LOG2(frag_size) <= 0) + return NULL; + + if ((q_len == 0) || (q_len > 1024)) + return NULL; + + /* allocate the rq */ + rq = malloc(sizeof(struct oce_rq), M_DEVBUF, M_NOWAIT | M_ZERO); + if (!rq) + return NULL; + + + rq->cfg.q_len = q_len; + rq->cfg.frag_size = frag_size; + rq->cfg.mtu = mtu; + rq->cfg.eqd = 0; + rq->lro_pkts_queued = 0; + rq->cfg.is_rss_queue = rss; + rq->packets_in = 0; + rq->packets_out = 0; + rq->pending = 0; + + rq->parent = (void *)sc; + + rc = bus_dma_tag_create(bus_get_dma_tag(sc->dev), + 1, 0, + BUS_SPACE_MAXADDR, + BUS_SPACE_MAXADDR, + NULL, NULL, + OCE_MAX_RX_SIZE, + 1, PAGE_SIZE, 0, NULL, NULL, &rq->tag); + + if (rc) + goto free_rq; + + for (i = 0; i < OCE_RQ_PACKET_ARRAY_SIZE; i++) { + rc = bus_dmamap_create(rq->tag, 0, &rq->pckts[i].map); + if (rc) + goto free_rq; + } + + /* create the ring buffer */ + rq->ring = oce_create_ring_buffer(sc, q_len, + sizeof(struct oce_nic_rqe)); + if (!rq->ring) + goto free_rq; + + LOCK_CREATE(&rq->rx_lock, "RX_lock"); + + return rq; + +free_rq: + device_printf(sc->dev, "Create RQ failed\n"); + oce_rq_free(rq); + return NULL; +} + + + + +/** + * @brief Free a receive queue + * @param rq pointer to receive queue + */ +static void +oce_rq_free(struct oce_rq *rq) +{ + POCE_SOFTC sc = (POCE_SOFTC) rq->parent; + int i = 0 ; + + if (rq->ring != NULL) { + oce_destroy_ring_buffer(sc, rq->ring); + rq->ring = NULL; + } + for (i = 0; i < OCE_RQ_PACKET_ARRAY_SIZE; i++) { + if (rq->pckts[i].map != NULL) { + bus_dmamap_unload(rq->tag, rq->pckts[i].map); + bus_dmamap_destroy(rq->tag, rq->pckts[i].map); + rq->pckts[i].map = NULL; + } + if (rq->pckts[i].mbuf) { + m_free(rq->pckts[i].mbuf); + rq->pckts[i].mbuf = NULL; + } + } + + if (rq->tag != NULL) + bus_dma_tag_destroy(rq->tag); + + LOCK_DESTROY(&rq->rx_lock); + free(rq, M_DEVBUF); +} + + + + +/** + * @brief Create a receive queue + * @param rq receive queue + * @param if_id interface identifier index` + * @param eq pointer to event queue + */ +static int +oce_rq_create(struct oce_rq *rq, uint32_t if_id, struct oce_eq *eq) +{ + POCE_SOFTC sc = rq->parent; + struct oce_cq *cq; + + cq = oce_cq_create(sc, + eq, + CQ_LEN_1024, + sizeof(struct oce_nic_rx_cqe), 0, 1, 0, 3); + if (!cq) + return ENXIO; + + rq->cq = cq; + rq->cfg.if_id = if_id; + + /* Dont create RQ here. Create in if_activate */ + rq->qstate = 0; + rq->ring->cidx = 0; + rq->ring->pidx = 0; + eq->cq[eq->cq_valid] = cq; + eq->cq_valid++; + cq->cb_arg = rq; + cq->cq_handler = oce_rq_handler; + + return 0; + +} + + + + +/** + * @brief Delete a receive queue + * @param rq receive queue + */ +static void +oce_rq_del(struct oce_rq *rq) +{ + POCE_SOFTC sc = (POCE_SOFTC) rq->parent; + struct oce_mbx mbx; + struct mbx_delete_nic_rq *fwcmd; + + if (rq->qstate == QCREATED) { + bzero(&mbx, sizeof(mbx)); + + fwcmd = (struct mbx_delete_nic_rq *)&mbx.payload; + fwcmd->params.req.rq_id = rq->rq_id; + (void)oce_destroy_q(sc, &mbx, + sizeof(struct mbx_delete_nic_rq), QTYPE_RQ); + rq->qstate = QDELETED; + } + + if (rq->cq != NULL) { + oce_cq_del(sc, rq->cq); + rq->cq = NULL; + } +} + + + +/** + * @brief function to create an event queue + * @param sc software handle to the device + * @param q_len length of event queue + * @param item_size size of an event queue item + * @param eq_delay event queue delay + * @retval eq success, pointer to event queue + * @retval NULL failure + */ +static struct +oce_eq *oce_eq_create(POCE_SOFTC sc, uint32_t q_len, + uint32_t item_size, + uint32_t eq_delay, + uint32_t vector) +{ + struct oce_eq *eq; + int rc = 0; + + /* allocate an eq */ + eq = malloc(sizeof(struct oce_eq), M_DEVBUF, M_NOWAIT | M_ZERO); + if (eq == NULL) + return NULL; + + eq->parent = (void *)sc; + eq->eq_id = 0xffff; + eq->ring = oce_create_ring_buffer(sc, q_len, item_size); + if (!eq->ring) + goto free_eq; + + eq->eq_cfg.q_len = q_len; + eq->eq_cfg.item_size = item_size; + eq->eq_cfg.cur_eqd = (uint8_t) eq_delay; + + rc = oce_mbox_create_eq(eq); + if (rc) + goto free_eq; + + sc->intrs[sc->neqs++].eq = eq; + + return eq; + +free_eq: + oce_eq_del(eq); + return NULL; +} + + + + +/** + * @brief Function to delete an event queue + * @param eq pointer to an event queue + */ +static void +oce_eq_del(struct oce_eq *eq) +{ + struct oce_mbx mbx; + struct mbx_destroy_common_eq *fwcmd; + POCE_SOFTC sc = (POCE_SOFTC) eq->parent; + + if (eq->eq_id != 0xffff) { + bzero(&mbx, sizeof(mbx)); + fwcmd = (struct mbx_destroy_common_eq *)&mbx.payload; + fwcmd->params.req.id = eq->eq_id; + (void)oce_destroy_q(sc, &mbx, + sizeof(struct mbx_destroy_common_eq), QTYPE_EQ); + } + + if (eq->ring != NULL) { + oce_destroy_ring_buffer(sc, eq->ring); + eq->ring = NULL; + } + + free(eq, M_DEVBUF); + +} + + + + +/** + * @brief Function to create an MQ + * @param sc software handle to the device + * @param eq the EQ to associate with the MQ for event notification + * @param q_len the number of entries to create in the MQ + * @returns pointer to the created MQ, failure otherwise + */ +static struct oce_mq * +oce_mq_create(POCE_SOFTC sc, struct oce_eq *eq, uint32_t q_len) +{ + struct oce_mbx mbx; + struct mbx_create_common_mq *fwcmd = NULL; + struct oce_mq *mq = NULL; + int rc = 0; + struct oce_cq *cq; + oce_mq_ctx_t *ctx; + uint32_t num_pages; + uint32_t page_size; + uint32_t version; + + + cq = oce_cq_create(sc, eq, CQ_LEN_256, + sizeof(struct oce_mq_cqe), 1, 1, 0, 0); + if (!cq) + return NULL; + + /* allocate the mq */ + mq = malloc(sizeof(struct oce_mq), M_DEVBUF, M_NOWAIT | M_ZERO); + if (!mq) { + oce_cq_del(sc, cq); + goto error; + } + + mq->parent = sc; + + mq->ring = oce_create_ring_buffer(sc, q_len, sizeof(struct oce_mbx)); + if (!mq->ring) + goto error; + + bzero(&mbx, sizeof(struct oce_mbx)); + + fwcmd = (struct mbx_create_common_mq *)&mbx.payload; + version = OCE_MBX_VER_V0; + mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, + MBX_SUBSYSTEM_COMMON, + OPCODE_COMMON_CREATE_MQ, + MBX_TIMEOUT_SEC, + sizeof(struct mbx_create_common_mq), + version); + + num_pages = oce_page_list(mq->ring, &fwcmd->params.req.pages[0]); + page_size = mq->ring->num_items * mq->ring->item_size; + + ctx = &fwcmd->params.req.context; + ctx->v0.num_pages = num_pages; + ctx->v0.cq_id = cq->cq_id; + ctx->v0.ring_size = OCE_LOG2(q_len) + 1; + ctx->v0.valid = 1; + + mbx.u0.s.embedded = 1; + mbx.payload_length = sizeof(struct mbx_create_common_mq); + DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); + + rc = oce_mbox_post(sc, &mbx, NULL); + if (rc) + goto error; + + mq->mq_id = LE_16(fwcmd->params.rsp.mq_id); + mq->cq = cq; + eq->cq[eq->cq_valid] = cq; + eq->cq_valid++; + mq->cq->eq = eq; + mq->cfg.q_len = (uint8_t) q_len; + mq->cfg.eqd = 0; + mq->qstate = QCREATED; + + mq->cq->cb_arg = mq; + mq->cq->cq_handler = oce_mq_handler; + + return mq; + +error: + device_printf(sc->dev, "MQ create failed\n"); + oce_mq_free(mq); + mq = NULL; + return mq; +} + + + + + +/** + * @brief Function to free a mailbox queue + * @param mq pointer to a mailbox queue + */ +static void +oce_mq_free(struct oce_mq *mq) +{ + POCE_SOFTC sc = (POCE_SOFTC) mq->parent; + struct oce_mbx mbx; + struct mbx_destroy_common_mq *fwcmd; + + if (!mq) + return; + + if (mq->ring != NULL) { + oce_destroy_ring_buffer(sc, mq->ring); + mq->ring = NULL; + if (mq->qstate == QCREATED) { + bzero(&mbx, sizeof (struct oce_mbx)); + fwcmd = (struct mbx_destroy_common_mq *)&mbx.payload; + fwcmd->params.req.id = mq->mq_id; + (void) oce_destroy_q(sc, &mbx, + sizeof (struct mbx_destroy_common_mq), + QTYPE_MQ); + } + mq->qstate = QDELETED; + } + + if (mq->cq != NULL) { + oce_cq_del(sc, mq->cq); + mq->cq = NULL; + } + + free(mq, M_DEVBUF); + mq = NULL; +} + + + +/** + * @brief Function to delete a EQ, CQ, MQ, WQ or RQ + * @param sc sofware handle to the device + * @param mbx mailbox command to send to the fw to delete the queue + * (mbx contains the queue information to delete) + * @param req_size the size of the mbx payload dependent on the qtype + * @param qtype the type of queue i.e. EQ, CQ, MQ, WQ or RQ + * @returns 0 on success, failure otherwise + */ +static int +oce_destroy_q(POCE_SOFTC sc, struct oce_mbx *mbx, size_t req_size, + enum qtype qtype) +{ + struct mbx_hdr *hdr = (struct mbx_hdr *)&mbx->payload; + int opcode; + int subsys; + int rc = 0; + + switch (qtype) { + case QTYPE_EQ: + opcode = OPCODE_COMMON_DESTROY_EQ; + subsys = MBX_SUBSYSTEM_COMMON; + break; + case QTYPE_CQ: + opcode = OPCODE_COMMON_DESTROY_CQ; + subsys = MBX_SUBSYSTEM_COMMON; + break; + case QTYPE_MQ: + opcode = OPCODE_COMMON_DESTROY_MQ; + subsys = MBX_SUBSYSTEM_COMMON; + break; + case QTYPE_WQ: + opcode = NIC_DELETE_WQ; + subsys = MBX_SUBSYSTEM_NIC; + break; + case QTYPE_RQ: + opcode = NIC_DELETE_RQ; + subsys = MBX_SUBSYSTEM_NIC; + break; + default: + return EINVAL; + } + + mbx_common_req_hdr_init(hdr, 0, 0, subsys, + opcode, MBX_TIMEOUT_SEC, req_size, + OCE_MBX_VER_V0); + + mbx->u0.s.embedded = 1; + mbx->payload_length = (uint32_t) req_size; + DW_SWAP(u32ptr(mbx), mbx->payload_length + OCE_BMBX_RHDR_SZ); + + rc = oce_mbox_post(sc, mbx, NULL); + + if (rc != 0) + device_printf(sc->dev, "Failed to del q\n"); + + return rc; +} + + + +/** + * @brief Function to create a completion queue + * @param sc software handle to the device + * @param eq optional eq to be associated with to the cq + * @param q_len length of completion queue + * @param item_size size of completion queue items + * @param sol_event command context event + * @param is_eventable event table + * @param nodelay no delay flag + * @param ncoalesce no coalescence flag + * @returns pointer to the cq created, NULL on failure + */ +struct oce_cq * +oce_cq_create(POCE_SOFTC sc, struct oce_eq *eq, + uint32_t q_len, + uint32_t item_size, + uint32_t sol_event, + uint32_t is_eventable, + uint32_t nodelay, uint32_t ncoalesce) +{ + struct oce_cq *cq = NULL; + int rc = 0; + + cq = malloc(sizeof(struct oce_cq), M_DEVBUF, M_NOWAIT | M_ZERO); + if (!cq) + return NULL; + + cq->ring = oce_create_ring_buffer(sc, q_len, item_size); + if (!cq->ring) + goto error; + + cq->parent = sc; + cq->eq = eq; + cq->cq_cfg.q_len = q_len; + cq->cq_cfg.item_size = item_size; + cq->cq_cfg.nodelay = (uint8_t) nodelay; + + rc = oce_mbox_cq_create(cq, ncoalesce, is_eventable); + if (rc) + goto error; + + sc->cq[sc->ncqs++] = cq; + + return cq; + +error: + device_printf(sc->dev, "CQ create failed\n"); + oce_cq_del(sc, cq); + return NULL; +} + + + +/** + * @brief Deletes the completion queue + * @param sc software handle to the device + * @param cq pointer to a completion queue + */ +static void +oce_cq_del(POCE_SOFTC sc, struct oce_cq *cq) +{ + struct oce_mbx mbx; + struct mbx_destroy_common_cq *fwcmd; + + if (cq->ring != NULL) { + + bzero(&mbx, sizeof(struct oce_mbx)); + /* now fill the command */ + fwcmd = (struct mbx_destroy_common_cq *)&mbx.payload; + fwcmd->params.req.id = cq->cq_id; + (void)oce_destroy_q(sc, &mbx, + sizeof(struct mbx_destroy_common_cq), QTYPE_CQ); + /*NOW destroy the ring */ + oce_destroy_ring_buffer(sc, cq->ring); + cq->ring = NULL; + } + + free(cq, M_DEVBUF); + cq = NULL; +} + + + +/** + * @brief Start a receive queue + * @param rq pointer to a receive queue + */ +int +oce_start_rq(struct oce_rq *rq) +{ + int rc; + + rc = oce_alloc_rx_bufs(rq, rq->cfg.q_len); + + if (rc == 0) + oce_arm_cq(rq->parent, rq->cq->cq_id, 0, TRUE); + return rc; +} + + + +/** + * @brief Start a work queue + * @param wq pointer to a work queue + */ +int +oce_start_wq(struct oce_wq *wq) +{ + oce_arm_cq(wq->parent, wq->cq->cq_id, 0, TRUE); + return 0; +} + + + +/** + * @brief Start a mailbox queue + * @param mq pointer to a mailbox queue + */ +int +oce_start_mq(struct oce_mq *mq) +{ + oce_arm_cq(mq->parent, mq->cq->cq_id, 0, TRUE); + return 0; +} + + + +/** + * @brief Function to arm an EQ so that it can generate events + * @param sc software handle to the device + * @param qid id of the EQ returned by the fw at the time of creation + * @param npopped number of EQEs to arm + * @param rearm rearm bit enable/disable + * @param clearint bit to clear the interrupt condition because of which + * EQEs are generated + */ +void +oce_arm_eq(POCE_SOFTC sc, + int16_t qid, int npopped, uint32_t rearm, uint32_t clearint) +{ + eq_db_t eq_db = { 0 }; + + eq_db.bits.rearm = rearm; + eq_db.bits.event = 1; + eq_db.bits.num_popped = npopped; + eq_db.bits.clrint = clearint; + eq_db.bits.qid = qid; + OCE_WRITE_REG32(sc, db, PD_EQ_DB, eq_db.dw0); + +} + + + + +/** + * @brief Function to arm a CQ with CQEs + * @param sc software handle to the device + * @param qid id of the CQ returned by the fw at the time of creation + * @param npopped number of CQEs to arm + * @param rearm rearm bit enable/disable + */ +void oce_arm_cq(POCE_SOFTC sc, int16_t qid, int npopped, uint32_t rearm) +{ + cq_db_t cq_db = { 0 }; + + cq_db.bits.rearm = rearm; + cq_db.bits.num_popped = npopped; + cq_db.bits.event = 0; + cq_db.bits.qid = qid; + OCE_WRITE_REG32(sc, db, PD_CQ_DB, cq_db.dw0); + +} + + + + +/* + * @brief function to cleanup the eqs used during stop + * @param eq pointer to event queue structure + * @returns the number of EQs processed + */ +void +oce_drain_eq(struct oce_eq *eq) +{ + + struct oce_eqe *eqe; + uint16_t num_eqe = 0; + POCE_SOFTC sc = eq->parent; + + do { + eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe); + if (eqe->evnt == 0) + break; + eqe->evnt = 0; + bus_dmamap_sync(eq->ring->dma.tag, eq->ring->dma.map, + BUS_DMASYNC_POSTWRITE); + num_eqe++; + RING_GET(eq->ring, 1); + + } while (TRUE); + + oce_arm_eq(sc, eq->eq_id, num_eqe, FALSE, TRUE); + +} + + + +void +oce_drain_wq_cq(struct oce_wq *wq) +{ + POCE_SOFTC sc = wq->parent; + struct oce_cq *cq = wq->cq; + struct oce_nic_tx_cqe *cqe; + int num_cqes = 0; + + bus_dmamap_sync(cq->ring->dma.tag, cq->ring->dma.map, + BUS_DMASYNC_POSTWRITE); + + do { + cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_tx_cqe); + if (cqe->u0.dw[3] == 0) + break; + cqe->u0.dw[3] = 0; + bus_dmamap_sync(cq->ring->dma.tag, cq->ring->dma.map, + BUS_DMASYNC_POSTWRITE); + RING_GET(cq->ring, 1); + num_cqes++; + + } while (TRUE); + + oce_arm_cq(sc, cq->cq_id, num_cqes, FALSE); + +} + + +/* + * @brief function to drain a MCQ and process its CQEs + * @param dev software handle to the device + * @param cq pointer to the cq to drain + * @returns the number of CQEs processed + */ +void +oce_drain_mq_cq(void *arg) +{ + /* TODO: additional code. */ + return; +} + + + +/** + * @brief function to process a Recieve queue + * @param arg pointer to the RQ to charge + * @return number of cqes processed + */ +void +oce_drain_rq_cq(struct oce_rq *rq) +{ + struct oce_nic_rx_cqe *cqe; + uint16_t num_cqe = 0; + struct oce_cq *cq; + POCE_SOFTC sc; + + sc = rq->parent; + cq = rq->cq; + cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_rx_cqe); + /* dequeue till you reach an invalid cqe */ + while (RQ_CQE_VALID(cqe)) { + RQ_CQE_INVALIDATE(cqe); + RING_GET(cq->ring, 1); + cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, + struct oce_nic_rx_cqe); + num_cqe++; + } + oce_arm_cq(sc, cq->cq_id, num_cqe, FALSE); + + return; +} + + +void +oce_free_posted_rxbuf(struct oce_rq *rq) +{ + struct oce_packet_desc *pd; + + while (rq->pending) { + + pd = &rq->pckts[rq->packets_out]; + bus_dmamap_sync(rq->tag, pd->map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(rq->tag, pd->map); + if (pd->mbuf != NULL) { + m_freem(pd->mbuf); + pd->mbuf = NULL; + } + + if ((rq->packets_out + 1) == OCE_RQ_PACKET_ARRAY_SIZE) + rq->packets_out = 0; + else + rq->packets_out++; + + rq->pending--; + } + +} + +void +oce_stop_rx(POCE_SOFTC sc) +{ + struct oce_mbx mbx; + struct mbx_delete_nic_rq *fwcmd; + struct oce_rq *rq; + int i = 0; + + for_all_rq_queues(sc, rq, i) { + if (rq->qstate == QCREATED) { + /* Delete rxq in firmware */ + + bzero(&mbx, sizeof(mbx)); + fwcmd = (struct mbx_delete_nic_rq *)&mbx.payload; + fwcmd->params.req.rq_id = rq->rq_id; + + (void)oce_destroy_q(sc, &mbx, + sizeof(struct mbx_delete_nic_rq), QTYPE_RQ); + + rq->qstate = QDELETED; + + DELAY(1); + + /* Free posted RX buffers that are not used */ + oce_free_posted_rxbuf(rq); + + } + } +} + + + +int +oce_start_rx(POCE_SOFTC sc) +{ + struct oce_rq *rq; + int rc = 0, i; + + for_all_rq_queues(sc, rq, i) { + if (rq->qstate == QCREATED) + continue; + rc = oce_mbox_create_rq(rq); + if (rc) + goto error; + /* reset queue pointers */ + rq->qstate = QCREATED; + rq->pending = 0; + rq->ring->cidx = 0; + rq->ring->pidx = 0; + rq->packets_in = 0; + rq->packets_out = 0; + } + + DELAY(1); + + /* RSS config */ + if (sc->rss_enable) { + rc = oce_config_nic_rss(sc, (uint8_t) sc->if_id, RSS_ENABLE); + if (rc) + goto error; + + } + + return rc; +error: + device_printf(sc->dev, "Start RX failed\n"); + return rc; + +} + + + |
