16f45ec7bSml29623 /* 26f45ec7bSml29623 * CDDL HEADER START 36f45ec7bSml29623 * 46f45ec7bSml29623 * The contents of this file are subject to the terms of the 56f45ec7bSml29623 * Common Development and Distribution License (the "License"). 66f45ec7bSml29623 * You may not use this file except in compliance with the License. 76f45ec7bSml29623 * 86f45ec7bSml29623 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96f45ec7bSml29623 * or http://www.opensolaris.org/os/licensing. 106f45ec7bSml29623 * See the License for the specific language governing permissions 116f45ec7bSml29623 * and limitations under the License. 126f45ec7bSml29623 * 136f45ec7bSml29623 * When distributing Covered Code, include this CDDL HEADER in each 146f45ec7bSml29623 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156f45ec7bSml29623 * If applicable, add the following below this CDDL HEADER, with the 166f45ec7bSml29623 * fields enclosed by brackets "[]" replaced with your own identifying 176f45ec7bSml29623 * information: Portions Copyright [yyyy] [name of copyright owner] 186f45ec7bSml29623 * 196f45ec7bSml29623 * CDDL HEADER END 206f45ec7bSml29623 */ 216f45ec7bSml29623 /* 220dc2366fSVenugopal Iyer * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 236f45ec7bSml29623 * Use is subject to license terms. 246f45ec7bSml29623 */ 256f45ec7bSml29623 26ae6aa22aSVenugopal Iyer #include <sys/mac_provider.h> 276f45ec7bSml29623 #include <sys/nxge/nxge_impl.h> 28678453a8Sspeer #include <sys/nxge/nxge_hio.h> 29678453a8Sspeer #include <npi_tx_wr64.h> 306f45ec7bSml29623 3130ac2e7bSml29623 /* Software LSO required header files */ 3230ac2e7bSml29623 #include <netinet/tcp.h> 3330ac2e7bSml29623 #include <inet/ip_impl.h> 3430ac2e7bSml29623 #include <inet/tcp.h> 3530ac2e7bSml29623 36ae6aa22aSVenugopal Iyer extern uint64_t mac_pkt_hash(uint_t, mblk_t *mp, uint8_t policy, 37ae6aa22aSVenugopal Iyer boolean_t is_outbound); 38ae6aa22aSVenugopal Iyer 3930ac2e7bSml29623 static mblk_t *nxge_lso_eliminate(mblk_t *); 4030ac2e7bSml29623 static mblk_t *nxge_do_softlso(mblk_t *mp, uint32_t mss); 4130ac2e7bSml29623 static void nxge_lso_info_get(mblk_t *, uint32_t *, uint32_t *); 4230ac2e7bSml29623 static void nxge_hcksum_retrieve(mblk_t *, 4330ac2e7bSml29623 uint32_t *, uint32_t *, uint32_t *, 4430ac2e7bSml29623 uint32_t *, uint32_t *); 4530ac2e7bSml29623 static uint32_t nxge_csgen(uint16_t *, int); 4630ac2e7bSml29623 476f45ec7bSml29623 extern uint32_t nxge_reclaim_pending; 486f45ec7bSml29623 extern uint32_t nxge_bcopy_thresh; 496f45ec7bSml29623 extern uint32_t nxge_dvma_thresh; 506f45ec7bSml29623 extern uint32_t nxge_dma_stream_thresh; 516f45ec7bSml29623 extern uint32_t nxge_tx_minfree; 526f45ec7bSml29623 extern uint32_t nxge_tx_intr_thres; 536f45ec7bSml29623 extern uint32_t nxge_tx_max_gathers; 546f45ec7bSml29623 extern uint32_t nxge_tx_tiny_pack; 556f45ec7bSml29623 extern uint32_t nxge_tx_use_bcopy; 561f8914d5Sml29623 extern nxge_tx_mode_t nxge_tx_scheme; 5730ac2e7bSml29623 uint32_t nxge_lso_kick_cnt = 2; 586f45ec7bSml29623 596f45ec7bSml29623 60da14cebeSEric Cheng void 61da14cebeSEric Cheng nxge_tx_ring_task(void *arg) 62da14cebeSEric Cheng { 63da14cebeSEric Cheng p_tx_ring_t ring = (p_tx_ring_t)arg; 64da14cebeSEric Cheng 650dc2366fSVenugopal Iyer ASSERT(ring->tx_ring_handle != NULL); 660dc2366fSVenugopal Iyer 67da14cebeSEric Cheng MUTEX_ENTER(&ring->lock); 68da14cebeSEric Cheng (void) nxge_txdma_reclaim(ring->nxgep, ring, 0); 69da14cebeSEric Cheng MUTEX_EXIT(&ring->lock); 70da14cebeSEric Cheng 7163f531d1SSriharsha Basavapatna if (!ring->tx_ring_offline) { 72da14cebeSEric Cheng mac_tx_ring_update(ring->nxgep->mach, ring->tx_ring_handle); 73da14cebeSEric Cheng } 74da14cebeSEric Cheng } 75da14cebeSEric Cheng 76da14cebeSEric Cheng static void 77da14cebeSEric Cheng nxge_tx_ring_dispatch(p_tx_ring_t ring) 78da14cebeSEric Cheng { 79da14cebeSEric Cheng /* 80da14cebeSEric Cheng * Kick the ring task to reclaim some buffers. 81da14cebeSEric Cheng */ 82da14cebeSEric Cheng (void) ddi_taskq_dispatch(ring->taskq, 83da14cebeSEric Cheng nxge_tx_ring_task, (void *)ring, DDI_SLEEP); 84da14cebeSEric Cheng } 85da14cebeSEric Cheng 86da14cebeSEric Cheng mblk_t * 87da14cebeSEric Cheng nxge_tx_ring_send(void *arg, mblk_t *mp) 88da14cebeSEric Cheng { 89da14cebeSEric Cheng p_nxge_ring_handle_t nrhp = (p_nxge_ring_handle_t)arg; 90da14cebeSEric Cheng p_nxge_t nxgep; 91da14cebeSEric Cheng p_tx_ring_t tx_ring_p; 92da14cebeSEric Cheng int status, channel; 93da14cebeSEric Cheng 94da14cebeSEric Cheng ASSERT(nrhp != NULL); 95da14cebeSEric Cheng nxgep = nrhp->nxgep; 96da14cebeSEric Cheng channel = nxgep->pt_config.hw_config.tdc.start + nrhp->index; 97da14cebeSEric Cheng tx_ring_p = nxgep->tx_rings->rings[channel]; 98da14cebeSEric Cheng 9948056c53SMichael Speer /* 10048056c53SMichael Speer * We may be in a transition from offlined DMA to onlined 10148056c53SMichael Speer * DMA. 10248056c53SMichael Speer */ 10348056c53SMichael Speer if (tx_ring_p == NULL) { 10448056c53SMichael Speer ASSERT(tx_ring_p != NULL); 10548056c53SMichael Speer freemsg(mp); 10648056c53SMichael Speer return ((mblk_t *)NULL); 10748056c53SMichael Speer } 10848056c53SMichael Speer 10948056c53SMichael Speer /* 11048056c53SMichael Speer * Valid DMA? 11148056c53SMichael Speer */ 112da14cebeSEric Cheng ASSERT(nxgep == tx_ring_p->nxgep); 113da14cebeSEric Cheng 11448056c53SMichael Speer /* 11548056c53SMichael Speer * Make sure DMA is not offlined. 11648056c53SMichael Speer */ 11748056c53SMichael Speer if (isLDOMservice(nxgep) && tx_ring_p->tx_ring_offline) { 118da14cebeSEric Cheng ASSERT(!tx_ring_p->tx_ring_offline); 11948056c53SMichael Speer freemsg(mp); 12048056c53SMichael Speer return ((mblk_t *)NULL); 121da14cebeSEric Cheng } 122da14cebeSEric Cheng 12348056c53SMichael Speer /* 12448056c53SMichael Speer * Transmit the packet. 12548056c53SMichael Speer */ 126da14cebeSEric Cheng status = nxge_start(nxgep, tx_ring_p, mp); 127da14cebeSEric Cheng if (status) { 128da14cebeSEric Cheng nxge_tx_ring_dispatch(tx_ring_p); 129da14cebeSEric Cheng return (mp); 130da14cebeSEric Cheng } 131da14cebeSEric Cheng 132da14cebeSEric Cheng return ((mblk_t *)NULL); 133da14cebeSEric Cheng } 134da14cebeSEric Cheng 1356f45ec7bSml29623 int 1366f45ec7bSml29623 nxge_start(p_nxge_t nxgep, p_tx_ring_t tx_ring_p, p_mblk_t mp) 1376f45ec7bSml29623 { 13831f519f9SMichael Speer int dma_status, status = 0; 1396f45ec7bSml29623 p_tx_desc_t tx_desc_ring_vp; 1406f45ec7bSml29623 npi_handle_t npi_desc_handle; 1416f45ec7bSml29623 nxge_os_dma_handle_t tx_desc_dma_handle; 1426f45ec7bSml29623 p_tx_desc_t tx_desc_p; 1436f45ec7bSml29623 p_tx_msg_t tx_msg_ring; 1446f45ec7bSml29623 p_tx_msg_t tx_msg_p; 1456f45ec7bSml29623 tx_desc_t tx_desc, *tmp_desc_p; 1466f45ec7bSml29623 tx_desc_t sop_tx_desc, *sop_tx_desc_p; 1476f45ec7bSml29623 p_tx_pkt_header_t hdrp; 148ef755e7aStc99174@train tx_pkt_hdr_all_t tmp_hdrp; 1496f45ec7bSml29623 p_tx_pkt_hdr_all_t pkthdrp; 1506f45ec7bSml29623 uint8_t npads = 0; 1516f45ec7bSml29623 uint64_t dma_ioaddr; 1526f45ec7bSml29623 uint32_t dma_flags; 1536f45ec7bSml29623 int last_bidx; 1546f45ec7bSml29623 uint8_t *b_rptr; 1556f45ec7bSml29623 caddr_t kaddr; 1566f45ec7bSml29623 uint32_t nmblks; 1576f45ec7bSml29623 uint32_t ngathers; 1586f45ec7bSml29623 uint32_t clen; 1596f45ec7bSml29623 int len; 1606f45ec7bSml29623 uint32_t pkt_len, pack_len, min_len; 1616f45ec7bSml29623 uint32_t bcopy_thresh; 1626f45ec7bSml29623 int i, cur_index, sop_index; 1636f45ec7bSml29623 uint16_t tail_index; 1646f45ec7bSml29623 boolean_t tail_wrap = B_FALSE; 1656f45ec7bSml29623 nxge_dma_common_t desc_area; 1666f45ec7bSml29623 nxge_os_dma_handle_t dma_handle; 1676f45ec7bSml29623 ddi_dma_cookie_t dma_cookie; 1686f45ec7bSml29623 npi_handle_t npi_handle; 1696f45ec7bSml29623 p_mblk_t nmp; 1706f45ec7bSml29623 p_mblk_t t_mp; 1716f45ec7bSml29623 uint32_t ncookies; 1726f45ec7bSml29623 boolean_t good_packet; 1736f45ec7bSml29623 boolean_t mark_mode = B_FALSE; 1746f45ec7bSml29623 p_nxge_stats_t statsp; 1756f45ec7bSml29623 p_nxge_tx_ring_stats_t tdc_stats; 1766f45ec7bSml29623 t_uscalar_t start_offset = 0; 1776f45ec7bSml29623 t_uscalar_t stuff_offset = 0; 1786f45ec7bSml29623 t_uscalar_t end_offset = 0; 1796f45ec7bSml29623 t_uscalar_t value = 0; 1806f45ec7bSml29623 t_uscalar_t cksum_flags = 0; 1816f45ec7bSml29623 boolean_t cksum_on = B_FALSE; 1826f45ec7bSml29623 uint32_t boff = 0; 183b4d05839Sml29623 uint64_t tot_xfer_len = 0; 1846f45ec7bSml29623 boolean_t header_set = B_FALSE; 1856f45ec7bSml29623 #ifdef NXGE_DEBUG 1866f45ec7bSml29623 p_tx_desc_t tx_desc_ring_pp; 1876f45ec7bSml29623 p_tx_desc_t tx_desc_pp; 1886f45ec7bSml29623 tx_desc_t *save_desc_p; 1896f45ec7bSml29623 int dump_len; 1906f45ec7bSml29623 int sad_len; 1916f45ec7bSml29623 uint64_t sad; 1926f45ec7bSml29623 int xfer_len; 1936f45ec7bSml29623 uint32_t msgsize; 1946f45ec7bSml29623 #endif 19530ac2e7bSml29623 p_mblk_t mp_chain = NULL; 19630ac2e7bSml29623 boolean_t is_lso = B_FALSE; 19730ac2e7bSml29623 boolean_t lso_again; 19830ac2e7bSml29623 int cur_index_lso; 19930ac2e7bSml29623 p_mblk_t nmp_lso_save; 20030ac2e7bSml29623 uint32_t lso_ngathers; 20130ac2e7bSml29623 boolean_t lso_tail_wrap = B_FALSE; 2026f45ec7bSml29623 2036f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 2046f45ec7bSml29623 "==> nxge_start: tx dma channel %d", tx_ring_p->tdc)); 2056f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 2066f45ec7bSml29623 "==> nxge_start: Starting tdc %d desc pending %d", 2076f45ec7bSml29623 tx_ring_p->tdc, tx_ring_p->descs_pending)); 2086f45ec7bSml29623 2096f45ec7bSml29623 statsp = nxgep->statsp; 2106f45ec7bSml29623 211678453a8Sspeer if (!isLDOMguest(nxgep)) { 212321febdeSsbehera switch (nxgep->mac.portmode) { 213321febdeSsbehera default: 214678453a8Sspeer if (nxgep->statsp->port_stats.lb_mode == 215678453a8Sspeer nxge_lb_normal) { 2166f45ec7bSml29623 if (!statsp->mac_stats.link_up) { 2176f45ec7bSml29623 freemsg(mp); 218321febdeSsbehera NXGE_DEBUG_MSG((nxgep, TX_CTL, 219321febdeSsbehera "==> nxge_start: " 220321febdeSsbehera "link not up")); 2216f45ec7bSml29623 goto nxge_start_fail1; 2226f45ec7bSml29623 } 2236f45ec7bSml29623 } 224321febdeSsbehera break; 225321febdeSsbehera case PORT_10G_FIBER: 226321febdeSsbehera /* 227321febdeSsbehera * For the following modes, check the link status 228321febdeSsbehera * before sending the packet out: 229eb1db165Stc99174@train * nxge_lb_normal, 230eb1db165Stc99174@train * nxge_lb_ext10g, 231eb1db165Stc99174@train * nxge_lb_ext1000, 232eb1db165Stc99174@train * nxge_lb_ext100, 233eb1db165Stc99174@train * nxge_lb_ext10. 234321febdeSsbehera */ 235678453a8Sspeer if (nxgep->statsp->port_stats.lb_mode < 236eb1db165Stc99174@train nxge_lb_phy10g) { 237321febdeSsbehera if (!statsp->mac_stats.link_up) { 238321febdeSsbehera freemsg(mp); 239321febdeSsbehera NXGE_DEBUG_MSG((nxgep, TX_CTL, 240321febdeSsbehera "==> nxge_start: " 241321febdeSsbehera "link not up")); 242321febdeSsbehera goto nxge_start_fail1; 243321febdeSsbehera } 244321febdeSsbehera } 245321febdeSsbehera break; 246321febdeSsbehera } 247678453a8Sspeer } 248678453a8Sspeer 249678453a8Sspeer if ((!(nxgep->drv_state & STATE_HW_INITIALIZED)) || 250678453a8Sspeer (nxgep->nxge_mac_state != NXGE_MAC_STARTED)) { 251678453a8Sspeer NXGE_DEBUG_MSG((nxgep, TX_CTL, 252678453a8Sspeer "==> nxge_start: hardware not initialized or stopped")); 253678453a8Sspeer freemsg(mp); 254678453a8Sspeer goto nxge_start_fail1; 255678453a8Sspeer } 2566f45ec7bSml29623 2573d16f8e7Sml29623 if (nxgep->soft_lso_enable) { 25830ac2e7bSml29623 mp_chain = nxge_lso_eliminate(mp); 25930ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 26030ac2e7bSml29623 "==> nxge_start(0): LSO mp $%p mp_chain $%p", 26130ac2e7bSml29623 mp, mp_chain)); 26230ac2e7bSml29623 if (mp_chain == NULL) { 26330ac2e7bSml29623 NXGE_ERROR_MSG((nxgep, TX_CTL, 26430ac2e7bSml29623 "==> nxge_send(0): NULL mp_chain $%p != mp $%p", 26530ac2e7bSml29623 mp_chain, mp)); 26630ac2e7bSml29623 goto nxge_start_fail1; 26730ac2e7bSml29623 } 26830ac2e7bSml29623 if (mp_chain != mp) { 26930ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 27030ac2e7bSml29623 "==> nxge_send(1): IS LSO mp_chain $%p != mp $%p", 27130ac2e7bSml29623 mp_chain, mp)); 27230ac2e7bSml29623 is_lso = B_TRUE; 27330ac2e7bSml29623 mp = mp_chain; 27430ac2e7bSml29623 mp_chain = mp_chain->b_next; 27530ac2e7bSml29623 mp->b_next = NULL; 27630ac2e7bSml29623 } 27730ac2e7bSml29623 } 27830ac2e7bSml29623 2790dc2366fSVenugopal Iyer mac_hcksum_get(mp, &start_offset, &stuff_offset, &end_offset, 2800dc2366fSVenugopal Iyer &value, &cksum_flags); 2816f45ec7bSml29623 if (!NXGE_IS_VLAN_PACKET(mp->b_rptr)) { 2826f45ec7bSml29623 start_offset += sizeof (ether_header_t); 2836f45ec7bSml29623 stuff_offset += sizeof (ether_header_t); 2846f45ec7bSml29623 } else { 2856f45ec7bSml29623 start_offset += sizeof (struct ether_vlan_header); 2866f45ec7bSml29623 stuff_offset += sizeof (struct ether_vlan_header); 2876f45ec7bSml29623 } 2886f45ec7bSml29623 2896f45ec7bSml29623 if (cksum_flags & HCK_PARTIALCKSUM) { 2906f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 29130ac2e7bSml29623 "==> nxge_start: mp $%p len %d " 29230ac2e7bSml29623 "cksum_flags 0x%x (partial checksum) ", 29330ac2e7bSml29623 mp, MBLKL(mp), cksum_flags)); 2946f45ec7bSml29623 cksum_on = B_TRUE; 2956f45ec7bSml29623 } 2966f45ec7bSml29623 297b4d05839Sml29623 pkthdrp = (p_tx_pkt_hdr_all_t)&tmp_hdrp; 298b4d05839Sml29623 pkthdrp->reserved = 0; 299ef755e7aStc99174@train tmp_hdrp.pkthdr.value = 0; 300b4d05839Sml29623 nxge_fill_tx_hdr(mp, B_FALSE, cksum_on, 301b4d05839Sml29623 0, 0, pkthdrp, 302b4d05839Sml29623 start_offset, stuff_offset); 303b4d05839Sml29623 30430ac2e7bSml29623 lso_again = B_FALSE; 30530ac2e7bSml29623 lso_ngathers = 0; 30630ac2e7bSml29623 30730ac2e7bSml29623 MUTEX_ENTER(&tx_ring_p->lock); 30822c0d73aSspeer 30922c0d73aSspeer if (isLDOMservice(nxgep)) { 3106895688eSspeer tx_ring_p->tx_ring_busy = B_TRUE; 31122c0d73aSspeer if (tx_ring_p->tx_ring_offline) { 31222c0d73aSspeer freemsg(mp); 3136895688eSspeer tx_ring_p->tx_ring_busy = B_FALSE; 3146895688eSspeer (void) atomic_swap_32(&tx_ring_p->tx_ring_offline, 3156895688eSspeer NXGE_TX_RING_OFFLINED); 31622c0d73aSspeer MUTEX_EXIT(&tx_ring_p->lock); 31722c0d73aSspeer return (status); 31822c0d73aSspeer } 31922c0d73aSspeer } 32022c0d73aSspeer 32130ac2e7bSml29623 cur_index_lso = tx_ring_p->wr_index; 32230ac2e7bSml29623 lso_tail_wrap = tx_ring_p->wr_index_wrap; 32330ac2e7bSml29623 start_again: 32430ac2e7bSml29623 ngathers = 0; 32530ac2e7bSml29623 sop_index = tx_ring_p->wr_index; 3266f45ec7bSml29623 #ifdef NXGE_DEBUG 3276f45ec7bSml29623 if (tx_ring_p->descs_pending) { 3286f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: " 3296f45ec7bSml29623 "desc pending %d ", tx_ring_p->descs_pending)); 3306f45ec7bSml29623 } 3316f45ec7bSml29623 3326f45ec7bSml29623 dump_len = (int)(MBLKL(mp)); 3336f45ec7bSml29623 dump_len = (dump_len > 128) ? 128: dump_len; 3346f45ec7bSml29623 3356f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 3366f45ec7bSml29623 "==> nxge_start: tdc %d: dumping ...: b_rptr $%p " 3376f45ec7bSml29623 "(Before header reserve: ORIGINAL LEN %d)", 3386f45ec7bSml29623 tx_ring_p->tdc, 3396f45ec7bSml29623 mp->b_rptr, 3406f45ec7bSml29623 dump_len)); 3416f45ec7bSml29623 3426f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: dump packets " 3436f45ec7bSml29623 "(IP ORIGINAL b_rptr $%p): %s", mp->b_rptr, 3446f45ec7bSml29623 nxge_dump_packet((char *)mp->b_rptr, dump_len))); 3456f45ec7bSml29623 #endif 3466f45ec7bSml29623 3476f45ec7bSml29623 tdc_stats = tx_ring_p->tdc_stats; 3486f45ec7bSml29623 mark_mode = (tx_ring_p->descs_pending && 349257bdc55SMichael Speer (((int)tx_ring_p->tx_ring_size - (int)tx_ring_p->descs_pending) < 350257bdc55SMichael Speer (int)nxge_tx_minfree)); 3516f45ec7bSml29623 3526f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 3536f45ec7bSml29623 "TX Descriptor ring is channel %d mark mode %d", 3546f45ec7bSml29623 tx_ring_p->tdc, mark_mode)); 3556f45ec7bSml29623 3567127d9f6Sml29623 if ((tx_ring_p->descs_pending + lso_ngathers) >= nxge_reclaim_pending) { 3577127d9f6Sml29623 if (!nxge_txdma_reclaim(nxgep, tx_ring_p, 3587127d9f6Sml29623 (nxge_tx_minfree + lso_ngathers))) { 3596f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 3606f45ec7bSml29623 "TX Descriptor ring is full: channel %d", 3616f45ec7bSml29623 tx_ring_p->tdc)); 36230ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 36330ac2e7bSml29623 "TX Descriptor ring is full: channel %d", 36430ac2e7bSml29623 tx_ring_p->tdc)); 36530ac2e7bSml29623 if (is_lso) { 3667127d9f6Sml29623 /* 3677127d9f6Sml29623 * free the current mp and mp_chain if not FULL. 3687127d9f6Sml29623 */ 36930ac2e7bSml29623 tdc_stats->tx_no_desc++; 37030ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 37130ac2e7bSml29623 "LSO packet: TX Descriptor ring is full: " 37230ac2e7bSml29623 "channel %d", 37330ac2e7bSml29623 tx_ring_p->tdc)); 37430ac2e7bSml29623 goto nxge_start_fail_lso; 37530ac2e7bSml29623 } else { 376*75d94465SJosef 'Jeff' Sipek (void) atomic_cas_32( 377*75d94465SJosef 'Jeff' Sipek (uint32_t *)&tx_ring_p->queueing, 0, 1); 3786f45ec7bSml29623 tdc_stats->tx_no_desc++; 3796895688eSspeer 3806895688eSspeer if (isLDOMservice(nxgep)) { 3816895688eSspeer tx_ring_p->tx_ring_busy = B_FALSE; 3826895688eSspeer if (tx_ring_p->tx_ring_offline) { 38322c0d73aSspeer (void) atomic_swap_32( 38422c0d73aSspeer &tx_ring_p->tx_ring_offline, 38522c0d73aSspeer NXGE_TX_RING_OFFLINED); 38622c0d73aSspeer } 3876895688eSspeer } 38822c0d73aSspeer 3896f45ec7bSml29623 MUTEX_EXIT(&tx_ring_p->lock); 3906f45ec7bSml29623 status = 1; 3916f45ec7bSml29623 goto nxge_start_fail1; 3926f45ec7bSml29623 } 39330ac2e7bSml29623 } 3947127d9f6Sml29623 } 3956f45ec7bSml29623 3966f45ec7bSml29623 nmp = mp; 3976f45ec7bSml29623 i = sop_index = tx_ring_p->wr_index; 3986f45ec7bSml29623 nmblks = 0; 3996f45ec7bSml29623 ngathers = 0; 4006f45ec7bSml29623 pkt_len = 0; 4016f45ec7bSml29623 pack_len = 0; 4026f45ec7bSml29623 clen = 0; 4036f45ec7bSml29623 last_bidx = -1; 4046f45ec7bSml29623 good_packet = B_TRUE; 4056f45ec7bSml29623 4066f45ec7bSml29623 desc_area = tx_ring_p->tdc_desc; 4076f45ec7bSml29623 npi_handle = desc_area.npi_handle; 4086f45ec7bSml29623 npi_desc_handle.regh = (nxge_os_acc_handle_t) 4096f45ec7bSml29623 DMA_COMMON_ACC_HANDLE(desc_area); 4106f45ec7bSml29623 tx_desc_ring_vp = (p_tx_desc_t)DMA_COMMON_VPTR(desc_area); 4116f45ec7bSml29623 tx_desc_dma_handle = (nxge_os_dma_handle_t) 4126f45ec7bSml29623 DMA_COMMON_HANDLE(desc_area); 4136f45ec7bSml29623 tx_msg_ring = tx_ring_p->tx_msg_ring; 4146f45ec7bSml29623 4156f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: wr_index %d i %d", 4166f45ec7bSml29623 sop_index, i)); 4176f45ec7bSml29623 4186f45ec7bSml29623 #ifdef NXGE_DEBUG 4196f45ec7bSml29623 msgsize = msgdsize(nmp); 4206f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 4216f45ec7bSml29623 "==> nxge_start(1): wr_index %d i %d msgdsize %d", 4226f45ec7bSml29623 sop_index, i, msgsize)); 4236f45ec7bSml29623 #endif 4246f45ec7bSml29623 /* 4256f45ec7bSml29623 * The first 16 bytes of the premapped buffer are reserved 4266f45ec7bSml29623 * for header. No padding will be used. 4276f45ec7bSml29623 */ 4286f45ec7bSml29623 pkt_len = pack_len = boff = TX_PKT_HEADER_SIZE; 4291f8914d5Sml29623 if (nxge_tx_use_bcopy && (nxgep->niu_type != N2_NIU)) { 4306f45ec7bSml29623 bcopy_thresh = (nxge_bcopy_thresh - TX_PKT_HEADER_SIZE); 4316f45ec7bSml29623 } else { 4326f45ec7bSml29623 bcopy_thresh = (TX_BCOPY_SIZE - TX_PKT_HEADER_SIZE); 4336f45ec7bSml29623 } 4346f45ec7bSml29623 while (nmp) { 4356f45ec7bSml29623 good_packet = B_TRUE; 4366f45ec7bSml29623 b_rptr = nmp->b_rptr; 4376f45ec7bSml29623 len = MBLKL(nmp); 4386f45ec7bSml29623 if (len <= 0) { 4396f45ec7bSml29623 nmp = nmp->b_cont; 4406f45ec7bSml29623 continue; 4416f45ec7bSml29623 } 4426f45ec7bSml29623 nmblks++; 4436f45ec7bSml29623 4446f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(1): nmblks %d " 4456f45ec7bSml29623 "len %d pkt_len %d pack_len %d", 4466f45ec7bSml29623 nmblks, len, pkt_len, pack_len)); 4476f45ec7bSml29623 /* 4486f45ec7bSml29623 * Hardware limits the transfer length to 4K for NIU and 4496f45ec7bSml29623 * 4076 (TX_MAX_TRANSFER_LENGTH) for Neptune. But we just 4506f45ec7bSml29623 * use TX_MAX_TRANSFER_LENGTH as the limit for both. 4516f45ec7bSml29623 * If len is longer than the limit, then we break nmp into 4526f45ec7bSml29623 * two chunks: Make the first chunk equal to the limit and 4536f45ec7bSml29623 * the second chunk for the remaining data. If the second 4546f45ec7bSml29623 * chunk is still larger than the limit, then it will be 4556f45ec7bSml29623 * broken into two in the next pass. 4566f45ec7bSml29623 */ 4576f45ec7bSml29623 if (len > TX_MAX_TRANSFER_LENGTH - TX_PKT_HEADER_SIZE) { 45853f3d8ecSyc148097 if ((t_mp = dupb(nmp)) != NULL) { 4596f45ec7bSml29623 nmp->b_wptr = nmp->b_rptr + 46053f3d8ecSyc148097 (TX_MAX_TRANSFER_LENGTH 46153f3d8ecSyc148097 - TX_PKT_HEADER_SIZE); 4626f45ec7bSml29623 t_mp->b_rptr = nmp->b_wptr; 4636f45ec7bSml29623 t_mp->b_cont = nmp->b_cont; 4646f45ec7bSml29623 nmp->b_cont = t_mp; 4656f45ec7bSml29623 len = MBLKL(nmp); 46653f3d8ecSyc148097 } else { 46730ac2e7bSml29623 if (is_lso) { 46830ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 46930ac2e7bSml29623 "LSO packet: dupb failed: " 47030ac2e7bSml29623 "channel %d", 47130ac2e7bSml29623 tx_ring_p->tdc)); 47230ac2e7bSml29623 mp = nmp; 47330ac2e7bSml29623 goto nxge_start_fail_lso; 47430ac2e7bSml29623 } else { 47553f3d8ecSyc148097 good_packet = B_FALSE; 47653f3d8ecSyc148097 goto nxge_start_fail2; 4776f45ec7bSml29623 } 47853f3d8ecSyc148097 } 47930ac2e7bSml29623 } 4806f45ec7bSml29623 tx_desc.value = 0; 4816f45ec7bSml29623 tx_desc_p = &tx_desc_ring_vp[i]; 4826f45ec7bSml29623 #ifdef NXGE_DEBUG 4836f45ec7bSml29623 tx_desc_pp = &tx_desc_ring_pp[i]; 4846f45ec7bSml29623 #endif 4856f45ec7bSml29623 tx_msg_p = &tx_msg_ring[i]; 486adfcba55Sjoycey #if defined(__i386) 487adfcba55Sjoycey npi_desc_handle.regp = (uint32_t)tx_desc_p; 488adfcba55Sjoycey #else 4896f45ec7bSml29623 npi_desc_handle.regp = (uint64_t)tx_desc_p; 490adfcba55Sjoycey #endif 4916f45ec7bSml29623 if (!header_set && 4926f45ec7bSml29623 ((!nxge_tx_use_bcopy && (len > TX_BCOPY_SIZE)) || 4936f45ec7bSml29623 (len >= bcopy_thresh))) { 4946f45ec7bSml29623 header_set = B_TRUE; 4956f45ec7bSml29623 bcopy_thresh += TX_PKT_HEADER_SIZE; 4966f45ec7bSml29623 boff = 0; 4976f45ec7bSml29623 pack_len = 0; 4986f45ec7bSml29623 kaddr = (caddr_t)DMA_COMMON_VPTR(tx_msg_p->buf_dma); 4996f45ec7bSml29623 hdrp = (p_tx_pkt_header_t)kaddr; 5006f45ec7bSml29623 clen = pkt_len; 5016f45ec7bSml29623 dma_handle = tx_msg_p->buf_dma_handle; 5026f45ec7bSml29623 dma_ioaddr = DMA_COMMON_IOADDR(tx_msg_p->buf_dma); 5036f45ec7bSml29623 (void) ddi_dma_sync(dma_handle, 5046f45ec7bSml29623 i * nxge_bcopy_thresh, nxge_bcopy_thresh, 5056f45ec7bSml29623 DDI_DMA_SYNC_FORDEV); 5066f45ec7bSml29623 5076f45ec7bSml29623 tx_msg_p->flags.dma_type = USE_BCOPY; 5086f45ec7bSml29623 goto nxge_start_control_header_only; 5096f45ec7bSml29623 } 5106f45ec7bSml29623 5116f45ec7bSml29623 pkt_len += len; 5126f45ec7bSml29623 pack_len += len; 5136f45ec7bSml29623 5146f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(3): " 5156f45ec7bSml29623 "desc entry %d " 5166f45ec7bSml29623 "DESC IOADDR $%p " 5176f45ec7bSml29623 "desc_vp $%p tx_desc_p $%p " 5186f45ec7bSml29623 "desc_pp $%p tx_desc_pp $%p " 5196f45ec7bSml29623 "len %d pkt_len %d pack_len %d", 5206f45ec7bSml29623 i, 5216f45ec7bSml29623 DMA_COMMON_IOADDR(desc_area), 5226f45ec7bSml29623 tx_desc_ring_vp, tx_desc_p, 5236f45ec7bSml29623 tx_desc_ring_pp, tx_desc_pp, 5246f45ec7bSml29623 len, pkt_len, pack_len)); 5256f45ec7bSml29623 5266f45ec7bSml29623 if (len < bcopy_thresh) { 5276f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(4): " 5286f45ec7bSml29623 "USE BCOPY: ")); 5296f45ec7bSml29623 if (nxge_tx_tiny_pack) { 5306f45ec7bSml29623 uint32_t blst = 5316f45ec7bSml29623 TXDMA_DESC_NEXT_INDEX(i, -1, 5326f45ec7bSml29623 tx_ring_p->tx_wrap_mask); 5336f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 5346f45ec7bSml29623 "==> nxge_start(5): pack")); 5356f45ec7bSml29623 if ((pack_len <= bcopy_thresh) && 5366f45ec7bSml29623 (last_bidx == blst)) { 5376f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 5386f45ec7bSml29623 "==> nxge_start: pack(6) " 5396f45ec7bSml29623 "(pkt_len %d pack_len %d)", 5406f45ec7bSml29623 pkt_len, pack_len)); 5416f45ec7bSml29623 i = blst; 5426f45ec7bSml29623 tx_desc_p = &tx_desc_ring_vp[i]; 5436f45ec7bSml29623 #ifdef NXGE_DEBUG 5446f45ec7bSml29623 tx_desc_pp = &tx_desc_ring_pp[i]; 5456f45ec7bSml29623 #endif 5466f45ec7bSml29623 tx_msg_p = &tx_msg_ring[i]; 5476f45ec7bSml29623 boff = pack_len - len; 5486f45ec7bSml29623 ngathers--; 5496f45ec7bSml29623 } else if (pack_len > bcopy_thresh && 5506f45ec7bSml29623 header_set) { 5516f45ec7bSml29623 pack_len = len; 5526f45ec7bSml29623 boff = 0; 5536f45ec7bSml29623 bcopy_thresh = nxge_bcopy_thresh; 5546f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 5556f45ec7bSml29623 "==> nxge_start(7): > max NEW " 5566f45ec7bSml29623 "bcopy thresh %d " 5576f45ec7bSml29623 "pkt_len %d pack_len %d(next)", 5586f45ec7bSml29623 bcopy_thresh, 5596f45ec7bSml29623 pkt_len, pack_len)); 5606f45ec7bSml29623 } 5616f45ec7bSml29623 last_bidx = i; 5626f45ec7bSml29623 } 5636f45ec7bSml29623 kaddr = (caddr_t)DMA_COMMON_VPTR(tx_msg_p->buf_dma); 5646f45ec7bSml29623 if ((boff == TX_PKT_HEADER_SIZE) && (nmblks == 1)) { 5656f45ec7bSml29623 hdrp = (p_tx_pkt_header_t)kaddr; 5666f45ec7bSml29623 header_set = B_TRUE; 5676f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 5686f45ec7bSml29623 "==> nxge_start(7_x2): " 5696f45ec7bSml29623 "pkt_len %d pack_len %d (new hdrp $%p)", 5706f45ec7bSml29623 pkt_len, pack_len, hdrp)); 5716f45ec7bSml29623 } 5726f45ec7bSml29623 tx_msg_p->flags.dma_type = USE_BCOPY; 5736f45ec7bSml29623 kaddr += boff; 5746f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(8): " 5756f45ec7bSml29623 "USE BCOPY: before bcopy " 5766f45ec7bSml29623 "DESC IOADDR $%p entry %d " 5776f45ec7bSml29623 "bcopy packets %d " 5786f45ec7bSml29623 "bcopy kaddr $%p " 5796f45ec7bSml29623 "bcopy ioaddr (SAD) $%p " 5806f45ec7bSml29623 "bcopy clen %d " 5816f45ec7bSml29623 "bcopy boff %d", 5826f45ec7bSml29623 DMA_COMMON_IOADDR(desc_area), i, 5836f45ec7bSml29623 tdc_stats->tx_hdr_pkts, 5846f45ec7bSml29623 kaddr, 5856f45ec7bSml29623 dma_ioaddr, 5866f45ec7bSml29623 clen, 5876f45ec7bSml29623 boff)); 5886f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: " 5896f45ec7bSml29623 "1USE BCOPY: ")); 5906f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: " 5916f45ec7bSml29623 "2USE BCOPY: ")); 5926f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: " 5936f45ec7bSml29623 "last USE BCOPY: copy from b_rptr $%p " 5946f45ec7bSml29623 "to KADDR $%p (len %d offset %d", 5956f45ec7bSml29623 b_rptr, kaddr, len, boff)); 5966f45ec7bSml29623 5976f45ec7bSml29623 bcopy(b_rptr, kaddr, len); 5986f45ec7bSml29623 5996f45ec7bSml29623 #ifdef NXGE_DEBUG 6006f45ec7bSml29623 dump_len = (len > 128) ? 128: len; 6016f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 6026f45ec7bSml29623 "==> nxge_start: dump packets " 6036f45ec7bSml29623 "(After BCOPY len %d)" 6046f45ec7bSml29623 "(b_rptr $%p): %s", len, nmp->b_rptr, 6056f45ec7bSml29623 nxge_dump_packet((char *)nmp->b_rptr, 6066f45ec7bSml29623 dump_len))); 6076f45ec7bSml29623 #endif 6086f45ec7bSml29623 6096f45ec7bSml29623 dma_handle = tx_msg_p->buf_dma_handle; 6106f45ec7bSml29623 dma_ioaddr = DMA_COMMON_IOADDR(tx_msg_p->buf_dma); 6116f45ec7bSml29623 (void) ddi_dma_sync(dma_handle, 6126f45ec7bSml29623 i * nxge_bcopy_thresh, nxge_bcopy_thresh, 6136f45ec7bSml29623 DDI_DMA_SYNC_FORDEV); 6146f45ec7bSml29623 clen = len + boff; 6156f45ec7bSml29623 tdc_stats->tx_hdr_pkts++; 6166f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(9): " 6176f45ec7bSml29623 "USE BCOPY: " 6186f45ec7bSml29623 "DESC IOADDR $%p entry %d " 6196f45ec7bSml29623 "bcopy packets %d " 6206f45ec7bSml29623 "bcopy kaddr $%p " 6216f45ec7bSml29623 "bcopy ioaddr (SAD) $%p " 6226f45ec7bSml29623 "bcopy clen %d " 6236f45ec7bSml29623 "bcopy boff %d", 6246f45ec7bSml29623 DMA_COMMON_IOADDR(desc_area), 6256f45ec7bSml29623 i, 6266f45ec7bSml29623 tdc_stats->tx_hdr_pkts, 6276f45ec7bSml29623 kaddr, 6286f45ec7bSml29623 dma_ioaddr, 6296f45ec7bSml29623 clen, 6306f45ec7bSml29623 boff)); 6316f45ec7bSml29623 } else { 6326f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(12): " 6336f45ec7bSml29623 "USE DVMA: len %d", len)); 6346f45ec7bSml29623 tx_msg_p->flags.dma_type = USE_DMA; 6356f45ec7bSml29623 dma_flags = DDI_DMA_WRITE; 6366f45ec7bSml29623 if (len < nxge_dma_stream_thresh) { 6376f45ec7bSml29623 dma_flags |= DDI_DMA_CONSISTENT; 6386f45ec7bSml29623 } else { 6396f45ec7bSml29623 dma_flags |= DDI_DMA_STREAMING; 6406f45ec7bSml29623 } 6416f45ec7bSml29623 6426f45ec7bSml29623 dma_handle = tx_msg_p->dma_handle; 64331f519f9SMichael Speer dma_status = ddi_dma_addr_bind_handle(dma_handle, NULL, 6446f45ec7bSml29623 (caddr_t)b_rptr, len, dma_flags, 6456f45ec7bSml29623 DDI_DMA_DONTWAIT, NULL, 6466f45ec7bSml29623 &dma_cookie, &ncookies); 64731f519f9SMichael Speer if (dma_status == DDI_DMA_MAPPED) { 6486f45ec7bSml29623 dma_ioaddr = dma_cookie.dmac_laddress; 6496f45ec7bSml29623 len = (int)dma_cookie.dmac_size; 6506f45ec7bSml29623 clen = (uint32_t)dma_cookie.dmac_size; 6516f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 6526f45ec7bSml29623 "==> nxge_start(12_1): " 6536f45ec7bSml29623 "USE DVMA: len %d clen %d " 6546f45ec7bSml29623 "ngathers %d", 6556f45ec7bSml29623 len, clen, 6566f45ec7bSml29623 ngathers)); 657adfcba55Sjoycey #if defined(__i386) 658adfcba55Sjoycey npi_desc_handle.regp = (uint32_t)tx_desc_p; 659adfcba55Sjoycey #else 6606f45ec7bSml29623 npi_desc_handle.regp = (uint64_t)tx_desc_p; 661adfcba55Sjoycey #endif 6626f45ec7bSml29623 while (ncookies > 1) { 6636f45ec7bSml29623 ngathers++; 6646f45ec7bSml29623 /* 6656f45ec7bSml29623 * this is the fix for multiple 66630ac2e7bSml29623 * cookies, which are basically 6676f45ec7bSml29623 * a descriptor entry, we don't set 6686f45ec7bSml29623 * SOP bit as well as related fields 6696f45ec7bSml29623 */ 6706f45ec7bSml29623 6716f45ec7bSml29623 (void) npi_txdma_desc_gather_set( 6726f45ec7bSml29623 npi_desc_handle, 6736f45ec7bSml29623 &tx_desc, 6746f45ec7bSml29623 (ngathers -1), 6756f45ec7bSml29623 mark_mode, 6766f45ec7bSml29623 ngathers, 6776f45ec7bSml29623 dma_ioaddr, 6786f45ec7bSml29623 clen); 6796f45ec7bSml29623 6806f45ec7bSml29623 tx_msg_p->tx_msg_size = clen; 6816f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 6826f45ec7bSml29623 "==> nxge_start: DMA " 6836f45ec7bSml29623 "ncookie %d " 6846f45ec7bSml29623 "ngathers %d " 6856f45ec7bSml29623 "dma_ioaddr $%p len %d" 6866f45ec7bSml29623 "desc $%p descp $%p (%d)", 6876f45ec7bSml29623 ncookies, 6886f45ec7bSml29623 ngathers, 6896f45ec7bSml29623 dma_ioaddr, clen, 6906f45ec7bSml29623 *tx_desc_p, tx_desc_p, i)); 6916f45ec7bSml29623 6926f45ec7bSml29623 ddi_dma_nextcookie(dma_handle, 6936f45ec7bSml29623 &dma_cookie); 6946f45ec7bSml29623 dma_ioaddr = 6956f45ec7bSml29623 dma_cookie.dmac_laddress; 6966f45ec7bSml29623 6976f45ec7bSml29623 len = (int)dma_cookie.dmac_size; 6986f45ec7bSml29623 clen = (uint32_t)dma_cookie.dmac_size; 6996f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 7006f45ec7bSml29623 "==> nxge_start(12_2): " 7016f45ec7bSml29623 "USE DVMA: len %d clen %d ", 7026f45ec7bSml29623 len, clen)); 7036f45ec7bSml29623 7046f45ec7bSml29623 i = TXDMA_DESC_NEXT_INDEX(i, 1, 7056f45ec7bSml29623 tx_ring_p->tx_wrap_mask); 7066f45ec7bSml29623 tx_desc_p = &tx_desc_ring_vp[i]; 7076f45ec7bSml29623 708adfcba55Sjoycey #if defined(__i386) 70952ccf843Smisaki npi_desc_handle.regp = 710adfcba55Sjoycey (uint32_t)tx_desc_p; 711adfcba55Sjoycey #else 71252ccf843Smisaki npi_desc_handle.regp = 7136f45ec7bSml29623 (uint64_t)tx_desc_p; 714adfcba55Sjoycey #endif 7156f45ec7bSml29623 tx_msg_p = &tx_msg_ring[i]; 7166f45ec7bSml29623 tx_msg_p->flags.dma_type = USE_NONE; 7176f45ec7bSml29623 tx_desc.value = 0; 7186f45ec7bSml29623 7196f45ec7bSml29623 ncookies--; 7206f45ec7bSml29623 } 7216f45ec7bSml29623 tdc_stats->tx_ddi_pkts++; 7226f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start:" 7236f45ec7bSml29623 "DMA: ddi packets %d", 7246f45ec7bSml29623 tdc_stats->tx_ddi_pkts)); 7256f45ec7bSml29623 } else { 7266f45ec7bSml29623 NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL, 7276f45ec7bSml29623 "dma mapping failed for %d " 7286f45ec7bSml29623 "bytes addr $%p flags %x (%d)", 7296f45ec7bSml29623 len, b_rptr, status, status)); 7306f45ec7bSml29623 good_packet = B_FALSE; 7316f45ec7bSml29623 tdc_stats->tx_dma_bind_fail++; 7326f45ec7bSml29623 tx_msg_p->flags.dma_type = USE_NONE; 73330ac2e7bSml29623 if (is_lso) { 73430ac2e7bSml29623 mp = nmp; 73530ac2e7bSml29623 goto nxge_start_fail_lso; 73630ac2e7bSml29623 } else { 73731f519f9SMichael Speer status = 1; 7386f45ec7bSml29623 goto nxge_start_fail2; 7396f45ec7bSml29623 } 74030ac2e7bSml29623 } 7416f45ec7bSml29623 } /* ddi dvma */ 7426f45ec7bSml29623 74330ac2e7bSml29623 if (is_lso) { 74430ac2e7bSml29623 nmp_lso_save = nmp; 74530ac2e7bSml29623 } 7466f45ec7bSml29623 nmp = nmp->b_cont; 7476f45ec7bSml29623 nxge_start_control_header_only: 748adfcba55Sjoycey #if defined(__i386) 749adfcba55Sjoycey npi_desc_handle.regp = (uint32_t)tx_desc_p; 750adfcba55Sjoycey #else 7516f45ec7bSml29623 npi_desc_handle.regp = (uint64_t)tx_desc_p; 752adfcba55Sjoycey #endif 7536f45ec7bSml29623 ngathers++; 7546f45ec7bSml29623 7556f45ec7bSml29623 if (ngathers == 1) { 7566f45ec7bSml29623 #ifdef NXGE_DEBUG 7576f45ec7bSml29623 save_desc_p = &sop_tx_desc; 7586f45ec7bSml29623 #endif 7596f45ec7bSml29623 sop_tx_desc_p = &sop_tx_desc; 7606f45ec7bSml29623 sop_tx_desc_p->value = 0; 7616f45ec7bSml29623 sop_tx_desc_p->bits.hdw.tr_len = clen; 7626f45ec7bSml29623 sop_tx_desc_p->bits.hdw.sad = dma_ioaddr >> 32; 7636f45ec7bSml29623 sop_tx_desc_p->bits.ldw.sad = dma_ioaddr & 0xffffffff; 7646f45ec7bSml29623 } else { 7656f45ec7bSml29623 #ifdef NXGE_DEBUG 7666f45ec7bSml29623 save_desc_p = &tx_desc; 7676f45ec7bSml29623 #endif 7686f45ec7bSml29623 tmp_desc_p = &tx_desc; 7696f45ec7bSml29623 tmp_desc_p->value = 0; 7706f45ec7bSml29623 tmp_desc_p->bits.hdw.tr_len = clen; 7716f45ec7bSml29623 tmp_desc_p->bits.hdw.sad = dma_ioaddr >> 32; 7726f45ec7bSml29623 tmp_desc_p->bits.ldw.sad = dma_ioaddr & 0xffffffff; 7736f45ec7bSml29623 7746f45ec7bSml29623 tx_desc_p->value = tmp_desc_p->value; 7756f45ec7bSml29623 } 7766f45ec7bSml29623 7776f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(13): " 7786f45ec7bSml29623 "Desc_entry %d ngathers %d " 7796f45ec7bSml29623 "desc_vp $%p tx_desc_p $%p " 7806f45ec7bSml29623 "len %d clen %d pkt_len %d pack_len %d nmblks %d " 7816f45ec7bSml29623 "dma_ioaddr (SAD) $%p mark %d", 7826f45ec7bSml29623 i, ngathers, 7836f45ec7bSml29623 tx_desc_ring_vp, tx_desc_p, 7846f45ec7bSml29623 len, clen, pkt_len, pack_len, nmblks, 7856f45ec7bSml29623 dma_ioaddr, mark_mode)); 7866f45ec7bSml29623 7876f45ec7bSml29623 #ifdef NXGE_DEBUG 7886f45ec7bSml29623 npi_desc_handle.nxgep = nxgep; 7896f45ec7bSml29623 npi_desc_handle.function.function = nxgep->function_num; 7906f45ec7bSml29623 npi_desc_handle.function.instance = nxgep->instance; 7916f45ec7bSml29623 sad = (save_desc_p->value & TX_PKT_DESC_SAD_MASK); 7926f45ec7bSml29623 xfer_len = ((save_desc_p->value & TX_PKT_DESC_TR_LEN_MASK) >> 7936f45ec7bSml29623 TX_PKT_DESC_TR_LEN_SHIFT); 7946f45ec7bSml29623 7956f45ec7bSml29623 7966f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "\n\t: value 0x%llx\n" 7976f45ec7bSml29623 "\t\tsad $%p\ttr_len %d len %d\tnptrs %d\t" 7986f45ec7bSml29623 "mark %d sop %d\n", 7996f45ec7bSml29623 save_desc_p->value, 8006f45ec7bSml29623 sad, 8016f45ec7bSml29623 save_desc_p->bits.hdw.tr_len, 8026f45ec7bSml29623 xfer_len, 8036f45ec7bSml29623 save_desc_p->bits.hdw.num_ptr, 8046f45ec7bSml29623 save_desc_p->bits.hdw.mark, 8056f45ec7bSml29623 save_desc_p->bits.hdw.sop)); 8066f45ec7bSml29623 8076f45ec7bSml29623 npi_txdma_dump_desc_one(npi_desc_handle, NULL, i); 8086f45ec7bSml29623 #endif 8096f45ec7bSml29623 8106f45ec7bSml29623 tx_msg_p->tx_msg_size = clen; 8116f45ec7bSml29623 i = TXDMA_DESC_NEXT_INDEX(i, 1, tx_ring_p->tx_wrap_mask); 8126f45ec7bSml29623 if (ngathers > nxge_tx_max_gathers) { 8136f45ec7bSml29623 good_packet = B_FALSE; 8140dc2366fSVenugopal Iyer mac_hcksum_get(mp, &start_offset, 8156f45ec7bSml29623 &stuff_offset, &end_offset, &value, 8166f45ec7bSml29623 &cksum_flags); 8176f45ec7bSml29623 8186f45ec7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 8196f45ec7bSml29623 "==> nxge_start(14): pull msg - " 8206f45ec7bSml29623 "len %d pkt_len %d ngathers %d", 8216f45ec7bSml29623 len, pkt_len, ngathers)); 82231f519f9SMichael Speer 82331f519f9SMichael Speer /* 82431f519f9SMichael Speer * Just give up on this packet. 82531f519f9SMichael Speer */ 82630ac2e7bSml29623 if (is_lso) { 82730ac2e7bSml29623 mp = nmp_lso_save; 82830ac2e7bSml29623 goto nxge_start_fail_lso; 82930ac2e7bSml29623 } 83031f519f9SMichael Speer status = 0; 8316f45ec7bSml29623 goto nxge_start_fail2; 8326f45ec7bSml29623 } 8336f45ec7bSml29623 } /* while (nmp) */ 8346f45ec7bSml29623 8356f45ec7bSml29623 tx_msg_p->tx_message = mp; 8366f45ec7bSml29623 tx_desc_p = &tx_desc_ring_vp[sop_index]; 837adfcba55Sjoycey #if defined(__i386) 838adfcba55Sjoycey npi_desc_handle.regp = (uint32_t)tx_desc_p; 839adfcba55Sjoycey #else 8406f45ec7bSml29623 npi_desc_handle.regp = (uint64_t)tx_desc_p; 841adfcba55Sjoycey #endif 8426f45ec7bSml29623 8436f45ec7bSml29623 pkthdrp = (p_tx_pkt_hdr_all_t)hdrp; 8446f45ec7bSml29623 pkthdrp->reserved = 0; 8456f45ec7bSml29623 hdrp->value = 0; 846b4d05839Sml29623 bcopy(&tmp_hdrp, hdrp, sizeof (tx_pkt_header_t)); 8476f45ec7bSml29623 8486f45ec7bSml29623 if (pkt_len > NXGE_MTU_DEFAULT_MAX) { 8496f45ec7bSml29623 tdc_stats->tx_jumbo_pkts++; 8506f45ec7bSml29623 } 8516f45ec7bSml29623 852678453a8Sspeer min_len = (ETHERMIN + TX_PKT_HEADER_SIZE + (npads * 2)); 8536f45ec7bSml29623 if (pkt_len < min_len) { 8546f45ec7bSml29623 /* Assume we use bcopy to premapped buffers */ 8556f45ec7bSml29623 kaddr = (caddr_t)DMA_COMMON_VPTR(tx_msg_p->buf_dma); 8566f45ec7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 8576f45ec7bSml29623 "==> nxge_start(14-1): < (msg_min + 16)" 8586f45ec7bSml29623 "len %d pkt_len %d min_len %d bzero %d ngathers %d", 8596f45ec7bSml29623 len, pkt_len, min_len, (min_len - pkt_len), ngathers)); 8606f45ec7bSml29623 bzero((kaddr + pkt_len), (min_len - pkt_len)); 8616f45ec7bSml29623 pkt_len = tx_msg_p->tx_msg_size = min_len; 8626f45ec7bSml29623 8636f45ec7bSml29623 sop_tx_desc_p->bits.hdw.tr_len = min_len; 8646f45ec7bSml29623 8656f45ec7bSml29623 NXGE_MEM_PIO_WRITE64(npi_desc_handle, sop_tx_desc_p->value); 8666f45ec7bSml29623 tx_desc_p->value = sop_tx_desc_p->value; 8676f45ec7bSml29623 8686f45ec7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 8696f45ec7bSml29623 "==> nxge_start(14-2): < msg_min - " 8706f45ec7bSml29623 "len %d pkt_len %d min_len %d ngathers %d", 8716f45ec7bSml29623 len, pkt_len, min_len, ngathers)); 8726f45ec7bSml29623 } 8736f45ec7bSml29623 8746f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: cksum_flags 0x%x ", 8756f45ec7bSml29623 cksum_flags)); 8766f45ec7bSml29623 { 8776f45ec7bSml29623 uint64_t tmp_len; 8786f45ec7bSml29623 8796f45ec7bSml29623 /* pkt_len already includes 16 + paddings!! */ 8806f45ec7bSml29623 /* Update the control header length */ 8816f45ec7bSml29623 tot_xfer_len = (pkt_len - TX_PKT_HEADER_SIZE); 8826f45ec7bSml29623 tmp_len = hdrp->value | 8836f45ec7bSml29623 (tot_xfer_len << TX_PKT_HEADER_TOT_XFER_LEN_SHIFT); 8846f45ec7bSml29623 8856f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 8866f45ec7bSml29623 "==> nxge_start(15_x1): setting SOP " 8876f45ec7bSml29623 "tot_xfer_len 0x%llx (%d) pkt_len %d tmp_len " 8886f45ec7bSml29623 "0x%llx hdrp->value 0x%llx", 8896f45ec7bSml29623 tot_xfer_len, tot_xfer_len, pkt_len, 8906f45ec7bSml29623 tmp_len, hdrp->value)); 8916f45ec7bSml29623 #if defined(_BIG_ENDIAN) 8926f45ec7bSml29623 hdrp->value = ddi_swap64(tmp_len); 8936f45ec7bSml29623 #else 8946f45ec7bSml29623 hdrp->value = tmp_len; 8956f45ec7bSml29623 #endif 8966f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, 8976f45ec7bSml29623 TX_CTL, "==> nxge_start(15_x2): setting SOP " 8986f45ec7bSml29623 "after SWAP: tot_xfer_len 0x%llx pkt_len %d " 8996f45ec7bSml29623 "tmp_len 0x%llx hdrp->value 0x%llx", 9006f45ec7bSml29623 tot_xfer_len, pkt_len, 9016f45ec7bSml29623 tmp_len, hdrp->value)); 9026f45ec7bSml29623 } 9036f45ec7bSml29623 9046f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(15): setting SOP " 9056f45ec7bSml29623 "wr_index %d " 9066f45ec7bSml29623 "tot_xfer_len (%d) pkt_len %d npads %d", 9076f45ec7bSml29623 sop_index, 9086f45ec7bSml29623 tot_xfer_len, pkt_len, 9096f45ec7bSml29623 npads)); 9106f45ec7bSml29623 9116f45ec7bSml29623 sop_tx_desc_p->bits.hdw.sop = 1; 9126f45ec7bSml29623 sop_tx_desc_p->bits.hdw.mark = mark_mode; 9136f45ec7bSml29623 sop_tx_desc_p->bits.hdw.num_ptr = ngathers; 9146f45ec7bSml29623 9156f45ec7bSml29623 NXGE_MEM_PIO_WRITE64(npi_desc_handle, sop_tx_desc_p->value); 9166f45ec7bSml29623 9176f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start(16): set SOP done")); 9186f45ec7bSml29623 9196f45ec7bSml29623 #ifdef NXGE_DEBUG 9206f45ec7bSml29623 npi_desc_handle.nxgep = nxgep; 9216f45ec7bSml29623 npi_desc_handle.function.function = nxgep->function_num; 9226f45ec7bSml29623 npi_desc_handle.function.instance = nxgep->instance; 9236f45ec7bSml29623 9246f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "\n\t: value 0x%llx\n" 9256f45ec7bSml29623 "\t\tsad $%p\ttr_len %d len %d\tnptrs %d\tmark %d sop %d\n", 9266f45ec7bSml29623 save_desc_p->value, 9276f45ec7bSml29623 sad, 9286f45ec7bSml29623 save_desc_p->bits.hdw.tr_len, 9296f45ec7bSml29623 xfer_len, 9306f45ec7bSml29623 save_desc_p->bits.hdw.num_ptr, 9316f45ec7bSml29623 save_desc_p->bits.hdw.mark, 9326f45ec7bSml29623 save_desc_p->bits.hdw.sop)); 9336f45ec7bSml29623 (void) npi_txdma_dump_desc_one(npi_desc_handle, NULL, sop_index); 9346f45ec7bSml29623 9356f45ec7bSml29623 dump_len = (pkt_len > 128) ? 128: pkt_len; 9366f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 9376f45ec7bSml29623 "==> nxge_start: dump packets(17) (after sop set, len " 9386f45ec7bSml29623 " (len/dump_len/pkt_len/tot_xfer_len) %d/%d/%d/%d):\n" 9396f45ec7bSml29623 "ptr $%p: %s", len, dump_len, pkt_len, tot_xfer_len, 9406f45ec7bSml29623 (char *)hdrp, 9416f45ec7bSml29623 nxge_dump_packet((char *)hdrp, dump_len))); 9426f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 9436f45ec7bSml29623 "==> nxge_start(18): TX desc sync: sop_index %d", 9446f45ec7bSml29623 sop_index)); 9456f45ec7bSml29623 #endif 9466f45ec7bSml29623 9476f45ec7bSml29623 if ((ngathers == 1) || tx_ring_p->wr_index < i) { 9486f45ec7bSml29623 (void) ddi_dma_sync(tx_desc_dma_handle, 9496f45ec7bSml29623 sop_index * sizeof (tx_desc_t), 9506f45ec7bSml29623 ngathers * sizeof (tx_desc_t), 9516f45ec7bSml29623 DDI_DMA_SYNC_FORDEV); 9526f45ec7bSml29623 9536f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "nxge_start(19): sync 1 " 9546f45ec7bSml29623 "cs_off = 0x%02X cs_s_off = 0x%02X " 9556f45ec7bSml29623 "pkt_len %d ngathers %d sop_index %d\n", 9566f45ec7bSml29623 stuff_offset, start_offset, 9576f45ec7bSml29623 pkt_len, ngathers, sop_index)); 9586f45ec7bSml29623 } else { /* more than one descriptor and wrap around */ 9596f45ec7bSml29623 uint32_t nsdescs = tx_ring_p->tx_ring_size - sop_index; 9606f45ec7bSml29623 (void) ddi_dma_sync(tx_desc_dma_handle, 9616f45ec7bSml29623 sop_index * sizeof (tx_desc_t), 9626f45ec7bSml29623 nsdescs * sizeof (tx_desc_t), 9636f45ec7bSml29623 DDI_DMA_SYNC_FORDEV); 9646f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "nxge_start(20): sync 1 " 9656f45ec7bSml29623 "cs_off = 0x%02X cs_s_off = 0x%02X " 9666f45ec7bSml29623 "pkt_len %d ngathers %d sop_index %d\n", 9676f45ec7bSml29623 stuff_offset, start_offset, 9686f45ec7bSml29623 pkt_len, ngathers, sop_index)); 9696f45ec7bSml29623 9706f45ec7bSml29623 (void) ddi_dma_sync(tx_desc_dma_handle, 9716f45ec7bSml29623 0, 9726f45ec7bSml29623 (ngathers - nsdescs) * sizeof (tx_desc_t), 9736f45ec7bSml29623 DDI_DMA_SYNC_FORDEV); 9746f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "nxge_start(21): sync 2 " 9756f45ec7bSml29623 "cs_off = 0x%02X cs_s_off = 0x%02X " 9766f45ec7bSml29623 "pkt_len %d ngathers %d sop_index %d\n", 9776f45ec7bSml29623 stuff_offset, start_offset, 9786f45ec7bSml29623 pkt_len, ngathers, sop_index)); 9796f45ec7bSml29623 } 9806f45ec7bSml29623 9816f45ec7bSml29623 tail_index = tx_ring_p->wr_index; 9826f45ec7bSml29623 tail_wrap = tx_ring_p->wr_index_wrap; 9836f45ec7bSml29623 9846f45ec7bSml29623 tx_ring_p->wr_index = i; 9856f45ec7bSml29623 if (tx_ring_p->wr_index <= tail_index) { 9866f45ec7bSml29623 tx_ring_p->wr_index_wrap = ((tail_wrap == B_TRUE) ? 9876f45ec7bSml29623 B_FALSE : B_TRUE); 9886f45ec7bSml29623 } 9896f45ec7bSml29623 9906f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: TX kick: " 9916f45ec7bSml29623 "channel %d wr_index %d wrap %d ngathers %d desc_pend %d", 9926f45ec7bSml29623 tx_ring_p->tdc, 9936f45ec7bSml29623 tx_ring_p->wr_index, 9946f45ec7bSml29623 tx_ring_p->wr_index_wrap, 9956f45ec7bSml29623 ngathers, 9966f45ec7bSml29623 tx_ring_p->descs_pending)); 9976f45ec7bSml29623 99830ac2e7bSml29623 if (is_lso) { 99930ac2e7bSml29623 lso_ngathers += ngathers; 100030ac2e7bSml29623 if (mp_chain != NULL) { 100130ac2e7bSml29623 mp = mp_chain; 100230ac2e7bSml29623 mp_chain = mp_chain->b_next; 100330ac2e7bSml29623 mp->b_next = NULL; 100430ac2e7bSml29623 if (nxge_lso_kick_cnt == lso_ngathers) { 10057a6dff21Sml29623 tx_ring_p->descs_pending += lso_ngathers; 100630ac2e7bSml29623 { 100730ac2e7bSml29623 tx_ring_kick_t kick; 100830ac2e7bSml29623 100930ac2e7bSml29623 kick.value = 0; 101030ac2e7bSml29623 kick.bits.ldw.wrap = 101130ac2e7bSml29623 tx_ring_p->wr_index_wrap; 101230ac2e7bSml29623 kick.bits.ldw.tail = 101330ac2e7bSml29623 (uint16_t)tx_ring_p->wr_index; 101430ac2e7bSml29623 101530ac2e7bSml29623 /* Kick the Transmit kick register */ 101630ac2e7bSml29623 TXDMA_REG_WRITE64( 101730ac2e7bSml29623 NXGE_DEV_NPI_HANDLE(nxgep), 101830ac2e7bSml29623 TX_RING_KICK_REG, 101930ac2e7bSml29623 (uint8_t)tx_ring_p->tdc, 102030ac2e7bSml29623 kick.value); 102130ac2e7bSml29623 tdc_stats->tx_starts++; 1022678453a8Sspeer 102330ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 102430ac2e7bSml29623 "==> nxge_start: more LSO: " 102530ac2e7bSml29623 "LSO_CNT %d", 1026678453a8Sspeer lso_ngathers)); 102730ac2e7bSml29623 } 102830ac2e7bSml29623 lso_ngathers = 0; 102930ac2e7bSml29623 ngathers = 0; 103030ac2e7bSml29623 cur_index_lso = sop_index = tx_ring_p->wr_index; 103130ac2e7bSml29623 lso_tail_wrap = tx_ring_p->wr_index_wrap; 103230ac2e7bSml29623 } 103330ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 103430ac2e7bSml29623 "==> nxge_start: lso again: " 103530ac2e7bSml29623 "lso_gathers %d ngathers %d cur_index_lso %d " 103630ac2e7bSml29623 "wr_index %d sop_index %d", 103730ac2e7bSml29623 lso_ngathers, ngathers, cur_index_lso, 103830ac2e7bSml29623 tx_ring_p->wr_index, sop_index)); 103930ac2e7bSml29623 104030ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 104130ac2e7bSml29623 "==> nxge_start: next : count %d", 1042678453a8Sspeer lso_ngathers)); 104330ac2e7bSml29623 lso_again = B_TRUE; 104430ac2e7bSml29623 goto start_again; 104530ac2e7bSml29623 } 10467a6dff21Sml29623 ngathers = lso_ngathers; 104730ac2e7bSml29623 } 104830ac2e7bSml29623 10496f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: TX KICKING: ")); 10506f45ec7bSml29623 10516f45ec7bSml29623 { 10526f45ec7bSml29623 tx_ring_kick_t kick; 10536f45ec7bSml29623 10546f45ec7bSml29623 kick.value = 0; 10556f45ec7bSml29623 kick.bits.ldw.wrap = tx_ring_p->wr_index_wrap; 10566f45ec7bSml29623 kick.bits.ldw.tail = (uint16_t)tx_ring_p->wr_index; 10576f45ec7bSml29623 10586f45ec7bSml29623 /* Kick start the Transmit kick register */ 10596f45ec7bSml29623 TXDMA_REG_WRITE64(NXGE_DEV_NPI_HANDLE(nxgep), 10606f45ec7bSml29623 TX_RING_KICK_REG, 10616f45ec7bSml29623 (uint8_t)tx_ring_p->tdc, 10626f45ec7bSml29623 kick.value); 10636f45ec7bSml29623 } 10646f45ec7bSml29623 10657a6dff21Sml29623 tx_ring_p->descs_pending += ngathers; 10666f45ec7bSml29623 tdc_stats->tx_starts++; 10676f45ec7bSml29623 10686895688eSspeer if (isLDOMservice(nxgep)) { 10696895688eSspeer tx_ring_p->tx_ring_busy = B_FALSE; 10706895688eSspeer if (tx_ring_p->tx_ring_offline) { 107122c0d73aSspeer (void) atomic_swap_32(&tx_ring_p->tx_ring_offline, 107222c0d73aSspeer NXGE_TX_RING_OFFLINED); 10736895688eSspeer } 10746895688eSspeer } 1075678453a8Sspeer 10766f45ec7bSml29623 MUTEX_EXIT(&tx_ring_p->lock); 10776f45ec7bSml29623 10786f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "<== nxge_start")); 10796f45ec7bSml29623 return (status); 10806f45ec7bSml29623 108130ac2e7bSml29623 nxge_start_fail_lso: 108230ac2e7bSml29623 status = 0; 108330ac2e7bSml29623 good_packet = B_FALSE; 108431f519f9SMichael Speer if (mp != NULL) 108530ac2e7bSml29623 freemsg(mp); 108631f519f9SMichael Speer if (mp_chain != NULL) 108731f519f9SMichael Speer freemsgchain(mp_chain); 108831f519f9SMichael Speer 108930ac2e7bSml29623 if (!lso_again && !ngathers) { 10906895688eSspeer if (isLDOMservice(nxgep)) { 10916895688eSspeer tx_ring_p->tx_ring_busy = B_FALSE; 10926895688eSspeer if (tx_ring_p->tx_ring_offline) { 10936895688eSspeer (void) atomic_swap_32( 10946895688eSspeer &tx_ring_p->tx_ring_offline, 109522c0d73aSspeer NXGE_TX_RING_OFFLINED); 10966895688eSspeer } 10976895688eSspeer } 10986895688eSspeer 109930ac2e7bSml29623 MUTEX_EXIT(&tx_ring_p->lock); 110030ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 110130ac2e7bSml29623 "==> nxge_start: lso exit (nothing changed)")); 110230ac2e7bSml29623 goto nxge_start_fail1; 110330ac2e7bSml29623 } 110430ac2e7bSml29623 110530ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 110630ac2e7bSml29623 "==> nxge_start (channel %d): before lso " 110730ac2e7bSml29623 "lso_gathers %d ngathers %d cur_index_lso %d " 110830ac2e7bSml29623 "wr_index %d sop_index %d lso_again %d", 110930ac2e7bSml29623 tx_ring_p->tdc, 111030ac2e7bSml29623 lso_ngathers, ngathers, cur_index_lso, 111130ac2e7bSml29623 tx_ring_p->wr_index, sop_index, lso_again)); 111230ac2e7bSml29623 111330ac2e7bSml29623 if (lso_again) { 111430ac2e7bSml29623 lso_ngathers += ngathers; 111530ac2e7bSml29623 ngathers = lso_ngathers; 111630ac2e7bSml29623 sop_index = cur_index_lso; 111730ac2e7bSml29623 tx_ring_p->wr_index = sop_index; 111830ac2e7bSml29623 tx_ring_p->wr_index_wrap = lso_tail_wrap; 111930ac2e7bSml29623 } 112030ac2e7bSml29623 112130ac2e7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 112230ac2e7bSml29623 "==> nxge_start (channel %d): after lso " 112330ac2e7bSml29623 "lso_gathers %d ngathers %d cur_index_lso %d " 112430ac2e7bSml29623 "wr_index %d sop_index %d lso_again %d", 112530ac2e7bSml29623 tx_ring_p->tdc, 112630ac2e7bSml29623 lso_ngathers, ngathers, cur_index_lso, 112730ac2e7bSml29623 tx_ring_p->wr_index, sop_index, lso_again)); 112830ac2e7bSml29623 11296f45ec7bSml29623 nxge_start_fail2: 11306f45ec7bSml29623 if (good_packet == B_FALSE) { 11316f45ec7bSml29623 cur_index = sop_index; 11326f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_start: clean up")); 11336f45ec7bSml29623 for (i = 0; i < ngathers; i++) { 11346f45ec7bSml29623 tx_desc_p = &tx_desc_ring_vp[cur_index]; 1135adfcba55Sjoycey #if defined(__i386) 1136adfcba55Sjoycey npi_handle.regp = (uint32_t)tx_desc_p; 1137adfcba55Sjoycey #else 11386f45ec7bSml29623 npi_handle.regp = (uint64_t)tx_desc_p; 1139adfcba55Sjoycey #endif 11406f45ec7bSml29623 tx_msg_p = &tx_msg_ring[cur_index]; 11416f45ec7bSml29623 (void) npi_txdma_desc_set_zero(npi_handle, 1); 11426f45ec7bSml29623 if (tx_msg_p->flags.dma_type == USE_DVMA) { 11436f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, 11446f45ec7bSml29623 "tx_desc_p = %X index = %d", 11456f45ec7bSml29623 tx_desc_p, tx_ring_p->rd_index)); 114653f3d8ecSyc148097 (void) dvma_unload(tx_msg_p->dvma_handle, 11476f45ec7bSml29623 0, -1); 11486f45ec7bSml29623 tx_msg_p->dvma_handle = NULL; 11496f45ec7bSml29623 if (tx_ring_p->dvma_wr_index == 11506f45ec7bSml29623 tx_ring_p->dvma_wrap_mask) 11516f45ec7bSml29623 tx_ring_p->dvma_wr_index = 0; 11526f45ec7bSml29623 else 11536f45ec7bSml29623 tx_ring_p->dvma_wr_index++; 11546f45ec7bSml29623 tx_ring_p->dvma_pending--; 115553f3d8ecSyc148097 } else if (tx_msg_p->flags.dma_type == USE_DMA) { 11566f45ec7bSml29623 if (ddi_dma_unbind_handle( 115753f3d8ecSyc148097 tx_msg_p->dma_handle)) { 11586f45ec7bSml29623 cmn_err(CE_WARN, "!nxge_start: " 11596f45ec7bSml29623 "ddi_dma_unbind_handle failed"); 11606f45ec7bSml29623 } 116153f3d8ecSyc148097 } 11626f45ec7bSml29623 tx_msg_p->flags.dma_type = USE_NONE; 11636f45ec7bSml29623 cur_index = TXDMA_DESC_NEXT_INDEX(cur_index, 1, 11646f45ec7bSml29623 tx_ring_p->tx_wrap_mask); 11656f45ec7bSml29623 11666f45ec7bSml29623 } 11676f45ec7bSml29623 } 11686f45ec7bSml29623 11696895688eSspeer if (isLDOMservice(nxgep)) { 11706895688eSspeer tx_ring_p->tx_ring_busy = B_FALSE; 11716895688eSspeer if (tx_ring_p->tx_ring_offline) { 117222c0d73aSspeer (void) atomic_swap_32(&tx_ring_p->tx_ring_offline, 117322c0d73aSspeer NXGE_TX_RING_OFFLINED); 11746895688eSspeer } 11756895688eSspeer } 1176678453a8Sspeer 11776f45ec7bSml29623 MUTEX_EXIT(&tx_ring_p->lock); 11786f45ec7bSml29623 11796f45ec7bSml29623 nxge_start_fail1: 11806f45ec7bSml29623 /* Add FMA to check the access handle nxge_hregh */ 11816f45ec7bSml29623 11826f45ec7bSml29623 NXGE_DEBUG_MSG((nxgep, TX_CTL, "<== nxge_start")); 11836f45ec7bSml29623 return (status); 11846f45ec7bSml29623 } 11856f45ec7bSml29623 118630ac2e7bSml29623 /* Software LSO starts here */ 118730ac2e7bSml29623 static void 118830ac2e7bSml29623 nxge_hcksum_retrieve(mblk_t *mp, 118930ac2e7bSml29623 uint32_t *start, uint32_t *stuff, uint32_t *end, 119030ac2e7bSml29623 uint32_t *value, uint32_t *flags) 119130ac2e7bSml29623 { 119230ac2e7bSml29623 if (mp->b_datap->db_type == M_DATA) { 119330ac2e7bSml29623 if (flags != NULL) { 119430ac2e7bSml29623 *flags = DB_CKSUMFLAGS(mp) & (HCK_IPV4_HDRCKSUM | 119530ac2e7bSml29623 HCK_PARTIALCKSUM | HCK_FULLCKSUM | 119630ac2e7bSml29623 HCK_FULLCKSUM_OK); 119730ac2e7bSml29623 if ((*flags & (HCK_PARTIALCKSUM | 119830ac2e7bSml29623 HCK_FULLCKSUM)) != 0) { 119930ac2e7bSml29623 if (value != NULL) 120030ac2e7bSml29623 *value = (uint32_t)DB_CKSUM16(mp); 120130ac2e7bSml29623 if ((*flags & HCK_PARTIALCKSUM) != 0) { 120230ac2e7bSml29623 if (start != NULL) 120330ac2e7bSml29623 *start = 120430ac2e7bSml29623 (uint32_t)DB_CKSUMSTART(mp); 120530ac2e7bSml29623 if (stuff != NULL) 120630ac2e7bSml29623 *stuff = 120730ac2e7bSml29623 (uint32_t)DB_CKSUMSTUFF(mp); 120830ac2e7bSml29623 if (end != NULL) 120930ac2e7bSml29623 *end = 121030ac2e7bSml29623 (uint32_t)DB_CKSUMEND(mp); 121130ac2e7bSml29623 } 121230ac2e7bSml29623 } 121330ac2e7bSml29623 } 121430ac2e7bSml29623 } 121530ac2e7bSml29623 } 121630ac2e7bSml29623 121730ac2e7bSml29623 static void 121830ac2e7bSml29623 nxge_lso_info_get(mblk_t *mp, uint32_t *mss, uint32_t *flags) 121930ac2e7bSml29623 { 122030ac2e7bSml29623 ASSERT(DB_TYPE(mp) == M_DATA); 122130ac2e7bSml29623 122230ac2e7bSml29623 *mss = 0; 122330ac2e7bSml29623 if (flags != NULL) { 122430ac2e7bSml29623 *flags = DB_CKSUMFLAGS(mp) & HW_LSO; 122530ac2e7bSml29623 if ((*flags != 0) && (mss != NULL)) { 122630ac2e7bSml29623 *mss = (uint32_t)DB_LSOMSS(mp); 122730ac2e7bSml29623 } 122830ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 122930ac2e7bSml29623 "==> nxge_lso_info_get(flag !=NULL): mss %d *flags 0x%x", 123030ac2e7bSml29623 *mss, *flags)); 123130ac2e7bSml29623 } 123230ac2e7bSml29623 123330ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 123430ac2e7bSml29623 "<== nxge_lso_info_get: mss %d", *mss)); 123530ac2e7bSml29623 } 123630ac2e7bSml29623 123730ac2e7bSml29623 /* 123830ac2e7bSml29623 * Do Soft LSO on the oversized packet. 123930ac2e7bSml29623 * 124030ac2e7bSml29623 * 1. Create a chain of message for headers. 124130ac2e7bSml29623 * 2. Fill up header messages with proper information. 124230ac2e7bSml29623 * 3. Copy Eithernet, IP, and TCP headers from the original message to 124330ac2e7bSml29623 * each new message with necessary adjustments. 124430ac2e7bSml29623 * * Unchange the ethernet header for DIX frames. (by default) 124530ac2e7bSml29623 * * IP Total Length field is updated to MSS or less(only for the last one). 124630ac2e7bSml29623 * * IP Identification value is incremented by one for each packet. 124730ac2e7bSml29623 * * TCP sequence Number is recalculated according to the payload length. 124830ac2e7bSml29623 * * Set FIN and/or PSH flags for the *last* packet if applied. 124930ac2e7bSml29623 * * TCP partial Checksum 125030ac2e7bSml29623 * 4. Update LSO information in the first message header. 125130ac2e7bSml29623 * 5. Release the original message header. 125230ac2e7bSml29623 */ 125330ac2e7bSml29623 static mblk_t * 125430ac2e7bSml29623 nxge_do_softlso(mblk_t *mp, uint32_t mss) 125530ac2e7bSml29623 { 125630ac2e7bSml29623 uint32_t hckflags; 125730ac2e7bSml29623 int pktlen; 125830ac2e7bSml29623 int hdrlen; 125930ac2e7bSml29623 int segnum; 126030ac2e7bSml29623 int i; 126130ac2e7bSml29623 struct ether_vlan_header *evh; 126230ac2e7bSml29623 int ehlen, iphlen, tcphlen; 126330ac2e7bSml29623 struct ip *oiph, *niph; 126430ac2e7bSml29623 struct tcphdr *otcph, *ntcph; 126530ac2e7bSml29623 int available, len, left; 126630ac2e7bSml29623 uint16_t ip_id; 126730ac2e7bSml29623 uint32_t tcp_seq; 126830ac2e7bSml29623 #ifdef __sparc 126930ac2e7bSml29623 uint32_t tcp_seq_tmp; 127030ac2e7bSml29623 #endif 127130ac2e7bSml29623 mblk_t *datamp; 127230ac2e7bSml29623 uchar_t *rptr; 127330ac2e7bSml29623 mblk_t *nmp; 127430ac2e7bSml29623 mblk_t *cmp; 127530ac2e7bSml29623 mblk_t *mp_chain; 127630ac2e7bSml29623 boolean_t do_cleanup = B_FALSE; 127730ac2e7bSml29623 t_uscalar_t start_offset = 0; 127830ac2e7bSml29623 t_uscalar_t stuff_offset = 0; 127930ac2e7bSml29623 t_uscalar_t value = 0; 128030ac2e7bSml29623 uint16_t l4_len; 128130ac2e7bSml29623 ipaddr_t src, dst; 128230ac2e7bSml29623 uint32_t cksum, sum, l4cksum; 128330ac2e7bSml29623 128430ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 128530ac2e7bSml29623 "==> nxge_do_softlso")); 128630ac2e7bSml29623 /* 128730ac2e7bSml29623 * check the length of LSO packet payload and calculate the number of 128830ac2e7bSml29623 * segments to be generated. 128930ac2e7bSml29623 */ 129030ac2e7bSml29623 pktlen = msgsize(mp); 129130ac2e7bSml29623 evh = (struct ether_vlan_header *)mp->b_rptr; 129230ac2e7bSml29623 129330ac2e7bSml29623 /* VLAN? */ 129430ac2e7bSml29623 if (evh->ether_tpid == htons(ETHERTYPE_VLAN)) 129530ac2e7bSml29623 ehlen = sizeof (struct ether_vlan_header); 129630ac2e7bSml29623 else 129730ac2e7bSml29623 ehlen = sizeof (struct ether_header); 129830ac2e7bSml29623 oiph = (struct ip *)(mp->b_rptr + ehlen); 129930ac2e7bSml29623 iphlen = oiph->ip_hl * 4; 130030ac2e7bSml29623 otcph = (struct tcphdr *)(mp->b_rptr + ehlen + iphlen); 130130ac2e7bSml29623 tcphlen = otcph->th_off * 4; 130230ac2e7bSml29623 130330ac2e7bSml29623 l4_len = pktlen - ehlen - iphlen; 130430ac2e7bSml29623 130530ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 130630ac2e7bSml29623 "==> nxge_do_softlso: mss %d oiph $%p " 130730ac2e7bSml29623 "original ip_sum oiph->ip_sum 0x%x " 130830ac2e7bSml29623 "original tcp_sum otcph->th_sum 0x%x " 130930ac2e7bSml29623 "oiph->ip_len %d pktlen %d ehlen %d " 131030ac2e7bSml29623 "l4_len %d (0x%x) ip_len - iphlen %d ", 131130ac2e7bSml29623 mss, 131230ac2e7bSml29623 oiph, 131330ac2e7bSml29623 oiph->ip_sum, 131430ac2e7bSml29623 otcph->th_sum, 131530ac2e7bSml29623 ntohs(oiph->ip_len), pktlen, 131630ac2e7bSml29623 ehlen, 131730ac2e7bSml29623 l4_len, 131830ac2e7bSml29623 l4_len, 131930ac2e7bSml29623 ntohs(oiph->ip_len) - iphlen)); 132030ac2e7bSml29623 132130ac2e7bSml29623 /* IPv4 + TCP */ 132230ac2e7bSml29623 if (!(oiph->ip_v == IPV4_VERSION)) { 132330ac2e7bSml29623 NXGE_ERROR_MSG((NULL, NXGE_ERR_CTL, 132430ac2e7bSml29623 "<== nxge_do_softlso: not IPV4 " 132530ac2e7bSml29623 "oiph->ip_len %d pktlen %d ehlen %d tcphlen %d", 132630ac2e7bSml29623 ntohs(oiph->ip_len), pktlen, ehlen, 132730ac2e7bSml29623 tcphlen)); 132830ac2e7bSml29623 freemsg(mp); 132930ac2e7bSml29623 return (NULL); 133030ac2e7bSml29623 } 133130ac2e7bSml29623 133230ac2e7bSml29623 if (!(oiph->ip_p == IPPROTO_TCP)) { 133330ac2e7bSml29623 NXGE_ERROR_MSG((NULL, NXGE_ERR_CTL, 133430ac2e7bSml29623 "<== nxge_do_softlso: not TCP " 133530ac2e7bSml29623 "oiph->ip_len %d pktlen %d ehlen %d tcphlen %d", 133630ac2e7bSml29623 ntohs(oiph->ip_len), pktlen, ehlen, 133730ac2e7bSml29623 tcphlen)); 133830ac2e7bSml29623 freemsg(mp); 133930ac2e7bSml29623 return (NULL); 134030ac2e7bSml29623 } 134130ac2e7bSml29623 134230ac2e7bSml29623 if (!(ntohs(oiph->ip_len) == pktlen - ehlen)) { 134330ac2e7bSml29623 NXGE_ERROR_MSG((NULL, NXGE_ERR_CTL, 134430ac2e7bSml29623 "<== nxge_do_softlso: len not matched " 134530ac2e7bSml29623 "oiph->ip_len %d pktlen %d ehlen %d tcphlen %d", 134630ac2e7bSml29623 ntohs(oiph->ip_len), pktlen, ehlen, 134730ac2e7bSml29623 tcphlen)); 134830ac2e7bSml29623 freemsg(mp); 134930ac2e7bSml29623 return (NULL); 135030ac2e7bSml29623 } 135130ac2e7bSml29623 135230ac2e7bSml29623 otcph = (struct tcphdr *)(mp->b_rptr + ehlen + iphlen); 135330ac2e7bSml29623 tcphlen = otcph->th_off * 4; 135430ac2e7bSml29623 135530ac2e7bSml29623 /* TCP flags can not include URG, RST, or SYN */ 135630ac2e7bSml29623 VERIFY((otcph->th_flags & (TH_SYN | TH_RST | TH_URG)) == 0); 135730ac2e7bSml29623 135830ac2e7bSml29623 hdrlen = ehlen + iphlen + tcphlen; 135930ac2e7bSml29623 136030ac2e7bSml29623 VERIFY(MBLKL(mp) >= hdrlen); 136130ac2e7bSml29623 136230ac2e7bSml29623 if (MBLKL(mp) > hdrlen) { 136330ac2e7bSml29623 datamp = mp; 136430ac2e7bSml29623 rptr = mp->b_rptr + hdrlen; 136530ac2e7bSml29623 } else { /* = */ 136630ac2e7bSml29623 datamp = mp->b_cont; 136730ac2e7bSml29623 rptr = datamp->b_rptr; 136830ac2e7bSml29623 } 136930ac2e7bSml29623 137030ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 137130ac2e7bSml29623 "nxge_do_softlso: otcph $%p pktlen: %d, " 137230ac2e7bSml29623 "hdrlen %d ehlen %d iphlen %d tcphlen %d " 137330ac2e7bSml29623 "mblkl(mp): %d, mblkl(datamp): %d", 137430ac2e7bSml29623 otcph, 137530ac2e7bSml29623 pktlen, hdrlen, ehlen, iphlen, tcphlen, 137630ac2e7bSml29623 (int)MBLKL(mp), (int)MBLKL(datamp))); 137730ac2e7bSml29623 137830ac2e7bSml29623 hckflags = 0; 137930ac2e7bSml29623 nxge_hcksum_retrieve(mp, 138030ac2e7bSml29623 &start_offset, &stuff_offset, &value, NULL, &hckflags); 138130ac2e7bSml29623 138230ac2e7bSml29623 dst = oiph->ip_dst.s_addr; 138330ac2e7bSml29623 src = oiph->ip_src.s_addr; 138430ac2e7bSml29623 138530ac2e7bSml29623 cksum = (dst >> 16) + (dst & 0xFFFF) + 138630ac2e7bSml29623 (src >> 16) + (src & 0xFFFF); 138730ac2e7bSml29623 l4cksum = cksum + IP_TCP_CSUM_COMP; 138830ac2e7bSml29623 138930ac2e7bSml29623 sum = l4_len + l4cksum; 139030ac2e7bSml29623 sum = (sum & 0xFFFF) + (sum >> 16); 139130ac2e7bSml29623 139230ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 139330ac2e7bSml29623 "==> nxge_do_softlso: dst 0x%x src 0x%x sum 0x%x ~new 0x%x " 139430ac2e7bSml29623 "hckflags 0x%x start_offset %d stuff_offset %d " 139530ac2e7bSml29623 "value (original) 0x%x th_sum 0x%x " 139630ac2e7bSml29623 "pktlen %d l4_len %d (0x%x) " 139730ac2e7bSml29623 "MBLKL(mp): %d, MBLKL(datamp): %d dump header %s", 139830ac2e7bSml29623 dst, src, 139930ac2e7bSml29623 (sum & 0xffff), (~sum & 0xffff), 140030ac2e7bSml29623 hckflags, start_offset, stuff_offset, 140130ac2e7bSml29623 value, otcph->th_sum, 140230ac2e7bSml29623 pktlen, 140330ac2e7bSml29623 l4_len, 140430ac2e7bSml29623 l4_len, 140530ac2e7bSml29623 ntohs(oiph->ip_len) - (int)MBLKL(mp), 140630ac2e7bSml29623 (int)MBLKL(datamp), 140730ac2e7bSml29623 nxge_dump_packet((char *)evh, 12))); 140830ac2e7bSml29623 140930ac2e7bSml29623 /* 141030ac2e7bSml29623 * Start to process. 141130ac2e7bSml29623 */ 141230ac2e7bSml29623 available = pktlen - hdrlen; 141330ac2e7bSml29623 segnum = (available - 1) / mss + 1; 141430ac2e7bSml29623 141530ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 141630ac2e7bSml29623 "==> nxge_do_softlso: pktlen %d " 141730ac2e7bSml29623 "MBLKL(mp): %d, MBLKL(datamp): %d " 141830ac2e7bSml29623 "available %d mss %d segnum %d", 141930ac2e7bSml29623 pktlen, (int)MBLKL(mp), (int)MBLKL(datamp), 142030ac2e7bSml29623 available, 142130ac2e7bSml29623 mss, 142230ac2e7bSml29623 segnum)); 142330ac2e7bSml29623 142430ac2e7bSml29623 VERIFY(segnum >= 2); 142530ac2e7bSml29623 142630ac2e7bSml29623 /* 142730ac2e7bSml29623 * Try to pre-allocate all header messages 142830ac2e7bSml29623 */ 142930ac2e7bSml29623 mp_chain = NULL; 143030ac2e7bSml29623 for (i = 0; i < segnum; i++) { 143130ac2e7bSml29623 if ((nmp = allocb(hdrlen, 0)) == NULL) { 143230ac2e7bSml29623 /* Clean up the mp_chain */ 143330ac2e7bSml29623 while (mp_chain != NULL) { 143430ac2e7bSml29623 nmp = mp_chain; 143530ac2e7bSml29623 mp_chain = mp_chain->b_next; 143630ac2e7bSml29623 freemsg(nmp); 143730ac2e7bSml29623 } 143830ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 143930ac2e7bSml29623 "<== nxge_do_softlso: " 144030ac2e7bSml29623 "Could not allocate enough messages for headers!")); 144130ac2e7bSml29623 freemsg(mp); 144230ac2e7bSml29623 return (NULL); 144330ac2e7bSml29623 } 144430ac2e7bSml29623 nmp->b_next = mp_chain; 144530ac2e7bSml29623 mp_chain = nmp; 144630ac2e7bSml29623 144730ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 144830ac2e7bSml29623 "==> nxge_do_softlso: " 144930ac2e7bSml29623 "mp $%p nmp $%p mp_chain $%p mp_chain->b_next $%p", 145030ac2e7bSml29623 mp, nmp, mp_chain, mp_chain->b_next)); 145130ac2e7bSml29623 } 145230ac2e7bSml29623 145330ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 145430ac2e7bSml29623 "==> nxge_do_softlso: mp $%p nmp $%p mp_chain $%p", 145530ac2e7bSml29623 mp, nmp, mp_chain)); 145630ac2e7bSml29623 145730ac2e7bSml29623 /* 145830ac2e7bSml29623 * Associate payload with new packets 145930ac2e7bSml29623 */ 146030ac2e7bSml29623 cmp = mp_chain; 146130ac2e7bSml29623 left = available; 146230ac2e7bSml29623 while (cmp != NULL) { 146330ac2e7bSml29623 nmp = dupb(datamp); 146430ac2e7bSml29623 if (nmp == NULL) { 146530ac2e7bSml29623 do_cleanup = B_TRUE; 146630ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 146730ac2e7bSml29623 "==>nxge_do_softlso: " 146830ac2e7bSml29623 "Can not dupb(datamp), have to do clean up")); 146930ac2e7bSml29623 goto cleanup_allocated_msgs; 147030ac2e7bSml29623 } 147130ac2e7bSml29623 147230ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 147330ac2e7bSml29623 "==> nxge_do_softlso: (loop) before mp $%p cmp $%p " 147430ac2e7bSml29623 "dupb nmp $%p len %d left %d msd %d ", 147530ac2e7bSml29623 mp, cmp, nmp, len, left, mss)); 147630ac2e7bSml29623 147730ac2e7bSml29623 cmp->b_cont = nmp; 147830ac2e7bSml29623 nmp->b_rptr = rptr; 147930ac2e7bSml29623 len = (left < mss) ? left : mss; 148030ac2e7bSml29623 left -= len; 148130ac2e7bSml29623 148230ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 148330ac2e7bSml29623 "==> nxge_do_softlso: (loop) after mp $%p cmp $%p " 148430ac2e7bSml29623 "dupb nmp $%p len %d left %d mss %d ", 148530ac2e7bSml29623 mp, cmp, nmp, len, left, mss)); 148630ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 148730ac2e7bSml29623 "nxge_do_softlso: before available: %d, " 148830ac2e7bSml29623 "left: %d, len: %d, segnum: %d MBLK(nmp): %d", 148930ac2e7bSml29623 available, left, len, segnum, (int)MBLKL(nmp))); 149030ac2e7bSml29623 149130ac2e7bSml29623 len -= MBLKL(nmp); 149230ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 149330ac2e7bSml29623 "nxge_do_softlso: after available: %d, " 149430ac2e7bSml29623 "left: %d, len: %d, segnum: %d MBLK(nmp): %d", 149530ac2e7bSml29623 available, left, len, segnum, (int)MBLKL(nmp))); 149630ac2e7bSml29623 149730ac2e7bSml29623 while (len > 0) { 149830ac2e7bSml29623 mblk_t *mmp = NULL; 149930ac2e7bSml29623 150030ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 150130ac2e7bSml29623 "nxge_do_softlso: (4) len > 0 available: %d, " 150230ac2e7bSml29623 "left: %d, len: %d, segnum: %d MBLK(nmp): %d", 150330ac2e7bSml29623 available, left, len, segnum, (int)MBLKL(nmp))); 150430ac2e7bSml29623 150530ac2e7bSml29623 if (datamp->b_cont != NULL) { 150630ac2e7bSml29623 datamp = datamp->b_cont; 150730ac2e7bSml29623 rptr = datamp->b_rptr; 150830ac2e7bSml29623 mmp = dupb(datamp); 150930ac2e7bSml29623 if (mmp == NULL) { 151030ac2e7bSml29623 do_cleanup = B_TRUE; 151130ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 151230ac2e7bSml29623 "==> nxge_do_softlso: " 1513678453a8Sspeer "Can not dupb(datamp) (1), :" 151430ac2e7bSml29623 "have to do clean up")); 151530ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 151630ac2e7bSml29623 "==> nxge_do_softlso: " 151730ac2e7bSml29623 "available: %d, left: %d, " 151830ac2e7bSml29623 "len: %d, MBLKL(nmp): %d", 151930ac2e7bSml29623 available, left, len, 152030ac2e7bSml29623 (int)MBLKL(nmp))); 152130ac2e7bSml29623 goto cleanup_allocated_msgs; 152230ac2e7bSml29623 } 152330ac2e7bSml29623 } else { 152430ac2e7bSml29623 NXGE_ERROR_MSG((NULL, NXGE_ERR_CTL, 152530ac2e7bSml29623 "==> nxge_do_softlso: " 152630ac2e7bSml29623 "(1)available: %d, left: %d, " 152730ac2e7bSml29623 "len: %d, MBLKL(nmp): %d", 152830ac2e7bSml29623 available, left, len, 152930ac2e7bSml29623 (int)MBLKL(nmp))); 153030ac2e7bSml29623 cmn_err(CE_PANIC, 153130ac2e7bSml29623 "==> nxge_do_softlso: " 153230ac2e7bSml29623 "Pointers must have been corrupted!\n" 153330ac2e7bSml29623 "datamp: $%p, nmp: $%p, rptr: $%p", 153430ac2e7bSml29623 (void *)datamp, 153530ac2e7bSml29623 (void *)nmp, 153630ac2e7bSml29623 (void *)rptr); 153730ac2e7bSml29623 } 153830ac2e7bSml29623 nmp->b_cont = mmp; 153930ac2e7bSml29623 nmp = mmp; 154030ac2e7bSml29623 len -= MBLKL(nmp); 154130ac2e7bSml29623 } 154230ac2e7bSml29623 if (len < 0) { 154330ac2e7bSml29623 nmp->b_wptr += len; 154430ac2e7bSml29623 rptr = nmp->b_wptr; 154530ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 154630ac2e7bSml29623 "(5) len < 0 (less than 0)" 154730ac2e7bSml29623 "available: %d, left: %d, len: %d, MBLKL(nmp): %d", 154830ac2e7bSml29623 available, left, len, (int)MBLKL(nmp))); 154930ac2e7bSml29623 155030ac2e7bSml29623 } else if (len == 0) { 155130ac2e7bSml29623 if (datamp->b_cont != NULL) { 155230ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 155330ac2e7bSml29623 "(5) len == 0" 155430ac2e7bSml29623 "available: %d, left: %d, len: %d, " 155530ac2e7bSml29623 "MBLKL(nmp): %d", 155630ac2e7bSml29623 available, left, len, (int)MBLKL(nmp))); 155730ac2e7bSml29623 datamp = datamp->b_cont; 155830ac2e7bSml29623 rptr = datamp->b_rptr; 155930ac2e7bSml29623 } else { 156030ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 156130ac2e7bSml29623 "(6)available b_cont == NULL : %d, " 156230ac2e7bSml29623 "left: %d, len: %d, MBLKL(nmp): %d", 156330ac2e7bSml29623 available, left, len, (int)MBLKL(nmp))); 156430ac2e7bSml29623 156530ac2e7bSml29623 VERIFY(cmp->b_next == NULL); 156630ac2e7bSml29623 VERIFY(left == 0); 156730ac2e7bSml29623 break; /* Done! */ 156830ac2e7bSml29623 } 156930ac2e7bSml29623 } 157030ac2e7bSml29623 cmp = cmp->b_next; 157130ac2e7bSml29623 157230ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 157330ac2e7bSml29623 "(7) do_softlso: " 157430ac2e7bSml29623 "next mp in mp_chain available len != 0 : %d, " 157530ac2e7bSml29623 "left: %d, len: %d, MBLKL(nmp): %d", 157630ac2e7bSml29623 available, left, len, (int)MBLKL(nmp))); 157730ac2e7bSml29623 } 157830ac2e7bSml29623 157930ac2e7bSml29623 /* 158030ac2e7bSml29623 * From now, start to fill up all headers for the first message 158130ac2e7bSml29623 * Hardware checksum flags need to be updated separately for FULLCKSUM 158230ac2e7bSml29623 * and PARTIALCKSUM cases. For full checksum, copy the original flags 158330ac2e7bSml29623 * into every new packet is enough. But for HCK_PARTIALCKSUM, all 158430ac2e7bSml29623 * required fields need to be updated properly. 158530ac2e7bSml29623 */ 158630ac2e7bSml29623 nmp = mp_chain; 158730ac2e7bSml29623 bcopy(mp->b_rptr, nmp->b_rptr, hdrlen); 158830ac2e7bSml29623 nmp->b_wptr = nmp->b_rptr + hdrlen; 158930ac2e7bSml29623 niph = (struct ip *)(nmp->b_rptr + ehlen); 159030ac2e7bSml29623 niph->ip_len = htons(mss + iphlen + tcphlen); 159130ac2e7bSml29623 ip_id = ntohs(niph->ip_id); 159230ac2e7bSml29623 ntcph = (struct tcphdr *)(nmp->b_rptr + ehlen + iphlen); 159330ac2e7bSml29623 #ifdef __sparc 159430ac2e7bSml29623 bcopy((char *)&ntcph->th_seq, &tcp_seq_tmp, 4); 159530ac2e7bSml29623 tcp_seq = ntohl(tcp_seq_tmp); 159630ac2e7bSml29623 #else 159730ac2e7bSml29623 tcp_seq = ntohl(ntcph->th_seq); 159830ac2e7bSml29623 #endif 159930ac2e7bSml29623 160030ac2e7bSml29623 ntcph->th_flags &= ~(TH_FIN | TH_PUSH | TH_RST); 160130ac2e7bSml29623 160230ac2e7bSml29623 DB_CKSUMFLAGS(nmp) = (uint16_t)hckflags; 160330ac2e7bSml29623 DB_CKSUMSTART(nmp) = start_offset; 160430ac2e7bSml29623 DB_CKSUMSTUFF(nmp) = stuff_offset; 160530ac2e7bSml29623 160630ac2e7bSml29623 /* calculate IP checksum and TCP pseudo header checksum */ 160730ac2e7bSml29623 niph->ip_sum = 0; 160830ac2e7bSml29623 niph->ip_sum = (uint16_t)nxge_csgen((uint16_t *)niph, iphlen); 160930ac2e7bSml29623 161030ac2e7bSml29623 l4_len = mss + tcphlen; 161130ac2e7bSml29623 sum = htons(l4_len) + l4cksum; 161230ac2e7bSml29623 sum = (sum & 0xFFFF) + (sum >> 16); 161330ac2e7bSml29623 ntcph->th_sum = (sum & 0xffff); 161430ac2e7bSml29623 161530ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 161630ac2e7bSml29623 "==> nxge_do_softlso: first mp $%p (mp_chain $%p) " 161730ac2e7bSml29623 "mss %d pktlen %d l4_len %d (0x%x) " 161830ac2e7bSml29623 "MBLKL(mp): %d, MBLKL(datamp): %d " 161930ac2e7bSml29623 "ip_sum 0x%x " 162030ac2e7bSml29623 "th_sum 0x%x sum 0x%x ) " 162130ac2e7bSml29623 "dump first ip->tcp %s", 162230ac2e7bSml29623 nmp, mp_chain, 162330ac2e7bSml29623 mss, 162430ac2e7bSml29623 pktlen, 162530ac2e7bSml29623 l4_len, 162630ac2e7bSml29623 l4_len, 162730ac2e7bSml29623 (int)MBLKL(mp), (int)MBLKL(datamp), 162830ac2e7bSml29623 niph->ip_sum, 162930ac2e7bSml29623 ntcph->th_sum, 163030ac2e7bSml29623 sum, 163130ac2e7bSml29623 nxge_dump_packet((char *)niph, 52))); 163230ac2e7bSml29623 163330ac2e7bSml29623 cmp = nmp; 163430ac2e7bSml29623 while ((nmp = nmp->b_next)->b_next != NULL) { 163530ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 163630ac2e7bSml29623 "==>nxge_do_softlso: middle l4_len %d ", l4_len)); 163730ac2e7bSml29623 bcopy(cmp->b_rptr, nmp->b_rptr, hdrlen); 163830ac2e7bSml29623 nmp->b_wptr = nmp->b_rptr + hdrlen; 163930ac2e7bSml29623 niph = (struct ip *)(nmp->b_rptr + ehlen); 164030ac2e7bSml29623 niph->ip_id = htons(++ip_id); 164130ac2e7bSml29623 niph->ip_len = htons(mss + iphlen + tcphlen); 164230ac2e7bSml29623 ntcph = (struct tcphdr *)(nmp->b_rptr + ehlen + iphlen); 164330ac2e7bSml29623 tcp_seq += mss; 164430ac2e7bSml29623 164530ac2e7bSml29623 ntcph->th_flags &= ~(TH_FIN | TH_PUSH | TH_RST | TH_URG); 164630ac2e7bSml29623 164730ac2e7bSml29623 #ifdef __sparc 164830ac2e7bSml29623 tcp_seq_tmp = htonl(tcp_seq); 164930ac2e7bSml29623 bcopy(&tcp_seq_tmp, (char *)&ntcph->th_seq, 4); 165030ac2e7bSml29623 #else 165130ac2e7bSml29623 ntcph->th_seq = htonl(tcp_seq); 165230ac2e7bSml29623 #endif 165330ac2e7bSml29623 DB_CKSUMFLAGS(nmp) = (uint16_t)hckflags; 165430ac2e7bSml29623 DB_CKSUMSTART(nmp) = start_offset; 165530ac2e7bSml29623 DB_CKSUMSTUFF(nmp) = stuff_offset; 165630ac2e7bSml29623 165730ac2e7bSml29623 /* calculate IP checksum and TCP pseudo header checksum */ 165830ac2e7bSml29623 niph->ip_sum = 0; 165930ac2e7bSml29623 niph->ip_sum = (uint16_t)nxge_csgen((uint16_t *)niph, iphlen); 166030ac2e7bSml29623 ntcph->th_sum = (sum & 0xffff); 166130ac2e7bSml29623 166230ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 166330ac2e7bSml29623 "==> nxge_do_softlso: middle ip_sum 0x%x " 166430ac2e7bSml29623 "th_sum 0x%x " 166530ac2e7bSml29623 " mp $%p (mp_chain $%p) pktlen %d " 166630ac2e7bSml29623 "MBLKL(mp): %d, MBLKL(datamp): %d ", 166730ac2e7bSml29623 niph->ip_sum, 166830ac2e7bSml29623 ntcph->th_sum, 166930ac2e7bSml29623 nmp, mp_chain, 167030ac2e7bSml29623 pktlen, (int)MBLKL(mp), (int)MBLKL(datamp))); 167130ac2e7bSml29623 } 167230ac2e7bSml29623 167330ac2e7bSml29623 /* Last segment */ 167430ac2e7bSml29623 /* 167530ac2e7bSml29623 * Set FIN and/or PSH flags if present only in the last packet. 167630ac2e7bSml29623 * The ip_len could be different from prior packets. 167730ac2e7bSml29623 */ 167830ac2e7bSml29623 bcopy(cmp->b_rptr, nmp->b_rptr, hdrlen); 167930ac2e7bSml29623 nmp->b_wptr = nmp->b_rptr + hdrlen; 168030ac2e7bSml29623 niph = (struct ip *)(nmp->b_rptr + ehlen); 168130ac2e7bSml29623 niph->ip_id = htons(++ip_id); 168230ac2e7bSml29623 niph->ip_len = htons(msgsize(nmp->b_cont) + iphlen + tcphlen); 168330ac2e7bSml29623 ntcph = (struct tcphdr *)(nmp->b_rptr + ehlen + iphlen); 168430ac2e7bSml29623 tcp_seq += mss; 168530ac2e7bSml29623 #ifdef __sparc 168630ac2e7bSml29623 tcp_seq_tmp = htonl(tcp_seq); 168730ac2e7bSml29623 bcopy(&tcp_seq_tmp, (char *)&ntcph->th_seq, 4); 168830ac2e7bSml29623 #else 168930ac2e7bSml29623 ntcph->th_seq = htonl(tcp_seq); 169030ac2e7bSml29623 #endif 169130ac2e7bSml29623 ntcph->th_flags = (otcph->th_flags & ~TH_URG); 169230ac2e7bSml29623 169330ac2e7bSml29623 DB_CKSUMFLAGS(nmp) = (uint16_t)hckflags; 169430ac2e7bSml29623 DB_CKSUMSTART(nmp) = start_offset; 169530ac2e7bSml29623 DB_CKSUMSTUFF(nmp) = stuff_offset; 169630ac2e7bSml29623 169730ac2e7bSml29623 /* calculate IP checksum and TCP pseudo header checksum */ 169830ac2e7bSml29623 niph->ip_sum = 0; 169930ac2e7bSml29623 niph->ip_sum = (uint16_t)nxge_csgen((uint16_t *)niph, iphlen); 170030ac2e7bSml29623 170130ac2e7bSml29623 l4_len = ntohs(niph->ip_len) - iphlen; 170230ac2e7bSml29623 sum = htons(l4_len) + l4cksum; 170330ac2e7bSml29623 sum = (sum & 0xFFFF) + (sum >> 16); 170430ac2e7bSml29623 ntcph->th_sum = (sum & 0xffff); 170530ac2e7bSml29623 170630ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 170730ac2e7bSml29623 "==> nxge_do_softlso: last next " 170830ac2e7bSml29623 "niph->ip_sum 0x%x " 170930ac2e7bSml29623 "ntcph->th_sum 0x%x sum 0x%x " 171030ac2e7bSml29623 "dump last ip->tcp %s " 171130ac2e7bSml29623 "cmp $%p mp $%p (mp_chain $%p) pktlen %d (0x%x) " 171230ac2e7bSml29623 "l4_len %d (0x%x) " 171330ac2e7bSml29623 "MBLKL(mp): %d, MBLKL(datamp): %d ", 171430ac2e7bSml29623 niph->ip_sum, 171530ac2e7bSml29623 ntcph->th_sum, sum, 171630ac2e7bSml29623 nxge_dump_packet((char *)niph, 52), 171730ac2e7bSml29623 cmp, nmp, mp_chain, 171830ac2e7bSml29623 pktlen, pktlen, 171930ac2e7bSml29623 l4_len, 172030ac2e7bSml29623 l4_len, 172130ac2e7bSml29623 (int)MBLKL(mp), (int)MBLKL(datamp))); 172230ac2e7bSml29623 172330ac2e7bSml29623 cleanup_allocated_msgs: 172430ac2e7bSml29623 if (do_cleanup) { 172530ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 172630ac2e7bSml29623 "==> nxge_do_softlso: " 172730ac2e7bSml29623 "Failed allocating messages, " 172830ac2e7bSml29623 "have to clean up and fail!")); 172930ac2e7bSml29623 while (mp_chain != NULL) { 173030ac2e7bSml29623 nmp = mp_chain; 173130ac2e7bSml29623 mp_chain = mp_chain->b_next; 173230ac2e7bSml29623 freemsg(nmp); 173330ac2e7bSml29623 } 173430ac2e7bSml29623 } 173530ac2e7bSml29623 /* 173630ac2e7bSml29623 * We're done here, so just free the original message and return the 173730ac2e7bSml29623 * new message chain, that could be NULL if failed, back to the caller. 173830ac2e7bSml29623 */ 173930ac2e7bSml29623 freemsg(mp); 174030ac2e7bSml29623 174130ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 174230ac2e7bSml29623 "<== nxge_do_softlso:mp_chain $%p", mp_chain)); 174330ac2e7bSml29623 return (mp_chain); 174430ac2e7bSml29623 } 174530ac2e7bSml29623 174630ac2e7bSml29623 /* 174730ac2e7bSml29623 * Will be called before NIC driver do further operation on the message. 174830ac2e7bSml29623 * The input message may include LSO information, if so, go to softlso logic 174930ac2e7bSml29623 * to eliminate the oversized LSO packet for the incapable underlying h/w. 175030ac2e7bSml29623 * The return could be the same non-LSO message or a message chain for LSO case. 175130ac2e7bSml29623 * 175230ac2e7bSml29623 * The driver needs to call this function per packet and process the whole chain 175330ac2e7bSml29623 * if applied. 175430ac2e7bSml29623 */ 175530ac2e7bSml29623 static mblk_t * 175630ac2e7bSml29623 nxge_lso_eliminate(mblk_t *mp) 175730ac2e7bSml29623 { 175830ac2e7bSml29623 uint32_t lsoflags; 175930ac2e7bSml29623 uint32_t mss; 176030ac2e7bSml29623 176130ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 176230ac2e7bSml29623 "==>nxge_lso_eliminate:")); 176330ac2e7bSml29623 nxge_lso_info_get(mp, &mss, &lsoflags); 176430ac2e7bSml29623 176530ac2e7bSml29623 if (lsoflags & HW_LSO) { 176630ac2e7bSml29623 mblk_t *nmp; 176730ac2e7bSml29623 176830ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 176930ac2e7bSml29623 "==>nxge_lso_eliminate:" 177030ac2e7bSml29623 "HW_LSO:mss %d mp $%p", 177130ac2e7bSml29623 mss, mp)); 177230ac2e7bSml29623 if ((nmp = nxge_do_softlso(mp, mss)) != NULL) { 177330ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 177430ac2e7bSml29623 "<== nxge_lso_eliminate: " 177530ac2e7bSml29623 "LSO: nmp not NULL nmp $%p mss %d mp $%p", 177630ac2e7bSml29623 nmp, mss, mp)); 177730ac2e7bSml29623 return (nmp); 177830ac2e7bSml29623 } else { 177930ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 178030ac2e7bSml29623 "<== nxge_lso_eliminate_ " 178130ac2e7bSml29623 "LSO: failed nmp NULL nmp $%p mss %d mp $%p", 178230ac2e7bSml29623 nmp, mss, mp)); 178330ac2e7bSml29623 return (NULL); 178430ac2e7bSml29623 } 178530ac2e7bSml29623 } 178630ac2e7bSml29623 178730ac2e7bSml29623 NXGE_DEBUG_MSG((NULL, TX_CTL, 178830ac2e7bSml29623 "<== nxge_lso_eliminate")); 178930ac2e7bSml29623 return (mp); 179030ac2e7bSml29623 } 179130ac2e7bSml29623 179230ac2e7bSml29623 static uint32_t 179330ac2e7bSml29623 nxge_csgen(uint16_t *adr, int len) 179430ac2e7bSml29623 { 179530ac2e7bSml29623 int i, odd; 179630ac2e7bSml29623 uint32_t sum = 0; 179730ac2e7bSml29623 uint32_t c = 0; 179830ac2e7bSml29623 179930ac2e7bSml29623 odd = len % 2; 180030ac2e7bSml29623 for (i = 0; i < (len / 2); i++) { 180130ac2e7bSml29623 sum += (adr[i] & 0xffff); 180230ac2e7bSml29623 } 180330ac2e7bSml29623 if (odd) { 180430ac2e7bSml29623 sum += adr[len / 2] & 0xff00; 180530ac2e7bSml29623 } 180630ac2e7bSml29623 while ((c = ((sum & 0xffff0000) >> 16)) != 0) { 180730ac2e7bSml29623 sum &= 0xffff; 180830ac2e7bSml29623 sum += c; 180930ac2e7bSml29623 } 181030ac2e7bSml29623 return (~sum & 0xffff); 181130ac2e7bSml29623 } 1812