/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright © 2003-2011 Emulex. All rights reserved. */ /* * Source file containing the implementation of the Transmit * Path */ #include static void oce_free_wqed(struct oce_wq *wq, oce_wqe_desc_t *wqed); static int oce_map_wqe(struct oce_wq *wq, oce_wqe_desc_t *wqed, mblk_t *mp, uint32_t pkt_len); static int oce_bcopy_wqe(struct oce_wq *wq, oce_wqe_desc_t *wqed, mblk_t *mp, uint32_t pkt_len); static void oce_wqb_dtor(struct oce_wq *wq, oce_wq_bdesc_t *wqbd); static int oce_wqb_ctor(oce_wq_bdesc_t *wqbd, struct oce_wq *wq, size_t size, int flags); static inline oce_wq_bdesc_t *oce_wqb_alloc(struct oce_wq *wq); static void oce_wqb_free(struct oce_wq *wq, oce_wq_bdesc_t *wqbd); static void oce_wqmd_free(struct oce_wq *wq, oce_wq_mdesc_t *wqmd); static void oce_wqm_free(struct oce_wq *wq, oce_wq_mdesc_t *wqmd); static oce_wq_mdesc_t *oce_wqm_alloc(struct oce_wq *wq); static int oce_wqm_ctor(oce_wq_mdesc_t *wqmd, struct oce_wq *wq); static void oce_wqm_dtor(struct oce_wq *wq, oce_wq_mdesc_t *wqmd); static void oce_fill_ring_descs(struct oce_wq *wq, oce_wqe_desc_t *wqed); static void oce_remove_vtag(mblk_t *mp); static void oce_insert_vtag(mblk_t *mp, uint16_t vlan_tag); static inline int oce_process_tx_compl(struct oce_wq *wq, boolean_t rearm); static ddi_dma_attr_t tx_map_dma_attr = { DMA_ATTR_V0, /* version number */ 0x0000000000000000ull, /* low address */ 0xFFFFFFFFFFFFFFFFull, /* high address */ 0x0000000000010000ull, /* dma counter max */ OCE_TXMAP_ALIGN, /* alignment */ 0x7FF, /* burst sizes */ 0x00000001, /* minimum transfer size */ 0x00000000FFFFFFFFull, /* maximum transfer size */ 0xFFFFFFFFFFFFFFFFull, /* maximum segment size */ OCE_MAX_TXDMA_COOKIES, /* scatter/gather list length */ 0x00000001, /* granularity */ DDI_DMA_FLAGERR /* dma_attr_flags */ }; ddi_dma_attr_t oce_tx_dma_buf_attr = { DMA_ATTR_V0, /* version number */ 0x0000000000000000ull, /* low address */ 0xFFFFFFFFFFFFFFFFull, /* high address */ 0x00000000FFFFFFFFull, /* dma counter max */ OCE_DMA_ALIGNMENT, /* alignment */ 0x000007FF, /* burst sizes */ 0x00000001, /* minimum transfer size */ 0x00000000FFFFFFFFull, /* maximum transfer size */ 0xFFFFFFFFFFFFFFFFull, /* maximum segment size */ 1, /* scatter/gather list length */ 0x00000001, /* granularity */ DDI_DMA_FLAGERR /* dma_attr_flags */ }; /* * WQ map handle destructor * * wq - Pointer to WQ structure * wqmd - pointer to WQE mapping handle descriptor * * return none */ static void oce_wqm_dtor(struct oce_wq *wq, oce_wq_mdesc_t *wqmd) { _NOTE(ARGUNUSED(wq)); /* Free the DMA handle */ if (wqmd->dma_handle != NULL) (void) ddi_dma_free_handle(&(wqmd->dma_handle)); wqmd->dma_handle = NULL; } /* oce_wqm_dtor */ /* * WQ map handles contructor * * wqmd - pointer to WQE mapping handle descriptor * wq - Pointer to WQ structure * * return DDI_SUCCESS=>success, DDI_FAILURE=>error */ static int oce_wqm_ctor(oce_wq_mdesc_t *wqmd, struct oce_wq *wq) { struct oce_dev *dev; int ret; dev = wq->parent; /* Allocate DMA handle */ ret = ddi_dma_alloc_handle(dev->dip, &tx_map_dma_attr, DDI_DMA_DONTWAIT, NULL, &wqmd->dma_handle); return (ret); } /* oce_wqm_ctor */ /* * function to create WQ mapping handles cache * * wq - pointer to WQ structure * * return DDI_SUCCESS=>success, DDI_FAILURE=>error */ int oce_wqm_cache_create(struct oce_wq *wq) { struct oce_dev *dev = wq->parent; int size; int cnt; int ret; size = wq->cfg.nhdl * sizeof (oce_wq_mdesc_t); wq->wq_mdesc_array = kmem_zalloc(size, KM_NOSLEEP); if (wq->wq_mdesc_array == NULL) { return (DDI_FAILURE); } /* Create the free buffer list */ OCE_LIST_CREATE(&wq->wq_mdesc_list, DDI_INTR_PRI(dev->intr_pri)); for (cnt = 0; cnt < wq->cfg.nhdl; cnt++) { ret = oce_wqm_ctor(&wq->wq_mdesc_array[cnt], wq); if (ret != DDI_SUCCESS) { goto wqm_fail; } OCE_LIST_INSERT_TAIL(&wq->wq_mdesc_list, &wq->wq_mdesc_array[cnt]); } return (DDI_SUCCESS); wqm_fail: oce_wqm_cache_destroy(wq); return (DDI_FAILURE); } /* * function to destroy WQ mapping handles cache * * wq - pointer to WQ structure * * return none */ void oce_wqm_cache_destroy(struct oce_wq *wq) { oce_wq_mdesc_t *wqmd; while ((wqmd = OCE_LIST_REM_HEAD(&wq->wq_mdesc_list)) != NULL) { oce_wqm_dtor(wq, wqmd); } kmem_free(wq->wq_mdesc_array, wq->cfg.nhdl * sizeof (oce_wq_mdesc_t)); OCE_LIST_DESTROY(&wq->wq_mdesc_list); } /* * function to create WQ buffer cache * * wq - pointer to WQ structure * buf_size - size of the buffer * * return DDI_SUCCESS=>success, DDI_FAILURE=>error */ int oce_wqb_cache_create(struct oce_wq *wq, size_t buf_size) { struct oce_dev *dev = wq->parent; int size; int cnt; int ret; size = wq->cfg.nbufs * sizeof (oce_wq_bdesc_t); wq->wq_bdesc_array = kmem_zalloc(size, KM_NOSLEEP); if (wq->wq_bdesc_array == NULL) { return (DDI_FAILURE); } /* Create the free buffer list */ OCE_LIST_CREATE(&wq->wq_buf_list, DDI_INTR_PRI(dev->intr_pri)); for (cnt = 0; cnt < wq->cfg.nbufs; cnt++) { ret = oce_wqb_ctor(&wq->wq_bdesc_array[cnt], wq, buf_size, DDI_DMA_STREAMING); if (ret != DDI_SUCCESS) { goto wqb_fail; } OCE_LIST_INSERT_TAIL(&wq->wq_buf_list, &wq->wq_bdesc_array[cnt]); } return (DDI_SUCCESS); wqb_fail: oce_wqb_cache_destroy(wq); return (DDI_FAILURE); } /* * function to destroy WQ buffer cache * * wq - pointer to WQ structure * * return none */ void oce_wqb_cache_destroy(struct oce_wq *wq) { oce_wq_bdesc_t *wqbd; while ((wqbd = OCE_LIST_REM_HEAD(&wq->wq_buf_list)) != NULL) { oce_wqb_dtor(wq, wqbd); } kmem_free(wq->wq_bdesc_array, wq->cfg.nbufs * sizeof (oce_wq_bdesc_t)); OCE_LIST_DESTROY(&wq->wq_buf_list); } /* * WQ buffer constructor * * wqbd - pointer to WQ buffer descriptor * wq - pointer to WQ structure * size - size of the buffer * flags - KM_SLEEP or KM_NOSLEEP * * return DDI_SUCCESS=>success, DDI_FAILURE=>error */ static int oce_wqb_ctor(oce_wq_bdesc_t *wqbd, struct oce_wq *wq, size_t size, int flags) { struct oce_dev *dev; dev = wq->parent; wqbd->wqb = oce_alloc_dma_buffer(dev, size, &oce_tx_dma_buf_attr, flags); if (wqbd->wqb == NULL) { return (DDI_FAILURE); } wqbd->frag_addr.dw.addr_lo = ADDR_LO(wqbd->wqb->addr); wqbd->frag_addr.dw.addr_hi = ADDR_HI(wqbd->wqb->addr); return (DDI_SUCCESS); } /* * WQ buffer destructor * * wq - pointer to WQ structure * wqbd - pointer to WQ buffer descriptor * * return none */ static void oce_wqb_dtor(struct oce_wq *wq, oce_wq_bdesc_t *wqbd) { oce_free_dma_buffer(wq->parent, wqbd->wqb); } /* * function to alloc WQE buffer descriptor * * wq - pointer to WQ structure * * return pointer to WQE buffer descriptor */ static inline oce_wq_bdesc_t * oce_wqb_alloc(struct oce_wq *wq) { return (OCE_LIST_REM_HEAD(&wq->wq_buf_list)); } /* * function to free WQE buffer descriptor * * wq - pointer to WQ structure * wqbd - pointer to WQ buffer descriptor * * return none */ static inline void oce_wqb_free(struct oce_wq *wq, oce_wq_bdesc_t *wqbd) { OCE_LIST_INSERT_TAIL(&wq->wq_buf_list, wqbd); } /* oce_wqb_free */ /* * function to allocate WQE mapping descriptor * * wq - pointer to WQ structure * * return pointer to WQE mapping descriptor */ static inline oce_wq_mdesc_t * oce_wqm_alloc(struct oce_wq *wq) { return (OCE_LIST_REM_HEAD(&wq->wq_mdesc_list)); } /* oce_wqm_alloc */ /* * function to insert WQE mapping descriptor to the list * * wq - pointer to WQ structure * wqmd - Pointer to WQ mapping descriptor * * return none */ static inline void oce_wqm_free(struct oce_wq *wq, oce_wq_mdesc_t *wqmd) { OCE_LIST_INSERT_TAIL(&wq->wq_mdesc_list, wqmd); } /* * function to free WQE mapping descriptor * * wq - pointer to WQ structure * wqmd - Pointer to WQ mapping descriptor * * return none */ static void oce_wqmd_free(struct oce_wq *wq, oce_wq_mdesc_t *wqmd) { if (wqmd == NULL) { return; } (void) ddi_dma_unbind_handle(wqmd->dma_handle); oce_wqm_free(wq, wqmd); } /* * WQED kmem_cache constructor * * buf - pointer to WQE descriptor * * return DDI_SUCCESS */ int oce_wqe_desc_ctor(void *buf, void *arg, int kmflags) { _NOTE(ARGUNUSED(buf)); _NOTE(ARGUNUSED(arg)); _NOTE(ARGUNUSED(kmflags)); return (DDI_SUCCESS); } /* * WQED kmem_cache destructor * * buf - pointer to WQE descriptor * * return none */ void oce_wqe_desc_dtor(void *buf, void *arg) { _NOTE(ARGUNUSED(buf)); _NOTE(ARGUNUSED(arg)); } /* * function to choose a WQ given a mblk depending on priority, flowID etc. * * dev - software handle to device * mp - the mblk to send * * return pointer to the WQ selected */ static uint8_t oce_tx_hash_policy = 0x4; struct oce_wq * oce_get_wq(struct oce_dev *dev, mblk_t *mp) { struct oce_wq *wq; int qidx = 0; if (dev->nwqs > 1) { qidx = mac_pkt_hash(DL_ETHER, mp, oce_tx_hash_policy, B_TRUE); qidx = qidx % dev->nwqs; } else { qidx = 0; } wq = dev->wq[qidx]; /* for the time being hardcode */ return (wq); } /* oce_get_wq */ /* * function to populate the single WQE * * wq - pointer to wq * wqed - pointer to WQ entry descriptor * * return none */ #pragma inline(oce_fill_ring_descs) static void oce_fill_ring_descs(struct oce_wq *wq, oce_wqe_desc_t *wqed) { struct oce_nic_frag_wqe *wqe; int i; /* Copy the precreate WQE descs to the ring desc */ for (i = 0; i < wqed->wqe_cnt; i++) { wqe = RING_GET_PRODUCER_ITEM_VA(wq->ring, struct oce_nic_frag_wqe); bcopy(&wqed->frag[i], wqe, NIC_WQE_SIZE); RING_PUT(wq->ring, 1); } } /* oce_fill_ring_descs */ /* * function to copy the packet to preallocated Tx buffer * * wq - pointer to WQ * wqed - Pointer to WQE descriptor * mp - Pointer to packet chain * pktlen - Size of the packet * * return 0=>success, error code otherwise */ static int oce_bcopy_wqe(struct oce_wq *wq, oce_wqe_desc_t *wqed, mblk_t *mp, uint32_t pkt_len) { oce_wq_bdesc_t *wqbd; caddr_t buf_va; struct oce_dev *dev = wq->parent; int len = 0; wqbd = oce_wqb_alloc(wq); if (wqbd == NULL) { atomic_inc_32(&dev->tx_noxmtbuf); oce_log(dev, CE_WARN, MOD_TX, "%s", "wqb pool empty"); return (ENOMEM); } /* create a fragment wqe for the packet */ wqed->frag[wqed->frag_idx].u0.s.frag_pa_hi = wqbd->frag_addr.dw.addr_hi; wqed->frag[wqed->frag_idx].u0.s.frag_pa_lo = wqbd->frag_addr.dw.addr_lo; buf_va = DBUF_VA(wqbd->wqb); /* copy pkt into buffer */ for (len = 0; mp != NULL && len < pkt_len; mp = mp->b_cont) { bcopy(mp->b_rptr, buf_va, MBLKL(mp)); buf_va += MBLKL(mp); len += MBLKL(mp); } (void) ddi_dma_sync(DBUF_DHDL(wqbd->wqb), 0, pkt_len, DDI_DMA_SYNC_FORDEV); if (oce_fm_check_dma_handle(dev, DBUF_DHDL(wqbd->wqb))) { ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); /* Free the buffer */ oce_wqb_free(wq, wqbd); return (EIO); } wqed->frag[wqed->frag_idx].u0.s.frag_len = pkt_len; wqed->hdesc[wqed->nhdl].hdl = (void *)(wqbd); wqed->hdesc[wqed->nhdl].type = COPY_WQE; wqed->frag_cnt++; wqed->frag_idx++; wqed->nhdl++; return (0); } /* oce_bcopy_wqe */ /* * function to copy the packet or dma map on the fly depending on size * * wq - pointer to WQ * wqed - Pointer to WQE descriptor * mp - Pointer to packet chain * * return DDI_SUCCESS=>success, DDI_FAILURE=>error */ static int oce_map_wqe(struct oce_wq *wq, oce_wqe_desc_t *wqed, mblk_t *mp, uint32_t pkt_len) { ddi_dma_cookie_t cookie; oce_wq_mdesc_t *wqmd; uint32_t ncookies; int ret; struct oce_dev *dev = wq->parent; wqmd = oce_wqm_alloc(wq); if (wqmd == NULL) { oce_log(dev, CE_WARN, MOD_TX, "%s", "wqm pool empty"); return (ENOMEM); } ret = ddi_dma_addr_bind_handle(wqmd->dma_handle, (struct as *)0, (caddr_t)mp->b_rptr, pkt_len, DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, &cookie, &ncookies); if (ret != DDI_DMA_MAPPED) { oce_log(dev, CE_WARN, MOD_TX, "MAP FAILED %d", ret); /* free the last one */ oce_wqm_free(wq, wqmd); return (ENOMEM); } do { wqed->frag[wqed->frag_idx].u0.s.frag_pa_hi = ADDR_HI(cookie.dmac_laddress); wqed->frag[wqed->frag_idx].u0.s.frag_pa_lo = ADDR_LO(cookie.dmac_laddress); wqed->frag[wqed->frag_idx].u0.s.frag_len = (uint32_t)cookie.dmac_size; wqed->frag_cnt++; wqed->frag_idx++; if (--ncookies > 0) ddi_dma_nextcookie(wqmd->dma_handle, &cookie); else break; } while (ncookies > 0); wqed->hdesc[wqed->nhdl].hdl = (void *)wqmd; wqed->hdesc[wqed->nhdl].type = MAPPED_WQE; wqed->nhdl++; return (0); } /* oce_map_wqe */ static inline int oce_process_tx_compl(struct oce_wq *wq, boolean_t rearm) { struct oce_nic_tx_cqe *cqe; uint16_t num_cqe = 0; struct oce_cq *cq; oce_wqe_desc_t *wqed; int wqe_freed = 0; struct oce_dev *dev; cq = wq->cq; dev = wq->parent; (void) ddi_dma_sync(cq->ring->dbuf->dma_handle, 0, 0, DDI_DMA_SYNC_FORKERNEL); mutex_enter(&wq->txc_lock); cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_tx_cqe); while (WQ_CQE_VALID(cqe)) { DW_SWAP(u32ptr(cqe), sizeof (struct oce_nic_tx_cqe)); /* update stats */ if (cqe->u0.s.status != 0) { atomic_inc_32(&dev->tx_errors); } /* complete the WQEs */ wqed = OCE_LIST_REM_HEAD(&wq->wqe_desc_list); wqe_freed = wqed->wqe_cnt; oce_free_wqed(wq, wqed); RING_GET(wq->ring, wqe_freed); atomic_add_32(&wq->wq_free, wqe_freed); /* clear the valid bit and progress cqe */ WQ_CQE_INVALIDATE(cqe); RING_GET(cq->ring, 1); cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_tx_cqe); num_cqe++; } /* for all valid CQE */ mutex_exit(&wq->txc_lock); if (num_cqe) oce_arm_cq(wq->parent, cq->cq_id, num_cqe, rearm); return (num_cqe); } /* oce_process_tx_completion */ /* * function to drain a TxCQ and process its CQEs * * dev - software handle to the device * cq - pointer to the cq to drain * * return the number of CQEs processed */ uint16_t oce_drain_wq_cq(void *arg) { uint16_t num_cqe = 0; struct oce_dev *dev; struct oce_wq *wq; wq = (struct oce_wq *)arg; dev = wq->parent; /* do while we do not reach a cqe that is not valid */ num_cqe = oce_process_tx_compl(wq, B_FALSE); /* check if we need to restart Tx */ if (wq->resched && num_cqe) { wq->resched = B_FALSE; mac_tx_update(dev->mac_handle); } return (num_cqe); } /* oce_process_wq_cqe */ /* * function to insert vtag to packet * * mp - mblk pointer * vlan_tag - tag to be inserted * * return none */ static inline void oce_insert_vtag(mblk_t *mp, uint16_t vlan_tag) { struct ether_vlan_header *evh; (void) memmove(mp->b_rptr - VTAG_SIZE, mp->b_rptr, 2 * ETHERADDRL); mp->b_rptr -= VTAG_SIZE; evh = (struct ether_vlan_header *)(void *)mp->b_rptr; evh->ether_tpid = htons(VLAN_TPID); evh->ether_tci = htons(vlan_tag); } /* * function to strip vtag from packet * * mp - mblk pointer * * return none */ static inline void oce_remove_vtag(mblk_t *mp) { (void) memmove(mp->b_rptr + VTAG_SIZE, mp->b_rptr, ETHERADDRL * 2); mp->b_rptr += VTAG_SIZE; } /* * function to xmit Single packet over the wire * * wq - pointer to WQ * mp - Pointer to packet chain * * return pointer to the packet */ mblk_t * oce_send_packet(struct oce_wq *wq, mblk_t *mp) { struct oce_nic_hdr_wqe *wqeh; struct oce_dev *dev; struct ether_header *eh; struct ether_vlan_header *evh; int32_t num_wqes; uint16_t etype; uint32_t ip_offset; uint32_t csum_flags = 0; boolean_t use_copy = B_FALSE; boolean_t tagged = B_FALSE; uint16_t vlan_tag; uint32_t reg_value = 0; oce_wqe_desc_t *wqed = NULL; mblk_t *nmp = NULL; mblk_t *tmp = NULL; uint32_t pkt_len = 0; int num_mblks = 0; int ret = 0; uint32_t mss = 0; uint32_t flags = 0; int len = 0; /* retrieve the adap priv struct ptr */ dev = wq->parent; /* check if we have enough free slots */ if (wq->wq_free < dev->tx_reclaim_threshold) { (void) oce_process_tx_compl(wq, B_FALSE); } if (wq->wq_free < OCE_MAX_TX_HDL) { return (mp); } /* check if we should copy */ for (tmp = mp; tmp != NULL; tmp = tmp->b_cont) { pkt_len += MBLKL(tmp); num_mblks++; } if (pkt_len == 0 || num_mblks == 0) { freemsg(mp); return (NULL); } /* retrieve LSO information */ mac_lso_get(mp, &mss, &flags); /* get the offload flags */ mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &csum_flags); /* restrict the mapped segment to wat we support */ if (num_mblks > OCE_MAX_TX_HDL) { nmp = msgpullup(mp, -1); if (nmp == NULL) { atomic_inc_32(&wq->pkt_drops); freemsg(mp); return (NULL); } /* Reset it to new collapsed mp */ freemsg(mp); mp = nmp; } /* Get the packet descriptor for Tx */ wqed = kmem_cache_alloc(wq->wqed_cache, KM_NOSLEEP); if (wqed == NULL) { atomic_inc_32(&wq->pkt_drops); freemsg(mp); return (NULL); } eh = (struct ether_header *)(void *)mp->b_rptr; if (ntohs(eh->ether_type) == VLAN_TPID) { evh = (struct ether_vlan_header *)(void *)mp->b_rptr; tagged = B_TRUE; etype = ntohs(evh->ether_type); ip_offset = sizeof (struct ether_vlan_header); pkt_len -= VTAG_SIZE; vlan_tag = ntohs(evh->ether_tci); oce_remove_vtag(mp); } else { etype = ntohs(eh->ether_type); ip_offset = sizeof (struct ether_header); } /* Save the WQ pointer */ wqed->wq = wq; wqed->frag_idx = 1; /* index zero is always header */ wqed->frag_cnt = 0; wqed->nhdl = 0; wqed->mp = NULL; OCE_LIST_LINK_INIT(&wqed->link); /* If entire packet is less than the copy limit just do copy */ if (pkt_len < dev->tx_bcopy_limit) { use_copy = B_TRUE; ret = oce_bcopy_wqe(wq, wqed, mp, pkt_len); } else { /* copy or dma map the individual fragments */ for (nmp = mp; nmp != NULL; nmp = nmp->b_cont) { len = MBLKL(nmp); if (len == 0) { continue; } if (len < dev->tx_bcopy_limit) { ret = oce_bcopy_wqe(wq, wqed, nmp, len); } else { ret = oce_map_wqe(wq, wqed, nmp, len); } if (ret != 0) break; } } /* * Any failure other than insufficient Q entries * drop the packet */ if (ret != 0) { oce_free_wqed(wq, wqed); atomic_inc_32(&wq->pkt_drops); freemsg(mp); return (NULL); } wqeh = (struct oce_nic_hdr_wqe *)&wqed->frag[0]; bzero(wqeh, sizeof (struct oce_nic_hdr_wqe)); /* fill rest of wqe header fields based on packet */ if (flags & HW_LSO) { wqeh->u0.s.lso = B_TRUE; wqeh->u0.s.lso_mss = mss; } if (csum_flags & HCK_FULLCKSUM) { uint8_t *proto; if (etype == ETHERTYPE_IP) { proto = (uint8_t *)(void *) (mp->b_rptr + ip_offset); if (proto[9] == 6) /* IPPROTO_TCP */ wqeh->u0.s.tcpcs = B_TRUE; else if (proto[9] == 17) /* IPPROTO_UDP */ wqeh->u0.s.udpcs = B_TRUE; } } if (csum_flags & HCK_IPV4_HDRCKSUM) wqeh->u0.s.ipcs = B_TRUE; if (tagged) { wqeh->u0.s.vlan = B_TRUE; wqeh->u0.s.vlan_tag = vlan_tag; } wqeh->u0.s.complete = B_TRUE; wqeh->u0.s.event = B_TRUE; wqeh->u0.s.crc = B_TRUE; wqeh->u0.s.total_length = pkt_len; num_wqes = wqed->frag_cnt + 1; /* h/w expects even no. of WQEs */ if (num_wqes & 0x1) { bzero(&wqed->frag[num_wqes], sizeof (struct oce_nic_frag_wqe)); num_wqes++; } wqed->wqe_cnt = (uint16_t)num_wqes; wqeh->u0.s.num_wqe = num_wqes; DW_SWAP(u32ptr(&wqed->frag[0]), (wqed->wqe_cnt * NIC_WQE_SIZE)); mutex_enter(&wq->tx_lock); if (num_wqes > wq->wq_free) { atomic_inc_32(&wq->tx_deferd); mutex_exit(&wq->tx_lock); goto wqe_fail; } atomic_add_32(&wq->wq_free, -num_wqes); /* fill the wq for adapter */ oce_fill_ring_descs(wq, wqed); /* Set the mp pointer in the wqe descriptor */ if (use_copy == B_FALSE) { wqed->mp = mp; } /* Add the packet desc to list to be retrieved during cmpl */ OCE_LIST_INSERT_TAIL(&wq->wqe_desc_list, wqed); (void) ddi_dma_sync(wq->ring->dbuf->dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); /* ring tx doorbell */ reg_value = (num_wqes << 16) | wq->wq_id; /* Ring the door bell */ OCE_DB_WRITE32(dev, PD_TXULP_DB, reg_value); mutex_exit(&wq->tx_lock); if (oce_fm_check_acc_handle(dev, dev->db_handle) != DDI_FM_OK) { ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); } /* free mp if copied or packet chain collapsed */ if (use_copy == B_TRUE) { freemsg(mp); } return (NULL); wqe_fail: if (tagged) { oce_insert_vtag(mp, vlan_tag); } oce_free_wqed(wq, wqed); return (mp); } /* oce_send_packet */ /* * function to free the WQE descriptor * * wq - pointer to WQ * wqed - Pointer to WQE descriptor * * return none */ #pragma inline(oce_free_wqed) static void oce_free_wqed(struct oce_wq *wq, oce_wqe_desc_t *wqed) { int i = 0; if (wqed == NULL) { return; } for (i = 0; i < wqed->nhdl; i++) { if (wqed->hdesc[i].type == COPY_WQE) { oce_wqb_free(wq, wqed->hdesc[i].hdl); } else if (wqed->hdesc[i].type == MAPPED_WQE) { oce_wqmd_free(wq, wqed->hdesc[i].hdl); } } if (wqed->mp) freemsg(wqed->mp); kmem_cache_free(wq->wqed_cache, wqed); } /* oce_free_wqed */ /* * function to start the WQ * * wq - pointer to WQ * * return DDI_SUCCESS */ int oce_start_wq(struct oce_wq *wq) { _NOTE(ARGUNUSED(wq)); return (DDI_SUCCESS); } /* oce_start_wq */ /* * function to stop the WQ * * wq - pointer to WQ * * return none */ void oce_clean_wq(struct oce_wq *wq) { oce_wqe_desc_t *wqed; int ti; /* Wait for already posted Tx to complete */ for (ti = 0; ti < DEFAULT_DRAIN_TIME; ti++) { (void) oce_process_tx_compl(wq, B_FALSE); OCE_MSDELAY(1); } /* Free the remaining descriptors */ while ((wqed = OCE_LIST_REM_HEAD(&wq->wqe_desc_list)) != NULL) { atomic_add_32(&wq->wq_free, wqed->wqe_cnt); oce_free_wqed(wq, wqed); } oce_drain_eq(wq->cq->eq); } /* oce_stop_wq */ /* * function to set the tx mapping handle fma attr * * fm_caps - capability flags * * return none */ void oce_set_tx_map_dma_fma_flags(int fm_caps) { if (fm_caps == DDI_FM_NOT_CAPABLE) { return; } if (DDI_FM_DMA_ERR_CAP(fm_caps)) { tx_map_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; } else { tx_map_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; } } /* oce_set_tx_map_dma_fma_flags */