15578Smx205022 /* 25578Smx205022 * CDDL HEADER START 35578Smx205022 * 45578Smx205022 * The contents of this file are subject to the terms of the 55578Smx205022 * Common Development and Distribution License (the "License"). 65578Smx205022 * You may not use this file except in compliance with the License. 75578Smx205022 * 85578Smx205022 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95578Smx205022 * or http://www.opensolaris.org/os/licensing. 105578Smx205022 * See the License for the specific language governing permissions 115578Smx205022 * and limitations under the License. 125578Smx205022 * 135578Smx205022 * When distributing Covered Code, include this CDDL HEADER in each 145578Smx205022 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155578Smx205022 * If applicable, add the following below this CDDL HEADER, with the 165578Smx205022 * fields enclosed by brackets "[]" replaced with your own identifying 175578Smx205022 * information: Portions Copyright [yyyy] [name of copyright owner] 185578Smx205022 * 195578Smx205022 * CDDL HEADER END 205578Smx205022 */ 215578Smx205022 225574Smx205022 /* 238530SZhen.W@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 245574Smx205022 * Use is subject to license terms. 255574Smx205022 */ 265574Smx205022 275574Smx205022 #include "nge.h" 285574Smx205022 295574Smx205022 #define TXD_OWN 0x80000000 305574Smx205022 #define TXD_ERR 0x40000000 315574Smx205022 #define TXD_END 0x20000000 325574Smx205022 #define TXD_BCNT_MSK 0x00003FFF 335574Smx205022 345574Smx205022 355574Smx205022 #undef NGE_DBG 365574Smx205022 #define NGE_DBG NGE_DBG_SEND 375574Smx205022 385574Smx205022 #define NGE_TXSWD_RECYCLE(sd) {\ 395574Smx205022 (sd)->mp = NULL; \ 405574Smx205022 (sd)->frags = 0; \ 415574Smx205022 (sd)->mp_hndl.head = NULL; \ 425574Smx205022 (sd)->mp_hndl.tail = NULL; \ 435574Smx205022 (sd)->flags = HOST_OWN; \ 445574Smx205022 } 455574Smx205022 465574Smx205022 475574Smx205022 static size_t nge_tx_dmah_pop(nge_dmah_list_t *, nge_dmah_list_t *, size_t); 485574Smx205022 static void nge_tx_dmah_push(nge_dmah_list_t *, nge_dmah_list_t *); 495574Smx205022 505574Smx205022 515574Smx205022 void nge_tx_recycle_all(nge_t *ngep); 525574Smx205022 #pragma no_inline(nge_tx_recycle_all) 535574Smx205022 545574Smx205022 void 555574Smx205022 nge_tx_recycle_all(nge_t *ngep) 565574Smx205022 { 575574Smx205022 send_ring_t *srp; 585574Smx205022 sw_tx_sbd_t *ssbdp; 595574Smx205022 nge_dmah_node_t *dmah; 605574Smx205022 uint32_t slot; 615574Smx205022 uint32_t nslots; 625574Smx205022 635574Smx205022 srp = ngep->send; 645574Smx205022 nslots = srp->desc.nslots; 655574Smx205022 665574Smx205022 for (slot = 0; slot < nslots; ++slot) { 675574Smx205022 685574Smx205022 ssbdp = srp->sw_sbds + slot; 695574Smx205022 705574Smx205022 DMA_ZERO(ssbdp->desc); 715574Smx205022 725574Smx205022 if (ssbdp->mp != NULL) { 735574Smx205022 745574Smx205022 for (dmah = ssbdp->mp_hndl.head; dmah != NULL; 755574Smx205022 dmah = dmah->next) 765574Smx205022 (void) ddi_dma_unbind_handle(dmah->hndl); 775574Smx205022 785574Smx205022 freemsg(ssbdp->mp); 795574Smx205022 } 805574Smx205022 815574Smx205022 NGE_TXSWD_RECYCLE(ssbdp); 825574Smx205022 } 835574Smx205022 } 845574Smx205022 855574Smx205022 static size_t 865574Smx205022 nge_tx_dmah_pop(nge_dmah_list_t *src, nge_dmah_list_t *dst, size_t num) 875574Smx205022 { 885574Smx205022 nge_dmah_node_t *node; 895574Smx205022 905574Smx205022 for (node = src->head; node != NULL && --num != 0; node = node->next) 915574Smx205022 ; 925574Smx205022 935574Smx205022 if (num == 0) { 945574Smx205022 955574Smx205022 dst->head = src->head; 965574Smx205022 dst->tail = node; 975574Smx205022 985574Smx205022 if ((src->head = node->next) == NULL) 995574Smx205022 src->tail = NULL; 1005574Smx205022 1015574Smx205022 node->next = NULL; 1025574Smx205022 } 1035574Smx205022 1045574Smx205022 return (num); 1055574Smx205022 } 1065574Smx205022 1075574Smx205022 static void 1085574Smx205022 nge_tx_dmah_push(nge_dmah_list_t *src, nge_dmah_list_t *dst) 1095574Smx205022 { 1105574Smx205022 if (dst->tail != NULL) 1115574Smx205022 dst->tail->next = src->head; 1125574Smx205022 else 1135574Smx205022 dst->head = src->head; 1145574Smx205022 1155574Smx205022 dst->tail = src->tail; 1165574Smx205022 } 1175574Smx205022 1185574Smx205022 static void 1197781SMin.Xu@Sun.COM nge_tx_desc_sync(nge_t *ngep, uint32_t start_index, uint32_t bds, uint_t type) 1205574Smx205022 { 1215574Smx205022 send_ring_t *srp = ngep->send; 1225574Smx205022 const size_t txd_size = ngep->desc_attr.txd_size; 1235574Smx205022 const uint64_t end = srp->desc.nslots * txd_size; 1247781SMin.Xu@Sun.COM uint64_t start; 1257781SMin.Xu@Sun.COM uint64_t num; 1265574Smx205022 1277781SMin.Xu@Sun.COM start = start_index * txd_size; 1287781SMin.Xu@Sun.COM num = bds * txd_size; 1295574Smx205022 1305574Smx205022 if (start + num <= end) 1315574Smx205022 (void) ddi_dma_sync(srp->desc.dma_hdl, start, num, type); 1325574Smx205022 else { 1335574Smx205022 1345574Smx205022 (void) ddi_dma_sync(srp->desc.dma_hdl, start, 0, type); 1355574Smx205022 (void) ddi_dma_sync(srp->desc.dma_hdl, 0, start + num - end, 1365574Smx205022 type); 1375574Smx205022 } 1385574Smx205022 } 1395574Smx205022 1405574Smx205022 /* 1415574Smx205022 * Reclaim the resource after tx's completion 1425574Smx205022 */ 1435574Smx205022 void 1445574Smx205022 nge_tx_recycle(nge_t *ngep, boolean_t is_intr) 1455574Smx205022 { 1465574Smx205022 int resched; 1475574Smx205022 uint32_t stflg; 1485574Smx205022 size_t len; 1497781SMin.Xu@Sun.COM uint32_t free; 1507781SMin.Xu@Sun.COM uint32_t slot; 1517781SMin.Xu@Sun.COM uint32_t used; 1527781SMin.Xu@Sun.COM uint32_t next; 1537781SMin.Xu@Sun.COM uint32_t nslots; 1545574Smx205022 mblk_t *mp; 1555574Smx205022 sw_tx_sbd_t *ssbdp; 1565574Smx205022 void *hw_sbd_p; 1575574Smx205022 send_ring_t *srp; 1585574Smx205022 nge_dmah_node_t *dme; 1595574Smx205022 nge_dmah_list_t dmah; 1605574Smx205022 1615574Smx205022 srp = ngep->send; 1625574Smx205022 1635574Smx205022 if (is_intr) { 1645574Smx205022 if (mutex_tryenter(srp->tc_lock) == 0) 1655574Smx205022 return; 1665574Smx205022 } else 1675574Smx205022 mutex_enter(srp->tc_lock); 1685574Smx205022 mutex_enter(srp->tx_lock); 1695574Smx205022 1705574Smx205022 next = srp->tx_next; 1715574Smx205022 used = srp->tx_flow; 1725574Smx205022 free = srp->tx_free; 1735574Smx205022 1745574Smx205022 mutex_exit(srp->tx_lock); 1755574Smx205022 1765574Smx205022 slot = srp->tc_next; 1775574Smx205022 nslots = srp->desc.nslots; 1785574Smx205022 1795574Smx205022 used = nslots - free - used; 1805574Smx205022 1815574Smx205022 ASSERT(slot == NEXT_INDEX(next, free, nslots)); 1825574Smx205022 1835574Smx205022 if (used > srp->tx_hwmark) 1845574Smx205022 used = srp->tx_hwmark; 1855574Smx205022 1865574Smx205022 nge_tx_desc_sync(ngep, slot, used, DDI_DMA_SYNC_FORKERNEL); 1875574Smx205022 1885574Smx205022 /* 1895574Smx205022 * Look through the send ring by bd's status part 1905574Smx205022 * to find all the bds which has been transmitted sucessfully 1915574Smx205022 * then reclaim all resouces associated with these bds 1925574Smx205022 */ 1935574Smx205022 1945574Smx205022 mp = NULL; 1955574Smx205022 dmah.head = NULL; 1965574Smx205022 dmah.tail = NULL; 1975574Smx205022 1985574Smx205022 for (free = 0; used-- != 0; slot = NEXT(slot, nslots), ++free) { 1995574Smx205022 2005574Smx205022 ssbdp = &srp->sw_sbds[slot]; 2015574Smx205022 hw_sbd_p = DMA_VPTR(ssbdp->desc); 2025574Smx205022 2038468SZhen.W@Sun.COM if (ssbdp->flags == HOST_OWN) 2045574Smx205022 break; 2058468SZhen.W@Sun.COM stflg = ngep->desc_attr.txd_check(hw_sbd_p, &len); 2068468SZhen.W@Sun.COM if ((stflg & TXD_OWN) != 0) 2078468SZhen.W@Sun.COM break; 2088530SZhen.W@Sun.COM DMA_ZERO(ssbdp->desc); 2095574Smx205022 if (ssbdp->mp != NULL) { 2105574Smx205022 ssbdp->mp->b_next = mp; 2115574Smx205022 mp = ssbdp->mp; 2125574Smx205022 2135574Smx205022 if (ssbdp->mp_hndl.head != NULL) 2145574Smx205022 nge_tx_dmah_push(&ssbdp->mp_hndl, &dmah); 2155574Smx205022 } 2165574Smx205022 2175574Smx205022 NGE_TXSWD_RECYCLE(ssbdp); 2185574Smx205022 } 2195574Smx205022 2205574Smx205022 /* 2215574Smx205022 * We're about to release one or more places :-) 2225574Smx205022 * These ASSERTions check that our invariants still hold: 2235574Smx205022 * there must always be at least one free place 2245574Smx205022 * at this point, there must be at least one place NOT free 2255574Smx205022 * we're not about to free more places than were claimed! 2265574Smx205022 */ 2275574Smx205022 2285574Smx205022 mutex_enter(srp->tx_lock); 2295574Smx205022 2305574Smx205022 srp->tx_free += free; 2315574Smx205022 ngep->watchdog = (srp->desc.nslots - srp->tx_free != 0); 2325574Smx205022 2335574Smx205022 srp->tc_next = slot; 2345574Smx205022 2355574Smx205022 ASSERT(srp->tx_free <= nslots); 2365574Smx205022 ASSERT(srp->tc_next == NEXT_INDEX(srp->tx_next, srp->tx_free, nslots)); 2375574Smx205022 2385574Smx205022 resched = (ngep->resched_needed != 0 && srp->tx_hwmark <= srp->tx_free); 2395574Smx205022 2405574Smx205022 mutex_exit(srp->tx_lock); 2415574Smx205022 mutex_exit(srp->tc_lock); 2425574Smx205022 2435574Smx205022 /* unbind/free mblks */ 2445574Smx205022 2455574Smx205022 for (dme = dmah.head; dme != NULL; dme = dme->next) 2465574Smx205022 (void) ddi_dma_unbind_handle(dme->hndl); 247*8848SZhen.W@Sun.COM if (dmah.head != NULL) { 248*8848SZhen.W@Sun.COM mutex_enter(&srp->dmah_lock); 249*8848SZhen.W@Sun.COM nge_tx_dmah_push(&dmah, &srp->dmah_free); 250*8848SZhen.W@Sun.COM mutex_exit(&srp->dmah_lock); 251*8848SZhen.W@Sun.COM } 2525574Smx205022 freemsgchain(mp); 2535574Smx205022 2545574Smx205022 /* 2555574Smx205022 * up to this place, we maybe have reclaim some resouce 2565574Smx205022 * if there is a requirement to report to gld, report this. 2575574Smx205022 */ 2585574Smx205022 2595574Smx205022 if (resched) 2605574Smx205022 (void) ddi_intr_trigger_softint(ngep->resched_hdl, NULL); 2615574Smx205022 } 2625574Smx205022 2637781SMin.Xu@Sun.COM static uint32_t 2647781SMin.Xu@Sun.COM nge_tx_alloc(nge_t *ngep, uint32_t num) 2655574Smx205022 { 2667781SMin.Xu@Sun.COM uint32_t start; 2675574Smx205022 send_ring_t *srp; 2685574Smx205022 2697781SMin.Xu@Sun.COM start = (uint32_t)-1; 2705574Smx205022 srp = ngep->send; 2715574Smx205022 2725574Smx205022 mutex_enter(srp->tx_lock); 2735574Smx205022 2745574Smx205022 if (srp->tx_free < srp->tx_lwmark) { 2755574Smx205022 2765574Smx205022 mutex_exit(srp->tx_lock); 2775574Smx205022 nge_tx_recycle(ngep, B_FALSE); 2785574Smx205022 mutex_enter(srp->tx_lock); 2795574Smx205022 } 2805574Smx205022 2815574Smx205022 if (srp->tx_free >= num) { 2825574Smx205022 2835574Smx205022 start = srp->tx_next; 2845574Smx205022 2855574Smx205022 srp->tx_next = NEXT_INDEX(start, num, srp->desc.nslots); 2865574Smx205022 srp->tx_free -= num; 2875574Smx205022 srp->tx_flow += num; 2885574Smx205022 } 2895574Smx205022 2905574Smx205022 mutex_exit(srp->tx_lock); 2915574Smx205022 return (start); 2925574Smx205022 } 2935574Smx205022 2945574Smx205022 static void 2957781SMin.Xu@Sun.COM nge_tx_start(nge_t *ngep, uint32_t slotnum) 2965574Smx205022 { 2975574Smx205022 nge_mode_cntl mode_cntl; 2985574Smx205022 send_ring_t *srp; 2995574Smx205022 3005574Smx205022 srp = ngep->send; 3015574Smx205022 3025574Smx205022 /* 3035574Smx205022 * Because there can be multiple concurrent threads in 3045574Smx205022 * transit through this code, we only want to notify the 3055574Smx205022 * hardware once the last one is departing ... 3065574Smx205022 */ 3075574Smx205022 3085574Smx205022 mutex_enter(srp->tx_lock); 3095574Smx205022 3105574Smx205022 srp->tx_flow -= slotnum; 3115574Smx205022 if (srp->tx_flow == 0) { 3125574Smx205022 3135574Smx205022 /* 3145574Smx205022 * Bump the watchdog counter, thus guaranteeing that it's 3155574Smx205022 * nonzero (watchdog activated). Note that non-synchonised 3165574Smx205022 * access here means we may race with the reclaim() code 3175574Smx205022 * above, but the outcome will be harmless. At worst, the 3185574Smx205022 * counter may not get reset on a partial reclaim; but the 3195574Smx205022 * large trigger threshold makes false positives unlikely 3205574Smx205022 */ 3215574Smx205022 ngep->watchdog ++; 3225574Smx205022 3235574Smx205022 mode_cntl.mode_val = nge_reg_get32(ngep, NGE_MODE_CNTL); 3245574Smx205022 mode_cntl.mode_bits.txdm = NGE_SET; 3255574Smx205022 mode_cntl.mode_bits.tx_rcom_en = NGE_SET; 3265574Smx205022 nge_reg_put32(ngep, NGE_MODE_CNTL, mode_cntl.mode_val); 3275574Smx205022 } 3285574Smx205022 mutex_exit(srp->tx_lock); 3295574Smx205022 } 3305574Smx205022 3315574Smx205022 static enum send_status 3325574Smx205022 nge_send_copy(nge_t *ngep, mblk_t *mp, send_ring_t *srp); 3335574Smx205022 #pragma inline(nge_send_copy) 3345574Smx205022 3355574Smx205022 static enum send_status 3365574Smx205022 nge_send_copy(nge_t *ngep, mblk_t *mp, send_ring_t *srp) 3375574Smx205022 { 3385574Smx205022 size_t totlen; 3395574Smx205022 size_t mblen; 3405574Smx205022 uint32_t flags; 3417781SMin.Xu@Sun.COM uint32_t bds; 3427781SMin.Xu@Sun.COM uint32_t start_index; 3435574Smx205022 char *txb; 3445574Smx205022 mblk_t *bp; 3455574Smx205022 void *hw_sbd_p; 3465574Smx205022 sw_tx_sbd_t *ssbdp; 3475574Smx205022 3485574Smx205022 hcksum_retrieve(mp, NULL, NULL, NULL, NULL, 3495574Smx205022 NULL, NULL, &flags); 3505574Smx205022 bds = 0x1; 3515574Smx205022 3527781SMin.Xu@Sun.COM if ((uint32_t)-1 == (start_index = nge_tx_alloc(ngep, bds))) 3535574Smx205022 return (SEND_COPY_FAIL); 3545574Smx205022 3555574Smx205022 ASSERT(start_index < srp->desc.nslots); 3565574Smx205022 3575574Smx205022 /* 3585574Smx205022 * up to this point, there's nothing that can fail, 3595574Smx205022 * so we can go straight to claiming our 3605574Smx205022 * already-reserved place son the train. 3615574Smx205022 * 3625574Smx205022 * This is the point of no return! 3635574Smx205022 */ 3645574Smx205022 3655574Smx205022 bp = mp; 3665574Smx205022 totlen = 0; 3675574Smx205022 ssbdp = &srp->sw_sbds[start_index]; 3685574Smx205022 ASSERT(ssbdp->flags == HOST_OWN); 3695574Smx205022 3705574Smx205022 txb = DMA_VPTR(ssbdp->pbuf); 3715574Smx205022 totlen = 0; 3725574Smx205022 for (; bp != NULL; bp = bp->b_cont) { 3735574Smx205022 if ((mblen = MBLKL(bp)) == 0) 3745574Smx205022 continue; 3755574Smx205022 if ((totlen += mblen) <= ngep->max_sdu) { 3765574Smx205022 bcopy(bp->b_rptr, txb, mblen); 3775574Smx205022 txb += mblen; 3785574Smx205022 } 3795574Smx205022 } 3805574Smx205022 3815574Smx205022 DMA_SYNC(ssbdp->pbuf, DDI_DMA_SYNC_FORDEV); 3825574Smx205022 3835574Smx205022 /* Fill & sync hw desc */ 3845574Smx205022 3855574Smx205022 hw_sbd_p = DMA_VPTR(ssbdp->desc); 3865574Smx205022 3875574Smx205022 ngep->desc_attr.txd_fill(hw_sbd_p, &ssbdp->pbuf.cookie, totlen, 3885574Smx205022 flags, B_TRUE); 3895574Smx205022 nge_tx_desc_sync(ngep, start_index, bds, DDI_DMA_SYNC_FORDEV); 3905574Smx205022 3915574Smx205022 ssbdp->flags = CONTROLER_OWN; 3925574Smx205022 3935574Smx205022 nge_tx_start(ngep, bds); 3945574Smx205022 3955574Smx205022 /* 3965574Smx205022 * The return status indicates that the message can be freed 3975574Smx205022 * right away, as we've already copied the contents ... 3985574Smx205022 */ 3995574Smx205022 4005574Smx205022 freemsg(mp); 4015574Smx205022 return (SEND_COPY_SUCESS); 4025574Smx205022 } 4035574Smx205022 4045574Smx205022 /* 4055574Smx205022 * static enum send_status 4065574Smx205022 * nge_send_mapped(nge_t *ngep, mblk_t *mp, size_t fragno); 4075574Smx205022 * #pragma inline(nge_send_mapped) 4085574Smx205022 */ 4095574Smx205022 4105574Smx205022 static enum send_status 4115574Smx205022 nge_send_mapped(nge_t *ngep, mblk_t *mp, size_t fragno) 4125574Smx205022 { 4135574Smx205022 int err; 4145574Smx205022 boolean_t end; 4155574Smx205022 uint32_t i; 4165574Smx205022 uint32_t j; 4175574Smx205022 uint32_t ncookies; 4185574Smx205022 uint32_t slot; 4195574Smx205022 uint32_t nslots; 4205574Smx205022 uint32_t mblen; 4215574Smx205022 uint32_t flags; 4227781SMin.Xu@Sun.COM uint32_t start_index; 4237781SMin.Xu@Sun.COM uint32_t end_index; 4245574Smx205022 mblk_t *bp; 4255574Smx205022 void *hw_sbd_p; 4265574Smx205022 send_ring_t *srp; 4275574Smx205022 nge_dmah_node_t *dmah; 4285574Smx205022 nge_dmah_node_t *dmer; 4295574Smx205022 nge_dmah_list_t dmah_list; 4305574Smx205022 ddi_dma_cookie_t cookie[NGE_MAX_COOKIES * NGE_MAP_FRAGS]; 4315574Smx205022 4325574Smx205022 srp = ngep->send; 4335574Smx205022 nslots = srp->desc.nslots; 4345574Smx205022 4355574Smx205022 mutex_enter(&srp->dmah_lock); 4365574Smx205022 err = nge_tx_dmah_pop(&srp->dmah_free, &dmah_list, fragno); 4375574Smx205022 mutex_exit(&srp->dmah_lock); 4385574Smx205022 4395574Smx205022 if (err != 0) { 4405574Smx205022 4415574Smx205022 return (SEND_MAP_FAIL); 4425574Smx205022 } 4435574Smx205022 4445574Smx205022 /* 4455574Smx205022 * Pre-scan the message chain, noting the total number of bytes, 4465574Smx205022 * the number of fragments by pre-doing dma addr bind 4475574Smx205022 * if the fragment is larger than NGE_COPY_SIZE. 4485574Smx205022 * This way has the following advantages: 4495574Smx205022 * 1. Acquire the detailed information of resouce 4505574Smx205022 * need to send the message 4515574Smx205022 * 4525574Smx205022 * 2. If can not pre-apply enough resouce, fails at once 4535574Smx205022 * and the driver will chose copy way to send out the 4545574Smx205022 * message 4555574Smx205022 */ 4565574Smx205022 4575574Smx205022 slot = 0; 4585574Smx205022 dmah = dmah_list.head; 4595574Smx205022 4605574Smx205022 hcksum_retrieve(mp, NULL, NULL, NULL, NULL, NULL, NULL, &flags); 4615574Smx205022 4625574Smx205022 for (bp = mp; bp != NULL; bp = bp->b_cont) { 4635574Smx205022 4645574Smx205022 mblen = MBLKL(bp); 4655574Smx205022 if (mblen == 0) 4665574Smx205022 continue; 4675574Smx205022 4685574Smx205022 err = ddi_dma_addr_bind_handle(dmah->hndl, 4695574Smx205022 NULL, (caddr_t)bp->b_rptr, mblen, 4705574Smx205022 DDI_DMA_STREAMING | DDI_DMA_WRITE, 4715574Smx205022 DDI_DMA_DONTWAIT, NULL, cookie + slot, &ncookies); 4725574Smx205022 4735574Smx205022 /* 4745574Smx205022 * If there can not map successfully, it is uncessary 4755574Smx205022 * sending the message by map way. Sending the message 4765574Smx205022 * by copy way. 4775574Smx205022 * 4785574Smx205022 * By referring to intel's suggestion, it is better 4795574Smx205022 * the number of cookies should be less than 4. 4805574Smx205022 */ 4815574Smx205022 if (err != DDI_DMA_MAPPED || ncookies > NGE_MAX_COOKIES) { 4825574Smx205022 NGE_DEBUG(("err(%x) map tx bulk fails" 4835574Smx205022 " cookie(%x), ncookies(%x)", 4845574Smx205022 err, cookie[slot].dmac_laddress, ncookies)); 4855574Smx205022 goto map_fail; 4865574Smx205022 } 4875574Smx205022 4885574Smx205022 /* 4895574Smx205022 * Check How many bds a cookie will consume 4905574Smx205022 */ 4915574Smx205022 for (end_index = slot + ncookies; 4925574Smx205022 ++slot != end_index; 4935574Smx205022 ddi_dma_nextcookie(dmah->hndl, cookie + slot)) 4945574Smx205022 ; 4955574Smx205022 4965574Smx205022 dmah = dmah->next; 4975574Smx205022 } 4985574Smx205022 4995574Smx205022 /* 5005574Smx205022 * Now allocate tx descriptors and fill them 5015574Smx205022 * IMPORTANT: 5025574Smx205022 * Up to the point where it claims a place, It is impossibel 5035574Smx205022 * to fail. 5045574Smx205022 * 5055574Smx205022 * In this version, there's no setup to be done here, and there's 5065574Smx205022 * nothing that can fail, so we can go straight to claiming our 5075574Smx205022 * already-reserved places on the train. 5085574Smx205022 * 5095574Smx205022 * This is the point of no return! 5105574Smx205022 */ 5115574Smx205022 5125574Smx205022 5137781SMin.Xu@Sun.COM if ((uint32_t)-1 == (start_index = nge_tx_alloc(ngep, slot))) 5145574Smx205022 goto map_fail; 5155574Smx205022 5165574Smx205022 ASSERT(start_index < nslots); 5175574Smx205022 5185574Smx205022 /* fill&sync hw desc, going in reverse order */ 5195574Smx205022 5205574Smx205022 end = B_TRUE; 5215574Smx205022 end_index = NEXT_INDEX(start_index, slot - 1, nslots); 5225574Smx205022 5235574Smx205022 for (i = slot - 1, j = end_index; start_index - j != 0; 5245574Smx205022 j = PREV(j, nslots), --i) { 5255574Smx205022 5265574Smx205022 hw_sbd_p = DMA_VPTR(srp->sw_sbds[j].desc); 5275574Smx205022 ngep->desc_attr.txd_fill(hw_sbd_p, cookie + i, 5285574Smx205022 cookie[i].dmac_size, 0, end); 5295574Smx205022 5305574Smx205022 end = B_FALSE; 5315574Smx205022 } 5325574Smx205022 5335574Smx205022 hw_sbd_p = DMA_VPTR(srp->sw_sbds[j].desc); 5345574Smx205022 ngep->desc_attr.txd_fill(hw_sbd_p, cookie + i, cookie[i].dmac_size, 5355574Smx205022 flags, end); 5365574Smx205022 5375574Smx205022 nge_tx_desc_sync(ngep, start_index, slot, DDI_DMA_SYNC_FORDEV); 5385574Smx205022 5395574Smx205022 /* fill sw desc */ 5405574Smx205022 5417781SMin.Xu@Sun.COM for (j = start_index; end_index - j != 0; j = NEXT(j, nslots)) { 5425574Smx205022 5435574Smx205022 srp->sw_sbds[j].flags = CONTROLER_OWN; 5445574Smx205022 } 5455574Smx205022 5465574Smx205022 srp->sw_sbds[j].mp = mp; 5475574Smx205022 srp->sw_sbds[j].mp_hndl = dmah_list; 5487781SMin.Xu@Sun.COM srp->sw_sbds[j].frags = (uint32_t)fragno; 5495574Smx205022 srp->sw_sbds[j].flags = CONTROLER_OWN; 5505574Smx205022 5515574Smx205022 nge_tx_start(ngep, slot); 5525574Smx205022 5535574Smx205022 /* 5545574Smx205022 * The return status indicates that the message can not be freed 5555574Smx205022 * right away, until we can make assure the message has been sent 5565574Smx205022 * out sucessfully. 5575574Smx205022 */ 5585574Smx205022 return (SEND_MAP_SUCCESS); 5595574Smx205022 5605574Smx205022 map_fail: 5615574Smx205022 for (dmer = dmah_list.head; dmah - dmer != 0; dmer = dmer->next) 5625574Smx205022 (void) ddi_dma_unbind_handle(dmer->hndl); 5635574Smx205022 5645574Smx205022 mutex_enter(&srp->dmah_lock); 5655574Smx205022 nge_tx_dmah_push(&dmah_list, &srp->dmah_free); 5665574Smx205022 mutex_exit(&srp->dmah_lock); 5675574Smx205022 5685574Smx205022 return (SEND_MAP_FAIL); 5695574Smx205022 } 5705574Smx205022 5715574Smx205022 static boolean_t 5725574Smx205022 nge_send(nge_t *ngep, mblk_t *mp) 5735574Smx205022 { 5745574Smx205022 mblk_t *bp; 5755574Smx205022 send_ring_t *srp; 5765574Smx205022 enum send_status status; 5775574Smx205022 uint32_t mblen = 0; 5785574Smx205022 uint32_t frags = 0; 5795574Smx205022 nge_statistics_t *nstp = &ngep->statistics; 5805574Smx205022 nge_sw_statistics_t *sw_stp = &nstp->sw_statistics; 5815574Smx205022 5825574Smx205022 ASSERT(mp != NULL); 5835574Smx205022 ASSERT(ngep->nge_mac_state == NGE_MAC_STARTED); 5845574Smx205022 5855574Smx205022 srp = ngep->send; 5865574Smx205022 /* 5875574Smx205022 * 1.Check the number of the fragments of the messages 5885574Smx205022 * If the total number is larger than 3, 5895574Smx205022 * Chose copy way 5905574Smx205022 * 5915574Smx205022 * 2. Check the length of the message whether is larger than 5925574Smx205022 * NGE_TX_COPY_SIZE, if so, choose the map way. 5935574Smx205022 */ 5945574Smx205022 for (frags = 0, bp = mp; bp != NULL; bp = bp->b_cont) { 5955574Smx205022 if (MBLKL(bp) == 0) 5965574Smx205022 continue; 5975574Smx205022 frags++; 5985574Smx205022 mblen += MBLKL(bp); 5995574Smx205022 } 6005574Smx205022 if (mblen > (ngep->max_sdu) || mblen == 0) { 6015574Smx205022 freemsg(mp); 6025574Smx205022 return (B_TRUE); 6035574Smx205022 } 6045574Smx205022 if ((mblen > ngep->param_txbcopy_threshold) && 605*8848SZhen.W@Sun.COM (frags <= NGE_MAP_FRAGS) && 6065574Smx205022 (srp->tx_free > frags * NGE_MAX_COOKIES)) { 6075574Smx205022 status = nge_send_mapped(ngep, mp, frags); 6085574Smx205022 if (status == SEND_MAP_FAIL) 6095574Smx205022 status = nge_send_copy(ngep, mp, srp); 6105574Smx205022 } else { 6115574Smx205022 status = nge_send_copy(ngep, mp, srp); 6125574Smx205022 } 6135574Smx205022 if (status == SEND_COPY_FAIL) { 6145574Smx205022 nge_tx_recycle(ngep, B_FALSE); 6155574Smx205022 status = nge_send_copy(ngep, mp, srp); 6165574Smx205022 if (status == SEND_COPY_FAIL) { 6175574Smx205022 ngep->resched_needed = 1; 6185574Smx205022 NGE_DEBUG(("nge_send: send fail!")); 6195574Smx205022 return (B_FALSE); 6205574Smx205022 } 6215574Smx205022 } 6225574Smx205022 /* Update the software statistics */ 6235574Smx205022 sw_stp->obytes += mblen + ETHERFCSL; 6245574Smx205022 sw_stp->xmit_count ++; 6255574Smx205022 6265574Smx205022 return (B_TRUE); 6275574Smx205022 } 6285574Smx205022 6295574Smx205022 /* 6305574Smx205022 * nge_m_tx : Send a chain of packets. 6315574Smx205022 */ 6325574Smx205022 mblk_t * 6335574Smx205022 nge_m_tx(void *arg, mblk_t *mp) 6345574Smx205022 { 6355574Smx205022 nge_t *ngep = arg; 6365574Smx205022 mblk_t *next; 6375574Smx205022 6385574Smx205022 rw_enter(ngep->rwlock, RW_READER); 6395574Smx205022 ASSERT(mp != NULL); 6405574Smx205022 if (ngep->nge_chip_state != NGE_CHIP_RUNNING) { 6415574Smx205022 freemsgchain(mp); 6425574Smx205022 mp = NULL; 6435574Smx205022 } 6445574Smx205022 while (mp != NULL) { 6455574Smx205022 next = mp->b_next; 6465574Smx205022 mp->b_next = NULL; 6475574Smx205022 6485574Smx205022 if (!nge_send(ngep, mp)) { 6495574Smx205022 mp->b_next = next; 6505574Smx205022 break; 6515574Smx205022 } 6525574Smx205022 6535574Smx205022 mp = next; 6545574Smx205022 } 6555574Smx205022 rw_exit(ngep->rwlock); 6565574Smx205022 6575574Smx205022 return (mp); 6585574Smx205022 } 6595574Smx205022 6605574Smx205022 /* ARGSUSED */ 6615574Smx205022 uint_t 6625574Smx205022 nge_reschedule(caddr_t args1, caddr_t args2) 6635574Smx205022 { 6645574Smx205022 nge_t *ngep; 6655574Smx205022 uint_t rslt; 6665574Smx205022 6675574Smx205022 ngep = (nge_t *)args1; 6685574Smx205022 rslt = DDI_INTR_UNCLAIMED; 6695574Smx205022 6705574Smx205022 /* 6715574Smx205022 * when softintr is trigged, checking whether this 6725574Smx205022 * is caused by our expected interrupt 6735574Smx205022 */ 6745574Smx205022 if (ngep->nge_mac_state == NGE_MAC_STARTED && 6755574Smx205022 ngep->resched_needed == 1) { 6765574Smx205022 ngep->resched_needed = 0; 6775574Smx205022 ++ngep->statistics.sw_statistics.tx_resched; 6785574Smx205022 mac_tx_update(ngep->mh); 6795574Smx205022 rslt = DDI_INTR_CLAIMED; 6805574Smx205022 } 6815574Smx205022 return (rslt); 6825574Smx205022 } 6835574Smx205022 6845574Smx205022 uint32_t 6855574Smx205022 nge_hot_txd_check(const void *hwd, size_t *len) 6865574Smx205022 { 6875574Smx205022 uint32_t err_flag; 6885574Smx205022 const hot_tx_bd * htbdp; 6895574Smx205022 6905574Smx205022 htbdp = hwd; 6915574Smx205022 err_flag = htbdp->control_status.cntl_val & ~TXD_BCNT_MSK; 6925574Smx205022 6935574Smx205022 *len = htbdp->control_status.status_bits.bcnt; 6945574Smx205022 return (err_flag); 6955574Smx205022 } 6965574Smx205022 6975574Smx205022 uint32_t 6985574Smx205022 nge_sum_txd_check(const void *hwd, size_t *len) 6995574Smx205022 { 7005574Smx205022 uint32_t err_flag; 7015574Smx205022 const sum_tx_bd * htbdp; 7025574Smx205022 7035574Smx205022 htbdp = hwd; 7045574Smx205022 err_flag = htbdp->control_status.cntl_val & ~TXD_BCNT_MSK; 7055574Smx205022 7065574Smx205022 *len = htbdp->control_status.status_bits.bcnt; 7075574Smx205022 return (err_flag); 7085574Smx205022 } 7095574Smx205022 7105574Smx205022 7115574Smx205022 /* 7125574Smx205022 * Filling the contents of Tx's data descriptor 7135574Smx205022 * before transmitting. 7145574Smx205022 */ 7155574Smx205022 7165574Smx205022 void 7175574Smx205022 nge_hot_txd_fill(void *hwdesc, const ddi_dma_cookie_t *cookie, 7185574Smx205022 size_t length, uint32_t sum_flag, boolean_t end) 7195574Smx205022 { 7205574Smx205022 hot_tx_bd * hw_sbd_p = hwdesc; 7215574Smx205022 7225574Smx205022 hw_sbd_p->host_buf_addr_hi = cookie->dmac_laddress >> 32; 7235574Smx205022 hw_sbd_p->host_buf_addr_lo = cookie->dmac_laddress; 7245574Smx205022 7255574Smx205022 /* 7265574Smx205022 * Setting the length of the packet 7275574Smx205022 * Note: the length filled in the part should be 7285574Smx205022 * the original length subtract 1; 7295574Smx205022 */ 7305574Smx205022 7315574Smx205022 hw_sbd_p->control_status.control_sum_bits.bcnt = length - 1; 7325574Smx205022 7335574Smx205022 /* setting ip checksum */ 7345574Smx205022 if (sum_flag & HCK_IPV4_HDRCKSUM) 7355574Smx205022 hw_sbd_p->control_status.control_sum_bits.ip_hsum 7365574Smx205022 = NGE_SET; 7375574Smx205022 /* setting tcp checksum */ 7385574Smx205022 if (sum_flag & HCK_FULLCKSUM) 7395574Smx205022 hw_sbd_p->control_status.control_sum_bits.tcp_hsum 7405574Smx205022 = NGE_SET; 7415574Smx205022 /* 7425574Smx205022 * indicating the end of BDs 7435574Smx205022 */ 7445574Smx205022 if (end) 7455574Smx205022 hw_sbd_p->control_status.control_sum_bits.end = NGE_SET; 7465574Smx205022 7475574Smx205022 membar_producer(); 7485574Smx205022 7495574Smx205022 /* pass desc to HW */ 7505574Smx205022 hw_sbd_p->control_status.control_sum_bits.own = NGE_SET; 7515574Smx205022 } 7525574Smx205022 7535574Smx205022 void 7545574Smx205022 nge_sum_txd_fill(void *hwdesc, const ddi_dma_cookie_t *cookie, 7555574Smx205022 size_t length, uint32_t sum_flag, boolean_t end) 7565574Smx205022 { 7575574Smx205022 sum_tx_bd * hw_sbd_p = hwdesc; 7585574Smx205022 7595574Smx205022 hw_sbd_p->host_buf_addr = cookie->dmac_address; 7605574Smx205022 7615574Smx205022 /* 7625574Smx205022 * Setting the length of the packet 7635574Smx205022 * Note: the length filled in the part should be 7645574Smx205022 * the original length subtract 1; 7655574Smx205022 */ 7665574Smx205022 7675574Smx205022 hw_sbd_p->control_status.control_sum_bits.bcnt = length - 1; 7685574Smx205022 7695574Smx205022 /* setting ip checksum */ 7705574Smx205022 if (sum_flag & HCK_IPV4_HDRCKSUM) 7715574Smx205022 hw_sbd_p->control_status.control_sum_bits.ip_hsum 7725574Smx205022 = NGE_SET; 7735574Smx205022 /* setting tcp checksum */ 7745574Smx205022 if (sum_flag & HCK_FULLCKSUM) 7755574Smx205022 hw_sbd_p->control_status.control_sum_bits.tcp_hsum 7765574Smx205022 = NGE_SET; 7775574Smx205022 /* 7785574Smx205022 * indicating the end of BDs 7795574Smx205022 */ 7805574Smx205022 if (end) 7815574Smx205022 hw_sbd_p->control_status.control_sum_bits.end = NGE_SET; 7825574Smx205022 7835574Smx205022 membar_producer(); 7845574Smx205022 7855574Smx205022 /* pass desc to HW */ 7865574Smx205022 hw_sbd_p->control_status.control_sum_bits.own = NGE_SET; 7875574Smx205022 } 788