19da57d7bSbt150084 /* 29da57d7bSbt150084 * CDDL HEADER START 39da57d7bSbt150084 * 49da57d7bSbt150084 * The contents of this file are subject to the terms of the 59da57d7bSbt150084 * Common Development and Distribution License (the "License"). 69da57d7bSbt150084 * You may not use this file except in compliance with the License. 79da57d7bSbt150084 * 8da14cebeSEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9da14cebeSEric Cheng * or http://www.opensolaris.org/os/licensing. 109da57d7bSbt150084 * See the License for the specific language governing permissions 119da57d7bSbt150084 * and limitations under the License. 129da57d7bSbt150084 * 13da14cebeSEric Cheng * When distributing Covered Code, include this CDDL HEADER in each 14da14cebeSEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 159da57d7bSbt150084 * If applicable, add the following below this CDDL HEADER, with the 169da57d7bSbt150084 * fields enclosed by brackets "[]" replaced with your own identifying 179da57d7bSbt150084 * information: Portions Copyright [yyyy] [name of copyright owner] 189da57d7bSbt150084 * 199da57d7bSbt150084 * CDDL HEADER END 209da57d7bSbt150084 */ 219da57d7bSbt150084 229da57d7bSbt150084 /* 235b6dd21fSchenlu chen - Sun Microsystems - Beijing China * Copyright(c) 2007-2010 Intel Corporation. All rights reserved. 245b6dd21fSchenlu chen - Sun Microsystems - Beijing China */ 255b6dd21fSchenlu chen - Sun Microsystems - Beijing China 265b6dd21fSchenlu chen - Sun Microsystems - Beijing China /* 275b6dd21fSchenlu chen - Sun Microsystems - Beijing China * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 2869b5a878SDan McDonald * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 297e579c30SDale Ghent * Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved. 30abcd9e32SRyan Zezeski * Copyright 2017 Joyent, Inc. 31da14cebeSEric Cheng */ 329da57d7bSbt150084 339da57d7bSbt150084 #include "ixgbe_sw.h" 349da57d7bSbt150084 359da57d7bSbt150084 static int ixgbe_tx_copy(ixgbe_tx_ring_t *, tx_control_block_t *, mblk_t *, 36c971fb7eSgg161487 uint32_t, boolean_t); 379da57d7bSbt150084 static int ixgbe_tx_bind(ixgbe_tx_ring_t *, tx_control_block_t *, mblk_t *, 389da57d7bSbt150084 uint32_t); 399da57d7bSbt150084 static int ixgbe_tx_fill_ring(ixgbe_tx_ring_t *, link_list_t *, 40c971fb7eSgg161487 ixgbe_tx_context_t *, size_t); 419da57d7bSbt150084 static void ixgbe_save_desc(tx_control_block_t *, uint64_t, size_t); 429da57d7bSbt150084 static tx_control_block_t *ixgbe_get_free_list(ixgbe_tx_ring_t *); 439da57d7bSbt150084 44c971fb7eSgg161487 static int ixgbe_get_context(mblk_t *, ixgbe_tx_context_t *); 45c971fb7eSgg161487 static boolean_t ixgbe_check_context(ixgbe_tx_ring_t *, 46c971fb7eSgg161487 ixgbe_tx_context_t *); 47c971fb7eSgg161487 static void ixgbe_fill_context(struct ixgbe_adv_tx_context_desc *, 4873cd555cSBin Tu - Sun Microsystems - Beijing China ixgbe_tx_context_t *); 499da57d7bSbt150084 509da57d7bSbt150084 #ifndef IXGBE_DEBUG 519da57d7bSbt150084 #pragma inline(ixgbe_save_desc) 52c971fb7eSgg161487 #pragma inline(ixgbe_get_context) 53c971fb7eSgg161487 #pragma inline(ixgbe_check_context) 54c971fb7eSgg161487 #pragma inline(ixgbe_fill_context) 559da57d7bSbt150084 #endif 569da57d7bSbt150084 579da57d7bSbt150084 /* 58da14cebeSEric Cheng * ixgbe_ring_tx 599da57d7bSbt150084 * 60da14cebeSEric Cheng * To transmit one mblk through one specified ring. 619da57d7bSbt150084 * 629da57d7bSbt150084 * One mblk can consist of several fragments, each fragment 639da57d7bSbt150084 * will be processed with different methods based on the size. 649da57d7bSbt150084 * For the fragments with size less than the bcopy threshold, 659da57d7bSbt150084 * they will be processed by using bcopy; otherwise, they will 669da57d7bSbt150084 * be processed by using DMA binding. 679da57d7bSbt150084 * 689da57d7bSbt150084 * To process the mblk, a tx control block is got from the 699da57d7bSbt150084 * free list. One tx control block contains one tx buffer, which 709da57d7bSbt150084 * is used to copy mblk fragments' data; and one tx DMA handle, 719da57d7bSbt150084 * which is used to bind a mblk fragment with DMA resource. 729da57d7bSbt150084 * 739da57d7bSbt150084 * Several small mblk fragments can be copied into one tx control 749da57d7bSbt150084 * block's buffer, and then the buffer will be transmitted with 759da57d7bSbt150084 * one tx descriptor. 769da57d7bSbt150084 * 779da57d7bSbt150084 * A large fragment only binds with one tx control block's DMA 789da57d7bSbt150084 * handle, and it can span several tx descriptors for transmitting. 799da57d7bSbt150084 * 809da57d7bSbt150084 * So to transmit a packet (mblk), several tx control blocks can 819da57d7bSbt150084 * be used. After the processing, those tx control blocks will 829da57d7bSbt150084 * be put to the work list. 839da57d7bSbt150084 */ 84da14cebeSEric Cheng mblk_t * 85da14cebeSEric Cheng ixgbe_ring_tx(void *arg, mblk_t *mp) 869da57d7bSbt150084 { 87da14cebeSEric Cheng ixgbe_tx_ring_t *tx_ring = (ixgbe_tx_ring_t *)arg; 889da57d7bSbt150084 ixgbe_t *ixgbe = tx_ring->ixgbe; 899da57d7bSbt150084 tx_type_t current_flag, next_flag; 909da57d7bSbt150084 uint32_t current_len, next_len; 919da57d7bSbt150084 uint32_t desc_total; 929da57d7bSbt150084 size_t mbsize; 939da57d7bSbt150084 int desc_num; 949da57d7bSbt150084 boolean_t copy_done, eop; 95185c5677SPaul Guo mblk_t *current_mp, *next_mp, *nmp, *pull_mp = NULL; 969da57d7bSbt150084 tx_control_block_t *tcb; 97c971fb7eSgg161487 ixgbe_tx_context_t tx_context, *ctx; 989da57d7bSbt150084 link_list_t pending_list; 99da14cebeSEric Cheng uint32_t len, hdr_frag_len, hdr_len; 100da14cebeSEric Cheng uint32_t copy_thresh; 1011fedc51fSWinson Wang - Sun Microsystems - Beijing China mblk_t *hdr_new_mp = NULL; 1021fedc51fSWinson Wang - Sun Microsystems - Beijing China mblk_t *hdr_pre_mp = NULL; 1031fedc51fSWinson Wang - Sun Microsystems - Beijing China mblk_t *hdr_nmp = NULL; 104da14cebeSEric Cheng 105da14cebeSEric Cheng ASSERT(mp->b_next == NULL); 106da14cebeSEric Cheng 10762e6e1adSPaul Guo if ((ixgbe->ixgbe_state & IXGBE_SUSPENDED) || 10862e6e1adSPaul Guo (ixgbe->ixgbe_state & IXGBE_ERROR) || 1095b6dd21fSchenlu chen - Sun Microsystems - Beijing China (ixgbe->ixgbe_state & IXGBE_OVERTEMP) || 110b607c8a3SKeith M Wesolowski !(ixgbe->ixgbe_state & IXGBE_STARTED) || 111b607c8a3SKeith M Wesolowski ixgbe->link_state != LINK_STATE_UP) { 112b607c8a3SKeith M Wesolowski freemsg(mp); 113b607c8a3SKeith M Wesolowski return (NULL); 11462e6e1adSPaul Guo } 11562e6e1adSPaul Guo 116ea65739eSchenlu chen - Sun Microsystems - Beijing China copy_thresh = ixgbe->tx_copy_thresh; 1179da57d7bSbt150084 1189da57d7bSbt150084 /* Get the mblk size */ 1199da57d7bSbt150084 mbsize = 0; 1209da57d7bSbt150084 for (nmp = mp; nmp != NULL; nmp = nmp->b_cont) { 121da14cebeSEric Cheng mbsize += MBLKL(nmp); 1229da57d7bSbt150084 } 1239da57d7bSbt150084 124c971fb7eSgg161487 if (ixgbe->tx_hcksum_enable) { 1259da57d7bSbt150084 /* 126c971fb7eSgg161487 * Retrieve checksum context information from the mblk 127c971fb7eSgg161487 * that will be used to decide whether/how to fill the 128c971fb7eSgg161487 * context descriptor. 1299da57d7bSbt150084 */ 130c971fb7eSgg161487 ctx = &tx_context; 131c971fb7eSgg161487 if (ixgbe_get_context(mp, ctx) < 0) { 132c971fb7eSgg161487 freemsg(mp); 133da14cebeSEric Cheng return (NULL); 134c971fb7eSgg161487 } 135c971fb7eSgg161487 136c971fb7eSgg161487 /* 137c971fb7eSgg161487 * If the mblk size exceeds the max size ixgbe could 138da14cebeSEric Cheng * process, then discard this mblk, and return NULL. 139c971fb7eSgg161487 */ 14073cd555cSBin Tu - Sun Microsystems - Beijing China if ((ctx->lso_flag && 14173cd555cSBin Tu - Sun Microsystems - Beijing China ((mbsize - ctx->mac_hdr_len) > IXGBE_LSO_MAXLEN)) || 14273cd555cSBin Tu - Sun Microsystems - Beijing China (!ctx->lso_flag && 143c971fb7eSgg161487 (mbsize > (ixgbe->max_frame_size - ETHERFCSL)))) { 1449da57d7bSbt150084 freemsg(mp); 1459da57d7bSbt150084 IXGBE_DEBUGLOG_0(ixgbe, "ixgbe_tx: packet oversize"); 146da14cebeSEric Cheng return (NULL); 1479da57d7bSbt150084 } 148c971fb7eSgg161487 } else { 149c971fb7eSgg161487 ctx = NULL; 150c971fb7eSgg161487 } 151c971fb7eSgg161487 1529da57d7bSbt150084 /* 1539da57d7bSbt150084 * Check and recycle tx descriptors. 1549da57d7bSbt150084 * The recycle threshold here should be selected carefully 1559da57d7bSbt150084 */ 156ea65739eSchenlu chen - Sun Microsystems - Beijing China if (tx_ring->tbd_free < ixgbe->tx_recycle_thresh) { 1579da57d7bSbt150084 tx_ring->tx_recycle(tx_ring); 15873cd555cSBin Tu - Sun Microsystems - Beijing China } 1599da57d7bSbt150084 1609da57d7bSbt150084 /* 1619da57d7bSbt150084 * After the recycling, if the tbd_free is less than the 162da14cebeSEric Cheng * overload_threshold, assert overload, return mp; 1639da57d7bSbt150084 * and we need to re-schedule the tx again. 1649da57d7bSbt150084 */ 165ea65739eSchenlu chen - Sun Microsystems - Beijing China if (tx_ring->tbd_free < ixgbe->tx_overload_thresh) { 1669da57d7bSbt150084 tx_ring->reschedule = B_TRUE; 167abcd9e32SRyan Zezeski tx_ring->stat_overload++; 168da14cebeSEric Cheng return (mp); 1699da57d7bSbt150084 } 1709da57d7bSbt150084 1719da57d7bSbt150084 /* 1729da57d7bSbt150084 * The pending_list is a linked list that is used to save 1739da57d7bSbt150084 * the tx control blocks that have packet data processed 1749da57d7bSbt150084 * but have not put the data to the tx descriptor ring. 1759da57d7bSbt150084 * It is used to reduce the lock contention of the tx_lock. 1769da57d7bSbt150084 */ 1779da57d7bSbt150084 LINK_LIST_INIT(&pending_list); 1789da57d7bSbt150084 desc_num = 0; 1799da57d7bSbt150084 desc_total = 0; 1809da57d7bSbt150084 181da14cebeSEric Cheng /* 182da14cebeSEric Cheng * The software should guarantee LSO packet header(MAC+IP+TCP) 183da14cebeSEric Cheng * to be within one descriptor. Here we reallocate and refill the 184da14cebeSEric Cheng * the header if it's physical memory non-contiguous. 185da14cebeSEric Cheng */ 186da14cebeSEric Cheng if ((ctx != NULL) && ctx->lso_flag) { 187da14cebeSEric Cheng /* find the last fragment of the header */ 188da14cebeSEric Cheng len = MBLKL(mp); 189da14cebeSEric Cheng ASSERT(len > 0); 1901fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_nmp = mp; 191da14cebeSEric Cheng hdr_len = ctx->ip_hdr_len + ctx->mac_hdr_len + ctx->l4_hdr_len; 192da14cebeSEric Cheng while (len < hdr_len) { 1931fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_pre_mp = hdr_nmp; 1941fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_nmp = hdr_nmp->b_cont; 1951fedc51fSWinson Wang - Sun Microsystems - Beijing China len += MBLKL(hdr_nmp); 196da14cebeSEric Cheng } 197da14cebeSEric Cheng /* 198da14cebeSEric Cheng * If the header and the payload are in different mblks, 199da14cebeSEric Cheng * we simply force the header to be copied into pre-allocated 200da14cebeSEric Cheng * page-aligned buffer. 201da14cebeSEric Cheng */ 202da14cebeSEric Cheng if (len == hdr_len) 203da14cebeSEric Cheng goto adjust_threshold; 204da14cebeSEric Cheng 2051fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_frag_len = hdr_len - (len - MBLKL(hdr_nmp)); 206da14cebeSEric Cheng /* 207da14cebeSEric Cheng * There are two cases we need to reallocate a mblk for the 208da14cebeSEric Cheng * last header fragment: 209da14cebeSEric Cheng * 1. the header is in multiple mblks and the last fragment 210da14cebeSEric Cheng * share the same mblk with the payload 211da14cebeSEric Cheng * 2. the header is in a single mblk shared with the payload 212da14cebeSEric Cheng * and the header is physical memory non-contiguous 213da14cebeSEric Cheng */ 2141fedc51fSWinson Wang - Sun Microsystems - Beijing China if ((hdr_nmp != mp) || 2151fedc51fSWinson Wang - Sun Microsystems - Beijing China (P2NPHASE((uintptr_t)hdr_nmp->b_rptr, ixgbe->sys_page_size) 21673cd555cSBin Tu - Sun Microsystems - Beijing China < hdr_len)) { 217*7a77a74aSRyan Zezeski tx_ring->stat_lso_header_fail++; 218da14cebeSEric Cheng /* 219da14cebeSEric Cheng * reallocate the mblk for the last header fragment, 220da14cebeSEric Cheng * expect to bcopy into pre-allocated page-aligned 221da14cebeSEric Cheng * buffer 222da14cebeSEric Cheng */ 2231fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_new_mp = allocb(hdr_frag_len, NULL); 2241fedc51fSWinson Wang - Sun Microsystems - Beijing China if (!hdr_new_mp) 22573cd555cSBin Tu - Sun Microsystems - Beijing China return (mp); 2261fedc51fSWinson Wang - Sun Microsystems - Beijing China bcopy(hdr_nmp->b_rptr, hdr_new_mp->b_rptr, 2271fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_frag_len); 228da14cebeSEric Cheng /* link the new header fragment with the other parts */ 2291fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_new_mp->b_wptr = hdr_new_mp->b_rptr + hdr_frag_len; 2301fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_new_mp->b_cont = hdr_nmp; 2311fedc51fSWinson Wang - Sun Microsystems - Beijing China if (hdr_pre_mp) 2321fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_pre_mp->b_cont = hdr_new_mp; 2331fedc51fSWinson Wang - Sun Microsystems - Beijing China else 2341fedc51fSWinson Wang - Sun Microsystems - Beijing China mp = hdr_new_mp; 2351fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_nmp->b_rptr += hdr_frag_len; 236da14cebeSEric Cheng } 237da14cebeSEric Cheng adjust_threshold: 238da14cebeSEric Cheng /* 239da14cebeSEric Cheng * adjust the bcopy threshhold to guarantee 240da14cebeSEric Cheng * the header to use bcopy way 241da14cebeSEric Cheng */ 242da14cebeSEric Cheng if (copy_thresh < hdr_len) 243da14cebeSEric Cheng copy_thresh = hdr_len; 244da14cebeSEric Cheng } 245da14cebeSEric Cheng 2469da57d7bSbt150084 current_mp = mp; 247da14cebeSEric Cheng current_len = MBLKL(current_mp); 2489da57d7bSbt150084 /* 2499da57d7bSbt150084 * Decide which method to use for the first fragment 2509da57d7bSbt150084 */ 251da14cebeSEric Cheng current_flag = (current_len <= copy_thresh) ? 2529da57d7bSbt150084 USE_COPY : USE_DMA; 2539da57d7bSbt150084 /* 2549da57d7bSbt150084 * If the mblk includes several contiguous small fragments, 2559da57d7bSbt150084 * they may be copied into one buffer. This flag is used to 2569da57d7bSbt150084 * indicate whether there are pending fragments that need to 2579da57d7bSbt150084 * be copied to the current tx buffer. 2589da57d7bSbt150084 * 2599da57d7bSbt150084 * If this flag is B_TRUE, it indicates that a new tx control 2609da57d7bSbt150084 * block is needed to process the next fragment using either 2619da57d7bSbt150084 * copy or DMA binding. 2629da57d7bSbt150084 * 2639da57d7bSbt150084 * Otherwise, it indicates that the next fragment will be 2649da57d7bSbt150084 * copied to the current tx buffer that is maintained by the 2659da57d7bSbt150084 * current tx control block. No new tx control block is needed. 2669da57d7bSbt150084 */ 2679da57d7bSbt150084 copy_done = B_TRUE; 2689da57d7bSbt150084 while (current_mp) { 2699da57d7bSbt150084 next_mp = current_mp->b_cont; 2709da57d7bSbt150084 eop = (next_mp == NULL); /* Last fragment of the packet? */ 271da14cebeSEric Cheng next_len = eop ? 0: MBLKL(next_mp); 2729da57d7bSbt150084 2739da57d7bSbt150084 /* 2749da57d7bSbt150084 * When the current fragment is an empty fragment, if 2759da57d7bSbt150084 * the next fragment will still be copied to the current 2769da57d7bSbt150084 * tx buffer, we cannot skip this fragment here. Because 2779da57d7bSbt150084 * the copy processing is pending for completion. We have 2789da57d7bSbt150084 * to process this empty fragment in the tx_copy routine. 2799da57d7bSbt150084 * 2809da57d7bSbt150084 * If the copy processing is completed or a DMA binding 2819da57d7bSbt150084 * processing is just completed, we can just skip this 2829da57d7bSbt150084 * empty fragment. 2839da57d7bSbt150084 */ 2849da57d7bSbt150084 if ((current_len == 0) && (copy_done)) { 2859da57d7bSbt150084 current_mp = next_mp; 2869da57d7bSbt150084 current_len = next_len; 287da14cebeSEric Cheng current_flag = (current_len <= copy_thresh) ? 2889da57d7bSbt150084 USE_COPY : USE_DMA; 2899da57d7bSbt150084 continue; 2909da57d7bSbt150084 } 2919da57d7bSbt150084 2929da57d7bSbt150084 if (copy_done) { 2939da57d7bSbt150084 /* 2949da57d7bSbt150084 * Get a new tx control block from the free list 2959da57d7bSbt150084 */ 2969da57d7bSbt150084 tcb = ixgbe_get_free_list(tx_ring); 2979da57d7bSbt150084 2989da57d7bSbt150084 if (tcb == NULL) { 299abcd9e32SRyan Zezeski tx_ring->stat_fail_no_tcb++; 3009da57d7bSbt150084 goto tx_failure; 3019da57d7bSbt150084 } 3029da57d7bSbt150084 3039da57d7bSbt150084 /* 3049da57d7bSbt150084 * Push the tx control block to the pending list 3059da57d7bSbt150084 * to avoid using lock too early 3069da57d7bSbt150084 */ 3079da57d7bSbt150084 LIST_PUSH_TAIL(&pending_list, &tcb->link); 3089da57d7bSbt150084 } 3099da57d7bSbt150084 3109da57d7bSbt150084 if (current_flag == USE_COPY) { 3119da57d7bSbt150084 /* 3129da57d7bSbt150084 * Check whether to use bcopy or DMA binding to process 3139da57d7bSbt150084 * the next fragment, and if using bcopy, whether we 3149da57d7bSbt150084 * need to continue copying the next fragment into the 3159da57d7bSbt150084 * current tx buffer. 3169da57d7bSbt150084 */ 3179da57d7bSbt150084 ASSERT((tcb->tx_buf.len + current_len) <= 3189da57d7bSbt150084 tcb->tx_buf.size); 3199da57d7bSbt150084 3209da57d7bSbt150084 if (eop) { 3219da57d7bSbt150084 /* 3229da57d7bSbt150084 * This is the last fragment of the packet, so 3239da57d7bSbt150084 * the copy processing will be completed with 3249da57d7bSbt150084 * this fragment. 3259da57d7bSbt150084 */ 3269da57d7bSbt150084 next_flag = USE_NONE; 3279da57d7bSbt150084 copy_done = B_TRUE; 3289da57d7bSbt150084 } else if ((tcb->tx_buf.len + current_len + next_len) > 3299da57d7bSbt150084 tcb->tx_buf.size) { 3309da57d7bSbt150084 /* 3319da57d7bSbt150084 * If the next fragment is too large to be 3329da57d7bSbt150084 * copied to the current tx buffer, we need 3339da57d7bSbt150084 * to complete the current copy processing. 3349da57d7bSbt150084 */ 335da14cebeSEric Cheng next_flag = (next_len > copy_thresh) ? 3369da57d7bSbt150084 USE_DMA: USE_COPY; 3379da57d7bSbt150084 copy_done = B_TRUE; 338da14cebeSEric Cheng } else if (next_len > copy_thresh) { 3399da57d7bSbt150084 /* 3409da57d7bSbt150084 * The next fragment needs to be processed with 3419da57d7bSbt150084 * DMA binding. So the copy prcessing will be 3429da57d7bSbt150084 * completed with the current fragment. 3439da57d7bSbt150084 */ 3449da57d7bSbt150084 next_flag = USE_DMA; 3459da57d7bSbt150084 copy_done = B_TRUE; 3469da57d7bSbt150084 } else { 3479da57d7bSbt150084 /* 3489da57d7bSbt150084 * Continue to copy the next fragment to the 3499da57d7bSbt150084 * current tx buffer. 3509da57d7bSbt150084 */ 3519da57d7bSbt150084 next_flag = USE_COPY; 3529da57d7bSbt150084 copy_done = B_FALSE; 3539da57d7bSbt150084 } 3549da57d7bSbt150084 3559da57d7bSbt150084 desc_num = ixgbe_tx_copy(tx_ring, tcb, current_mp, 356c971fb7eSgg161487 current_len, copy_done); 3579da57d7bSbt150084 } else { 3589da57d7bSbt150084 /* 3599da57d7bSbt150084 * Check whether to use bcopy or DMA binding to process 3609da57d7bSbt150084 * the next fragment. 3619da57d7bSbt150084 */ 362da14cebeSEric Cheng next_flag = (next_len > copy_thresh) ? 3639da57d7bSbt150084 USE_DMA: USE_COPY; 3649da57d7bSbt150084 ASSERT(copy_done == B_TRUE); 3659da57d7bSbt150084 3669da57d7bSbt150084 desc_num = ixgbe_tx_bind(tx_ring, tcb, current_mp, 3679da57d7bSbt150084 current_len); 3689da57d7bSbt150084 } 3699da57d7bSbt150084 3709da57d7bSbt150084 if (desc_num > 0) 3719da57d7bSbt150084 desc_total += desc_num; 3729da57d7bSbt150084 else if (desc_num < 0) 3739da57d7bSbt150084 goto tx_failure; 3749da57d7bSbt150084 3759da57d7bSbt150084 current_mp = next_mp; 3769da57d7bSbt150084 current_len = next_len; 3779da57d7bSbt150084 current_flag = next_flag; 3789da57d7bSbt150084 } 3799da57d7bSbt150084 3809da57d7bSbt150084 /* 3819da57d7bSbt150084 * Attach the mblk to the last tx control block 3829da57d7bSbt150084 */ 3839da57d7bSbt150084 ASSERT(tcb); 3849da57d7bSbt150084 ASSERT(tcb->mp == NULL); 3859da57d7bSbt150084 tcb->mp = mp; 3869da57d7bSbt150084 3879da57d7bSbt150084 /* 388edf70dc9SPaul Guo * 82598/82599 chipset has a limitation that no more than 32 tx 389edf70dc9SPaul Guo * descriptors can be transmited out at one time. 390edf70dc9SPaul Guo * 391edf70dc9SPaul Guo * Here is a workaround for it: pull up the mblk then send it 392edf70dc9SPaul Guo * out with bind way. By doing so, no more than MAX_COOKIE (18) 393edf70dc9SPaul Guo * descriptors is needed. 394edf70dc9SPaul Guo */ 395edf70dc9SPaul Guo if (desc_total + 1 > IXGBE_TX_DESC_LIMIT) { 396*7a77a74aSRyan Zezeski tx_ring->stat_break_tbd_limit++; 397edf70dc9SPaul Guo 398edf70dc9SPaul Guo /* 399edf70dc9SPaul Guo * Discard the mblk and free the used resources 400edf70dc9SPaul Guo */ 401edf70dc9SPaul Guo tcb = (tx_control_block_t *)LIST_GET_HEAD(&pending_list); 402edf70dc9SPaul Guo while (tcb) { 403edf70dc9SPaul Guo tcb->mp = NULL; 404edf70dc9SPaul Guo ixgbe_free_tcb(tcb); 405edf70dc9SPaul Guo tcb = (tx_control_block_t *) 406edf70dc9SPaul Guo LIST_GET_NEXT(&pending_list, &tcb->link); 407edf70dc9SPaul Guo } 408edf70dc9SPaul Guo 409edf70dc9SPaul Guo /* 410edf70dc9SPaul Guo * Return the tx control blocks in the pending list to 411edf70dc9SPaul Guo * the free list. 412edf70dc9SPaul Guo */ 413edf70dc9SPaul Guo ixgbe_put_free_list(tx_ring, &pending_list); 414edf70dc9SPaul Guo 415edf70dc9SPaul Guo /* 416edf70dc9SPaul Guo * pull up the mblk and send it out with bind way 417edf70dc9SPaul Guo */ 418185c5677SPaul Guo if ((pull_mp = msgpullup(mp, -1)) == NULL) { 419185c5677SPaul Guo tx_ring->reschedule = B_TRUE; 4201fedc51fSWinson Wang - Sun Microsystems - Beijing China 4211fedc51fSWinson Wang - Sun Microsystems - Beijing China /* 4221fedc51fSWinson Wang - Sun Microsystems - Beijing China * If new mblk has been allocted for the last header 4231fedc51fSWinson Wang - Sun Microsystems - Beijing China * fragment of a LSO packet, we should restore the 4241fedc51fSWinson Wang - Sun Microsystems - Beijing China * modified mp. 4251fedc51fSWinson Wang - Sun Microsystems - Beijing China */ 4261fedc51fSWinson Wang - Sun Microsystems - Beijing China if (hdr_new_mp) { 4271fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_new_mp->b_cont = NULL; 4281fedc51fSWinson Wang - Sun Microsystems - Beijing China freeb(hdr_new_mp); 4291fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_nmp->b_rptr -= hdr_frag_len; 4301fedc51fSWinson Wang - Sun Microsystems - Beijing China if (hdr_pre_mp) 4311fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_pre_mp->b_cont = hdr_nmp; 4321fedc51fSWinson Wang - Sun Microsystems - Beijing China else 4331fedc51fSWinson Wang - Sun Microsystems - Beijing China mp = hdr_nmp; 4341fedc51fSWinson Wang - Sun Microsystems - Beijing China } 435185c5677SPaul Guo return (mp); 436edf70dc9SPaul Guo } 437edf70dc9SPaul Guo 438edf70dc9SPaul Guo LINK_LIST_INIT(&pending_list); 439185c5677SPaul Guo desc_total = 0; 440185c5677SPaul Guo 441185c5677SPaul Guo /* 442185c5677SPaul Guo * if the packet is a LSO packet, we simply 443185c5677SPaul Guo * transmit the header in one descriptor using the copy way 444185c5677SPaul Guo */ 445185c5677SPaul Guo if ((ctx != NULL) && ctx->lso_flag) { 446185c5677SPaul Guo hdr_len = ctx->ip_hdr_len + ctx->mac_hdr_len + 447185c5677SPaul Guo ctx->l4_hdr_len; 448185c5677SPaul Guo 449edf70dc9SPaul Guo tcb = ixgbe_get_free_list(tx_ring); 450edf70dc9SPaul Guo if (tcb == NULL) { 451abcd9e32SRyan Zezeski tx_ring->stat_fail_no_tcb++; 452185c5677SPaul Guo goto tx_failure; 453185c5677SPaul Guo } 454185c5677SPaul Guo desc_num = ixgbe_tx_copy(tx_ring, tcb, pull_mp, 455185c5677SPaul Guo hdr_len, B_TRUE); 456185c5677SPaul Guo LIST_PUSH_TAIL(&pending_list, &tcb->link); 457185c5677SPaul Guo desc_total += desc_num; 458185c5677SPaul Guo 459185c5677SPaul Guo pull_mp->b_rptr += hdr_len; 460185c5677SPaul Guo } 461185c5677SPaul Guo 462185c5677SPaul Guo tcb = ixgbe_get_free_list(tx_ring); 463185c5677SPaul Guo if (tcb == NULL) { 464abcd9e32SRyan Zezeski tx_ring->stat_fail_no_tcb++; 465185c5677SPaul Guo goto tx_failure; 466185c5677SPaul Guo } 467185c5677SPaul Guo if ((ctx != NULL) && ctx->lso_flag) { 468185c5677SPaul Guo desc_num = ixgbe_tx_bind(tx_ring, tcb, pull_mp, 469185c5677SPaul Guo mbsize - hdr_len); 470185c5677SPaul Guo } else { 471185c5677SPaul Guo desc_num = ixgbe_tx_bind(tx_ring, tcb, pull_mp, 472185c5677SPaul Guo mbsize); 473185c5677SPaul Guo } 474185c5677SPaul Guo if (desc_num < 0) { 475185c5677SPaul Guo goto tx_failure; 476edf70dc9SPaul Guo } 477edf70dc9SPaul Guo LIST_PUSH_TAIL(&pending_list, &tcb->link); 478edf70dc9SPaul Guo 479185c5677SPaul Guo desc_total += desc_num; 480185c5677SPaul Guo tcb->mp = pull_mp; 481edf70dc9SPaul Guo } 482edf70dc9SPaul Guo 483edf70dc9SPaul Guo /* 4849da57d7bSbt150084 * Before fill the tx descriptor ring with the data, we need to 4859da57d7bSbt150084 * ensure there are adequate free descriptors for transmit 4869da57d7bSbt150084 * (including one context descriptor). 4875b6dd21fSchenlu chen - Sun Microsystems - Beijing China * Do not use up all the tx descriptors. 4885b6dd21fSchenlu chen - Sun Microsystems - Beijing China * Otherwise tx recycle will fail and cause false hang. 4899da57d7bSbt150084 */ 4905b6dd21fSchenlu chen - Sun Microsystems - Beijing China if (tx_ring->tbd_free <= (desc_total + 1)) { 4919da57d7bSbt150084 tx_ring->tx_recycle(tx_ring); 4929da57d7bSbt150084 } 4939da57d7bSbt150084 4949da57d7bSbt150084 mutex_enter(&tx_ring->tx_lock); 4959da57d7bSbt150084 /* 4969da57d7bSbt150084 * If the number of free tx descriptors is not enough for transmit 497da14cebeSEric Cheng * then return mp. 4989da57d7bSbt150084 * 4999da57d7bSbt150084 * Note: we must put this check under the mutex protection to 5009da57d7bSbt150084 * ensure the correctness when multiple threads access it in 5019da57d7bSbt150084 * parallel. 5029da57d7bSbt150084 */ 5035b6dd21fSchenlu chen - Sun Microsystems - Beijing China if (tx_ring->tbd_free <= (desc_total + 1)) { 504abcd9e32SRyan Zezeski tx_ring->stat_fail_no_tbd++; 5059da57d7bSbt150084 mutex_exit(&tx_ring->tx_lock); 5069da57d7bSbt150084 goto tx_failure; 5079da57d7bSbt150084 } 5089da57d7bSbt150084 509c971fb7eSgg161487 desc_num = ixgbe_tx_fill_ring(tx_ring, &pending_list, ctx, 510c971fb7eSgg161487 mbsize); 5119da57d7bSbt150084 5129da57d7bSbt150084 ASSERT((desc_num == desc_total) || (desc_num == (desc_total + 1))); 5139da57d7bSbt150084 5140dc2366fSVenugopal Iyer tx_ring->stat_obytes += mbsize; 5150dc2366fSVenugopal Iyer tx_ring->stat_opackets ++; 5160dc2366fSVenugopal Iyer 5179da57d7bSbt150084 mutex_exit(&tx_ring->tx_lock); 5189da57d7bSbt150084 519185c5677SPaul Guo /* 520185c5677SPaul Guo * now that the transmission succeeds, need to free the original 521185c5677SPaul Guo * mp if we used the pulling up mblk for transmission. 522185c5677SPaul Guo */ 523185c5677SPaul Guo if (pull_mp) { 524185c5677SPaul Guo freemsg(mp); 525185c5677SPaul Guo } 526185c5677SPaul Guo 527da14cebeSEric Cheng return (NULL); 5289da57d7bSbt150084 5299da57d7bSbt150084 tx_failure: 5309da57d7bSbt150084 /* 531185c5677SPaul Guo * If transmission fails, need to free the pulling up mblk. 532185c5677SPaul Guo */ 533185c5677SPaul Guo if (pull_mp) { 534185c5677SPaul Guo freemsg(pull_mp); 535185c5677SPaul Guo } 536185c5677SPaul Guo 537185c5677SPaul Guo /* 5381fedc51fSWinson Wang - Sun Microsystems - Beijing China * If new mblk has been allocted for the last header 5391fedc51fSWinson Wang - Sun Microsystems - Beijing China * fragment of a LSO packet, we should restore the 5401fedc51fSWinson Wang - Sun Microsystems - Beijing China * modified mp. 5411fedc51fSWinson Wang - Sun Microsystems - Beijing China */ 5421fedc51fSWinson Wang - Sun Microsystems - Beijing China if (hdr_new_mp) { 5431fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_new_mp->b_cont = NULL; 5441fedc51fSWinson Wang - Sun Microsystems - Beijing China freeb(hdr_new_mp); 5451fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_nmp->b_rptr -= hdr_frag_len; 5461fedc51fSWinson Wang - Sun Microsystems - Beijing China if (hdr_pre_mp) 5471fedc51fSWinson Wang - Sun Microsystems - Beijing China hdr_pre_mp->b_cont = hdr_nmp; 5481fedc51fSWinson Wang - Sun Microsystems - Beijing China else 5491fedc51fSWinson Wang - Sun Microsystems - Beijing China mp = hdr_nmp; 5501fedc51fSWinson Wang - Sun Microsystems - Beijing China } 5511fedc51fSWinson Wang - Sun Microsystems - Beijing China /* 5529da57d7bSbt150084 * Discard the mblk and free the used resources 5539da57d7bSbt150084 */ 5549da57d7bSbt150084 tcb = (tx_control_block_t *)LIST_GET_HEAD(&pending_list); 5559da57d7bSbt150084 while (tcb) { 5569da57d7bSbt150084 tcb->mp = NULL; 5579da57d7bSbt150084 5589da57d7bSbt150084 ixgbe_free_tcb(tcb); 5599da57d7bSbt150084 5609da57d7bSbt150084 tcb = (tx_control_block_t *) 5619da57d7bSbt150084 LIST_GET_NEXT(&pending_list, &tcb->link); 5629da57d7bSbt150084 } 5639da57d7bSbt150084 5649da57d7bSbt150084 /* 5659da57d7bSbt150084 * Return the tx control blocks in the pending list to the free list. 5669da57d7bSbt150084 */ 5679da57d7bSbt150084 ixgbe_put_free_list(tx_ring, &pending_list); 5689da57d7bSbt150084 5699da57d7bSbt150084 /* Transmit failed, do not drop the mblk, rechedule the transmit */ 5709da57d7bSbt150084 tx_ring->reschedule = B_TRUE; 5719da57d7bSbt150084 572da14cebeSEric Cheng return (mp); 5739da57d7bSbt150084 } 5749da57d7bSbt150084 5759da57d7bSbt150084 /* 5769da57d7bSbt150084 * ixgbe_tx_copy 5779da57d7bSbt150084 * 5789da57d7bSbt150084 * Copy the mblk fragment to the pre-allocated tx buffer 5799da57d7bSbt150084 */ 5809da57d7bSbt150084 static int 5819da57d7bSbt150084 ixgbe_tx_copy(ixgbe_tx_ring_t *tx_ring, tx_control_block_t *tcb, mblk_t *mp, 582c971fb7eSgg161487 uint32_t len, boolean_t copy_done) 5839da57d7bSbt150084 { 5849da57d7bSbt150084 dma_buffer_t *tx_buf; 5859da57d7bSbt150084 uint32_t desc_num; 5869da57d7bSbt150084 _NOTE(ARGUNUSED(tx_ring)); 5879da57d7bSbt150084 5889da57d7bSbt150084 tx_buf = &tcb->tx_buf; 5899da57d7bSbt150084 5909da57d7bSbt150084 /* 5919da57d7bSbt150084 * Copy the packet data of the mblk fragment into the 5929da57d7bSbt150084 * pre-allocated tx buffer, which is maintained by the 5939da57d7bSbt150084 * tx control block. 5949da57d7bSbt150084 * 5959da57d7bSbt150084 * Several mblk fragments can be copied into one tx buffer. 5969da57d7bSbt150084 * The destination address of the current copied fragment in 5979da57d7bSbt150084 * the tx buffer is next to the end of the previous copied 5989da57d7bSbt150084 * fragment. 5999da57d7bSbt150084 */ 6009da57d7bSbt150084 if (len > 0) { 6019da57d7bSbt150084 bcopy(mp->b_rptr, tx_buf->address + tx_buf->len, len); 6029da57d7bSbt150084 6039da57d7bSbt150084 tx_buf->len += len; 6049da57d7bSbt150084 tcb->frag_num++; 6059da57d7bSbt150084 } 6069da57d7bSbt150084 6079da57d7bSbt150084 desc_num = 0; 6089da57d7bSbt150084 6099da57d7bSbt150084 /* 6109da57d7bSbt150084 * If it is the last fragment copied to the current tx buffer, 6119da57d7bSbt150084 * in other words, if there's no remaining fragment or the remaining 6129da57d7bSbt150084 * fragment requires a new tx control block to process, we need to 6139da57d7bSbt150084 * complete the current copy processing by syncing up the current 6149da57d7bSbt150084 * DMA buffer and saving the descriptor data. 6159da57d7bSbt150084 */ 6169da57d7bSbt150084 if (copy_done) { 6179da57d7bSbt150084 /* 6189da57d7bSbt150084 * Sync the DMA buffer of the packet data 6199da57d7bSbt150084 */ 6209da57d7bSbt150084 DMA_SYNC(tx_buf, DDI_DMA_SYNC_FORDEV); 6219da57d7bSbt150084 6229da57d7bSbt150084 tcb->tx_type = USE_COPY; 6239da57d7bSbt150084 6249da57d7bSbt150084 /* 6259da57d7bSbt150084 * Save the address and length to the private data structure 6269da57d7bSbt150084 * of the tx control block, which will be used to fill the 6279da57d7bSbt150084 * tx descriptor ring after all the fragments are processed. 6289da57d7bSbt150084 */ 6299da57d7bSbt150084 ixgbe_save_desc(tcb, tx_buf->dma_address, tx_buf->len); 6309da57d7bSbt150084 desc_num++; 6319da57d7bSbt150084 } 6329da57d7bSbt150084 6339da57d7bSbt150084 return (desc_num); 6349da57d7bSbt150084 } 6359da57d7bSbt150084 6369da57d7bSbt150084 /* 6379da57d7bSbt150084 * ixgbe_tx_bind 6389da57d7bSbt150084 * 6399da57d7bSbt150084 * Bind the mblk fragment with DMA 6409da57d7bSbt150084 */ 6419da57d7bSbt150084 static int 6429da57d7bSbt150084 ixgbe_tx_bind(ixgbe_tx_ring_t *tx_ring, tx_control_block_t *tcb, mblk_t *mp, 6439da57d7bSbt150084 uint32_t len) 6449da57d7bSbt150084 { 6459da57d7bSbt150084 int status, i; 6469da57d7bSbt150084 ddi_dma_cookie_t dma_cookie; 6479da57d7bSbt150084 uint_t ncookies; 6489da57d7bSbt150084 int desc_num; 6499da57d7bSbt150084 6509da57d7bSbt150084 /* 6519da57d7bSbt150084 * Use DMA binding to process the mblk fragment 6529da57d7bSbt150084 */ 6539da57d7bSbt150084 status = ddi_dma_addr_bind_handle(tcb->tx_dma_handle, NULL, 6549da57d7bSbt150084 (caddr_t)mp->b_rptr, len, 6559da57d7bSbt150084 DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 6569da57d7bSbt150084 0, &dma_cookie, &ncookies); 6579da57d7bSbt150084 6589da57d7bSbt150084 if (status != DDI_DMA_MAPPED) { 659abcd9e32SRyan Zezeski tx_ring->stat_fail_dma_bind++; 6609da57d7bSbt150084 return (-1); 6619da57d7bSbt150084 } 6629da57d7bSbt150084 6639da57d7bSbt150084 tcb->frag_num++; 6649da57d7bSbt150084 tcb->tx_type = USE_DMA; 6659da57d7bSbt150084 /* 6669da57d7bSbt150084 * Each fragment can span several cookies. One cookie will have 6679da57d7bSbt150084 * one tx descriptor to transmit. 6689da57d7bSbt150084 */ 6699da57d7bSbt150084 desc_num = 0; 6709da57d7bSbt150084 for (i = ncookies; i > 0; i--) { 6719da57d7bSbt150084 /* 6729da57d7bSbt150084 * Save the address and length to the private data structure 6739da57d7bSbt150084 * of the tx control block, which will be used to fill the 6749da57d7bSbt150084 * tx descriptor ring after all the fragments are processed. 6759da57d7bSbt150084 */ 6769da57d7bSbt150084 ixgbe_save_desc(tcb, 6779da57d7bSbt150084 dma_cookie.dmac_laddress, 6789da57d7bSbt150084 dma_cookie.dmac_size); 6799da57d7bSbt150084 6809da57d7bSbt150084 desc_num++; 6819da57d7bSbt150084 6829da57d7bSbt150084 if (i > 1) 6839da57d7bSbt150084 ddi_dma_nextcookie(tcb->tx_dma_handle, &dma_cookie); 6849da57d7bSbt150084 } 6859da57d7bSbt150084 6869da57d7bSbt150084 return (desc_num); 6879da57d7bSbt150084 } 6889da57d7bSbt150084 6899da57d7bSbt150084 /* 690c971fb7eSgg161487 * ixgbe_get_context 6919da57d7bSbt150084 * 692c971fb7eSgg161487 * Get the context information from the mblk 6939da57d7bSbt150084 */ 694c971fb7eSgg161487 static int 695c971fb7eSgg161487 ixgbe_get_context(mblk_t *mp, ixgbe_tx_context_t *ctx) 6969da57d7bSbt150084 { 6979da57d7bSbt150084 uint32_t start; 698da14cebeSEric Cheng uint32_t hckflags; 699da14cebeSEric Cheng uint32_t lsoflags; 700da14cebeSEric Cheng uint32_t mss; 7019da57d7bSbt150084 uint32_t len; 7029da57d7bSbt150084 uint32_t size; 7039da57d7bSbt150084 uint32_t offset; 7049da57d7bSbt150084 unsigned char *pos; 7059da57d7bSbt150084 ushort_t etype; 7069da57d7bSbt150084 uint32_t mac_hdr_len; 7079da57d7bSbt150084 uint32_t l4_proto; 708c971fb7eSgg161487 uint32_t l4_hdr_len; 7099da57d7bSbt150084 7109da57d7bSbt150084 ASSERT(mp != NULL); 7119da57d7bSbt150084 7120dc2366fSVenugopal Iyer mac_hcksum_get(mp, &start, NULL, NULL, NULL, &hckflags); 713c971fb7eSgg161487 bzero(ctx, sizeof (ixgbe_tx_context_t)); 7149da57d7bSbt150084 71573cd555cSBin Tu - Sun Microsystems - Beijing China if (hckflags == 0) { 716c971fb7eSgg161487 return (0); 71773cd555cSBin Tu - Sun Microsystems - Beijing China } 71873cd555cSBin Tu - Sun Microsystems - Beijing China 719da14cebeSEric Cheng ctx->hcksum_flags = hckflags; 720c971fb7eSgg161487 7210dc2366fSVenugopal Iyer mac_lso_get(mp, &mss, &lsoflags); 722da14cebeSEric Cheng ctx->mss = mss; 723da14cebeSEric Cheng ctx->lso_flag = (lsoflags == HW_LSO); 724c971fb7eSgg161487 725c971fb7eSgg161487 /* 726c971fb7eSgg161487 * LSO relies on tx h/w checksum, so here will drop the package 727c971fb7eSgg161487 * if h/w checksum flag is not declared. 728c971fb7eSgg161487 */ 729c971fb7eSgg161487 if (ctx->lso_flag) { 730c971fb7eSgg161487 if (!((ctx->hcksum_flags & HCK_PARTIALCKSUM) && 731c971fb7eSgg161487 (ctx->hcksum_flags & HCK_IPV4_HDRCKSUM))) { 732c971fb7eSgg161487 IXGBE_DEBUGLOG_0(NULL, "ixgbe_tx: h/w " 733c971fb7eSgg161487 "checksum flags are not specified when doing LSO"); 734c971fb7eSgg161487 return (-1); 735c971fb7eSgg161487 } 736c971fb7eSgg161487 } 7379da57d7bSbt150084 7389da57d7bSbt150084 etype = 0; 7399da57d7bSbt150084 mac_hdr_len = 0; 7409da57d7bSbt150084 l4_proto = 0; 7419da57d7bSbt150084 7429da57d7bSbt150084 /* 7439da57d7bSbt150084 * Firstly get the position of the ether_type/ether_tpid. 7449da57d7bSbt150084 * Here we don't assume the ether (VLAN) header is fully included 7459da57d7bSbt150084 * in one mblk fragment, so we go thourgh the fragments to parse 7469da57d7bSbt150084 * the ether type. 7479da57d7bSbt150084 */ 748da14cebeSEric Cheng size = len = MBLKL(mp); 7499da57d7bSbt150084 offset = offsetof(struct ether_header, ether_type); 7509da57d7bSbt150084 while (size <= offset) { 7519da57d7bSbt150084 mp = mp->b_cont; 7529da57d7bSbt150084 ASSERT(mp != NULL); 753da14cebeSEric Cheng len = MBLKL(mp); 7549da57d7bSbt150084 size += len; 7559da57d7bSbt150084 } 7569da57d7bSbt150084 pos = mp->b_rptr + offset + len - size; 7579da57d7bSbt150084 7589da57d7bSbt150084 etype = ntohs(*(ushort_t *)(uintptr_t)pos); 7599da57d7bSbt150084 if (etype == ETHERTYPE_VLAN) { 7609da57d7bSbt150084 /* 7619da57d7bSbt150084 * Get the position of the ether_type in VLAN header 7629da57d7bSbt150084 */ 7639da57d7bSbt150084 offset = offsetof(struct ether_vlan_header, ether_type); 7649da57d7bSbt150084 while (size <= offset) { 7659da57d7bSbt150084 mp = mp->b_cont; 7669da57d7bSbt150084 ASSERT(mp != NULL); 767da14cebeSEric Cheng len = MBLKL(mp); 7689da57d7bSbt150084 size += len; 7699da57d7bSbt150084 } 7709da57d7bSbt150084 pos = mp->b_rptr + offset + len - size; 7719da57d7bSbt150084 7729da57d7bSbt150084 etype = ntohs(*(ushort_t *)(uintptr_t)pos); 7739da57d7bSbt150084 mac_hdr_len = sizeof (struct ether_vlan_header); 7749da57d7bSbt150084 } else { 7759da57d7bSbt150084 mac_hdr_len = sizeof (struct ether_header); 7769da57d7bSbt150084 } 7779da57d7bSbt150084 7789da57d7bSbt150084 /* 779da14cebeSEric Cheng * Here we don't assume the IP(V6) header is fully included in 780c971fb7eSgg161487 * one mblk fragment. 7819da57d7bSbt150084 */ 7829da57d7bSbt150084 switch (etype) { 7839da57d7bSbt150084 case ETHERTYPE_IP: 784da14cebeSEric Cheng if (ctx->lso_flag) { 785da14cebeSEric Cheng offset = offsetof(ipha_t, ipha_length) + mac_hdr_len; 7869da57d7bSbt150084 while (size <= offset) { 7879da57d7bSbt150084 mp = mp->b_cont; 7889da57d7bSbt150084 ASSERT(mp != NULL); 789da14cebeSEric Cheng len = MBLKL(mp); 7909da57d7bSbt150084 size += len; 7919da57d7bSbt150084 } 7929da57d7bSbt150084 pos = mp->b_rptr + offset + len - size; 793da14cebeSEric Cheng *((uint16_t *)(uintptr_t)(pos)) = 0; 7949da57d7bSbt150084 795da14cebeSEric Cheng offset = offsetof(ipha_t, ipha_hdr_checksum) + 796da14cebeSEric Cheng mac_hdr_len; 797da14cebeSEric Cheng while (size <= offset) { 798da14cebeSEric Cheng mp = mp->b_cont; 799da14cebeSEric Cheng ASSERT(mp != NULL); 800da14cebeSEric Cheng len = MBLKL(mp); 801da14cebeSEric Cheng size += len; 802da14cebeSEric Cheng } 803da14cebeSEric Cheng pos = mp->b_rptr + offset + len - size; 804da14cebeSEric Cheng *((uint16_t *)(uintptr_t)(pos)) = 0; 805c971fb7eSgg161487 806c971fb7eSgg161487 /* 807c971fb7eSgg161487 * To perform ixgbe LSO, here also need to fill 808c971fb7eSgg161487 * the tcp checksum field of the packet with the 809c971fb7eSgg161487 * following pseudo-header checksum: 810c971fb7eSgg161487 * (ip_source_addr, ip_destination_addr, l4_proto) 811c971fb7eSgg161487 * Currently the tcp/ip stack has done it. 812c971fb7eSgg161487 */ 813c971fb7eSgg161487 } 814c971fb7eSgg161487 815da14cebeSEric Cheng offset = offsetof(ipha_t, ipha_protocol) + mac_hdr_len; 816da14cebeSEric Cheng while (size <= offset) { 817da14cebeSEric Cheng mp = mp->b_cont; 818da14cebeSEric Cheng ASSERT(mp != NULL); 819da14cebeSEric Cheng len = MBLKL(mp); 820da14cebeSEric Cheng size += len; 821da14cebeSEric Cheng } 822da14cebeSEric Cheng pos = mp->b_rptr + offset + len - size; 823da14cebeSEric Cheng 824da14cebeSEric Cheng l4_proto = *(uint8_t *)pos; 8259da57d7bSbt150084 break; 8269da57d7bSbt150084 case ETHERTYPE_IPV6: 8279da57d7bSbt150084 offset = offsetof(ip6_t, ip6_nxt) + mac_hdr_len; 8289da57d7bSbt150084 while (size <= offset) { 8299da57d7bSbt150084 mp = mp->b_cont; 8309da57d7bSbt150084 ASSERT(mp != NULL); 831da14cebeSEric Cheng len = MBLKL(mp); 8329da57d7bSbt150084 size += len; 8339da57d7bSbt150084 } 8349da57d7bSbt150084 pos = mp->b_rptr + offset + len - size; 8359da57d7bSbt150084 8369da57d7bSbt150084 l4_proto = *(uint8_t *)pos; 8379da57d7bSbt150084 break; 8389da57d7bSbt150084 default: 8399da57d7bSbt150084 /* Unrecoverable error */ 8409da57d7bSbt150084 IXGBE_DEBUGLOG_0(NULL, "Ether type error with tx hcksum"); 841c971fb7eSgg161487 return (-2); 8429da57d7bSbt150084 } 8439da57d7bSbt150084 844c971fb7eSgg161487 if (ctx->lso_flag) { 845c971fb7eSgg161487 offset = mac_hdr_len + start; 846c971fb7eSgg161487 while (size <= offset) { 847c971fb7eSgg161487 mp = mp->b_cont; 848c971fb7eSgg161487 ASSERT(mp != NULL); 849da14cebeSEric Cheng len = MBLKL(mp); 850c971fb7eSgg161487 size += len; 851c971fb7eSgg161487 } 852c971fb7eSgg161487 pos = mp->b_rptr + offset + len - size; 853c971fb7eSgg161487 854c971fb7eSgg161487 l4_hdr_len = TCP_HDR_LENGTH((tcph_t *)pos); 855c971fb7eSgg161487 } else { 856c971fb7eSgg161487 /* 857c971fb7eSgg161487 * l4 header length is only required for LSO 858c971fb7eSgg161487 */ 859c971fb7eSgg161487 l4_hdr_len = 0; 860c971fb7eSgg161487 } 861c971fb7eSgg161487 862c971fb7eSgg161487 ctx->mac_hdr_len = mac_hdr_len; 863c971fb7eSgg161487 ctx->ip_hdr_len = start; 864c971fb7eSgg161487 ctx->l4_proto = l4_proto; 865c971fb7eSgg161487 ctx->l4_hdr_len = l4_hdr_len; 866c971fb7eSgg161487 867c971fb7eSgg161487 return (0); 8689da57d7bSbt150084 } 8699da57d7bSbt150084 8709da57d7bSbt150084 /* 871c971fb7eSgg161487 * ixgbe_check_context 8729da57d7bSbt150084 * 8739da57d7bSbt150084 * Check if a new context descriptor is needed 8749da57d7bSbt150084 */ 8759da57d7bSbt150084 static boolean_t 876c971fb7eSgg161487 ixgbe_check_context(ixgbe_tx_ring_t *tx_ring, ixgbe_tx_context_t *ctx) 8779da57d7bSbt150084 { 878c971fb7eSgg161487 ixgbe_tx_context_t *last; 8799da57d7bSbt150084 880c971fb7eSgg161487 if (ctx == NULL) 8819da57d7bSbt150084 return (B_FALSE); 8829da57d7bSbt150084 8839da57d7bSbt150084 /* 884da14cebeSEric Cheng * Compare the context data retrieved from the mblk and the 885da14cebeSEric Cheng * stored data of the last context descriptor. The data need 886da14cebeSEric Cheng * to be checked are: 8879da57d7bSbt150084 * hcksum_flags 8889da57d7bSbt150084 * l4_proto 8899da57d7bSbt150084 * mac_hdr_len 8909da57d7bSbt150084 * ip_hdr_len 891da14cebeSEric Cheng * lso_flag 892c971fb7eSgg161487 * mss (only checked for LSO) 893c971fb7eSgg161487 * l4_hr_len (only checked for LSO) 8949da57d7bSbt150084 * Either one of the above data is changed, a new context descriptor 8959da57d7bSbt150084 * will be needed. 8969da57d7bSbt150084 */ 897c971fb7eSgg161487 last = &tx_ring->tx_context; 8989da57d7bSbt150084 899c971fb7eSgg161487 if ((ctx->hcksum_flags != last->hcksum_flags) || 900c971fb7eSgg161487 (ctx->l4_proto != last->l4_proto) || 901c971fb7eSgg161487 (ctx->mac_hdr_len != last->mac_hdr_len) || 902c971fb7eSgg161487 (ctx->ip_hdr_len != last->ip_hdr_len) || 903da14cebeSEric Cheng (ctx->lso_flag != last->lso_flag) || 904c971fb7eSgg161487 (ctx->lso_flag && ((ctx->mss != last->mss) || 905c971fb7eSgg161487 (ctx->l4_hdr_len != last->l4_hdr_len)))) { 9069da57d7bSbt150084 return (B_TRUE); 9079da57d7bSbt150084 } 9089da57d7bSbt150084 9099da57d7bSbt150084 return (B_FALSE); 9109da57d7bSbt150084 } 9119da57d7bSbt150084 9129da57d7bSbt150084 /* 913c971fb7eSgg161487 * ixgbe_fill_context 9149da57d7bSbt150084 * 9159da57d7bSbt150084 * Fill the context descriptor with hardware checksum informations 9169da57d7bSbt150084 */ 9179da57d7bSbt150084 static void 918c971fb7eSgg161487 ixgbe_fill_context(struct ixgbe_adv_tx_context_desc *ctx_tbd, 91973cd555cSBin Tu - Sun Microsystems - Beijing China ixgbe_tx_context_t *ctx) 9209da57d7bSbt150084 { 9219da57d7bSbt150084 /* 9229da57d7bSbt150084 * Fill the context descriptor with the checksum 923da14cebeSEric Cheng * context information we've got. 9249da57d7bSbt150084 */ 925c971fb7eSgg161487 ctx_tbd->vlan_macip_lens = ctx->ip_hdr_len; 926c971fb7eSgg161487 ctx_tbd->vlan_macip_lens |= ctx->mac_hdr_len << 9279da57d7bSbt150084 IXGBE_ADVTXD_MACLEN_SHIFT; 9289da57d7bSbt150084 9299da57d7bSbt150084 ctx_tbd->type_tucmd_mlhl = 9309da57d7bSbt150084 IXGBE_ADVTXD_DCMD_DEXT | IXGBE_ADVTXD_DTYP_CTXT; 9319da57d7bSbt150084 932c971fb7eSgg161487 if (ctx->hcksum_flags & HCK_IPV4_HDRCKSUM) 9339da57d7bSbt150084 ctx_tbd->type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4; 9349da57d7bSbt150084 935c971fb7eSgg161487 if (ctx->hcksum_flags & HCK_PARTIALCKSUM) { 936c971fb7eSgg161487 switch (ctx->l4_proto) { 9379da57d7bSbt150084 case IPPROTO_TCP: 9389da57d7bSbt150084 ctx_tbd->type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP; 9399da57d7bSbt150084 break; 9409da57d7bSbt150084 case IPPROTO_UDP: 9419da57d7bSbt150084 /* 9429da57d7bSbt150084 * We don't have to explicitly set: 9439da57d7bSbt150084 * ctx_tbd->type_tucmd_mlhl |= 9449da57d7bSbt150084 * IXGBE_ADVTXD_TUCMD_L4T_UDP; 9459da57d7bSbt150084 * Because IXGBE_ADVTXD_TUCMD_L4T_UDP == 0b 9469da57d7bSbt150084 */ 9479da57d7bSbt150084 break; 9489da57d7bSbt150084 default: 9499da57d7bSbt150084 /* Unrecoverable error */ 9509da57d7bSbt150084 IXGBE_DEBUGLOG_0(NULL, "L4 type error with tx hcksum"); 9519da57d7bSbt150084 break; 9529da57d7bSbt150084 } 9539da57d7bSbt150084 } 9549da57d7bSbt150084 9559da57d7bSbt150084 ctx_tbd->seqnum_seed = 0; 956da14cebeSEric Cheng 957c971fb7eSgg161487 if (ctx->lso_flag) { 95873cd555cSBin Tu - Sun Microsystems - Beijing China ctx_tbd->mss_l4len_idx = 959c971fb7eSgg161487 (ctx->l4_hdr_len << IXGBE_ADVTXD_L4LEN_SHIFT) | 960c971fb7eSgg161487 (ctx->mss << IXGBE_ADVTXD_MSS_SHIFT); 96173cd555cSBin Tu - Sun Microsystems - Beijing China } else { 96273cd555cSBin Tu - Sun Microsystems - Beijing China ctx_tbd->mss_l4len_idx = 0; 9639da57d7bSbt150084 } 964c971fb7eSgg161487 } 9659da57d7bSbt150084 9669da57d7bSbt150084 /* 9679da57d7bSbt150084 * ixgbe_tx_fill_ring 9689da57d7bSbt150084 * 9699da57d7bSbt150084 * Fill the tx descriptor ring with the data 9709da57d7bSbt150084 */ 9719da57d7bSbt150084 static int 9729da57d7bSbt150084 ixgbe_tx_fill_ring(ixgbe_tx_ring_t *tx_ring, link_list_t *pending_list, 973c971fb7eSgg161487 ixgbe_tx_context_t *ctx, size_t mbsize) 9749da57d7bSbt150084 { 9759da57d7bSbt150084 struct ixgbe_hw *hw = &tx_ring->ixgbe->hw; 9769da57d7bSbt150084 boolean_t load_context; 9779da57d7bSbt150084 uint32_t index, tcb_index, desc_num; 9789da57d7bSbt150084 union ixgbe_adv_tx_desc *tbd, *first_tbd; 9799da57d7bSbt150084 tx_control_block_t *tcb, *first_tcb; 9809da57d7bSbt150084 uint32_t hcksum_flags; 9819da57d7bSbt150084 int i; 9829da57d7bSbt150084 9839da57d7bSbt150084 ASSERT(mutex_owned(&tx_ring->tx_lock)); 9849da57d7bSbt150084 9859da57d7bSbt150084 tbd = NULL; 9869da57d7bSbt150084 first_tbd = NULL; 9879da57d7bSbt150084 first_tcb = NULL; 9889da57d7bSbt150084 desc_num = 0; 9899da57d7bSbt150084 hcksum_flags = 0; 9909da57d7bSbt150084 load_context = B_FALSE; 9919da57d7bSbt150084 9929da57d7bSbt150084 /* 9939da57d7bSbt150084 * Get the index of the first tx descriptor that will be filled, 9949da57d7bSbt150084 * and the index of the first work list item that will be attached 9959da57d7bSbt150084 * with the first used tx control block in the pending list. 9969da57d7bSbt150084 * Note: the two indexes are the same. 9979da57d7bSbt150084 */ 9989da57d7bSbt150084 index = tx_ring->tbd_tail; 9999da57d7bSbt150084 tcb_index = tx_ring->tbd_tail; 10009da57d7bSbt150084 1001c971fb7eSgg161487 if (ctx != NULL) { 1002c971fb7eSgg161487 hcksum_flags = ctx->hcksum_flags; 10039da57d7bSbt150084 10049da57d7bSbt150084 /* 10059da57d7bSbt150084 * Check if a new context descriptor is needed for this packet 10069da57d7bSbt150084 */ 1007c971fb7eSgg161487 load_context = ixgbe_check_context(tx_ring, ctx); 1008c971fb7eSgg161487 10099da57d7bSbt150084 if (load_context) { 10109da57d7bSbt150084 tbd = &tx_ring->tbd_ring[index]; 10119da57d7bSbt150084 10129da57d7bSbt150084 /* 10139da57d7bSbt150084 * Fill the context descriptor with the 10149da57d7bSbt150084 * hardware checksum offload informations. 10159da57d7bSbt150084 */ 1016c971fb7eSgg161487 ixgbe_fill_context( 101773cd555cSBin Tu - Sun Microsystems - Beijing China (struct ixgbe_adv_tx_context_desc *)tbd, ctx); 10189da57d7bSbt150084 10199da57d7bSbt150084 index = NEXT_INDEX(index, 1, tx_ring->ring_size); 10209da57d7bSbt150084 desc_num++; 10219da57d7bSbt150084 10229da57d7bSbt150084 /* 10239da57d7bSbt150084 * Store the checksum context data if 10249da57d7bSbt150084 * a new context descriptor is added 10259da57d7bSbt150084 */ 1026c971fb7eSgg161487 tx_ring->tx_context = *ctx; 10279da57d7bSbt150084 } 10289da57d7bSbt150084 } 10299da57d7bSbt150084 10309da57d7bSbt150084 first_tbd = &tx_ring->tbd_ring[index]; 10319da57d7bSbt150084 10329da57d7bSbt150084 /* 10339da57d7bSbt150084 * Fill tx data descriptors with the data saved in the pending list. 10349da57d7bSbt150084 * The tx control blocks in the pending list are added to the work list 10359da57d7bSbt150084 * at the same time. 10369da57d7bSbt150084 * 10379da57d7bSbt150084 * The work list is strictly 1:1 corresponding to the descriptor ring. 10389da57d7bSbt150084 * One item of the work list corresponds to one tx descriptor. Because 10399da57d7bSbt150084 * one tx control block can span multiple tx descriptors, the tx 10409da57d7bSbt150084 * control block will be added to the first work list item that 10419da57d7bSbt150084 * corresponds to the first tx descriptor generated from that tx 10429da57d7bSbt150084 * control block. 10439da57d7bSbt150084 */ 10449da57d7bSbt150084 tcb = (tx_control_block_t *)LIST_POP_HEAD(pending_list); 1045edf70dc9SPaul Guo first_tcb = tcb; 10469da57d7bSbt150084 while (tcb != NULL) { 10479da57d7bSbt150084 10489da57d7bSbt150084 for (i = 0; i < tcb->desc_num; i++) { 10499da57d7bSbt150084 tbd = &tx_ring->tbd_ring[index]; 10509da57d7bSbt150084 10519da57d7bSbt150084 tbd->read.buffer_addr = tcb->desc[i].address; 10529da57d7bSbt150084 tbd->read.cmd_type_len = tcb->desc[i].length; 10539da57d7bSbt150084 1054edf70dc9SPaul Guo tbd->read.cmd_type_len |= IXGBE_ADVTXD_DCMD_DEXT 1055edf70dc9SPaul Guo | IXGBE_ADVTXD_DTYP_DATA; 10569da57d7bSbt150084 10579da57d7bSbt150084 tbd->read.olinfo_status = 0; 10589da57d7bSbt150084 10599da57d7bSbt150084 index = NEXT_INDEX(index, 1, tx_ring->ring_size); 10609da57d7bSbt150084 desc_num++; 10619da57d7bSbt150084 } 10629da57d7bSbt150084 10639da57d7bSbt150084 /* 10649da57d7bSbt150084 * Add the tx control block to the work list 10659da57d7bSbt150084 */ 10669da57d7bSbt150084 ASSERT(tx_ring->work_list[tcb_index] == NULL); 10679da57d7bSbt150084 tx_ring->work_list[tcb_index] = tcb; 10689da57d7bSbt150084 10699da57d7bSbt150084 tcb_index = index; 10709da57d7bSbt150084 tcb = (tx_control_block_t *)LIST_POP_HEAD(pending_list); 10719da57d7bSbt150084 } 10729da57d7bSbt150084 1073edf70dc9SPaul Guo if (load_context) { 1074edf70dc9SPaul Guo /* 1075edf70dc9SPaul Guo * Count the context descriptor for 1076edf70dc9SPaul Guo * the first tx control block. 1077edf70dc9SPaul Guo */ 1078edf70dc9SPaul Guo first_tcb->desc_num++; 1079edf70dc9SPaul Guo } 1080edf70dc9SPaul Guo first_tcb->last_index = PREV_INDEX(index, 1, tx_ring->ring_size); 1081edf70dc9SPaul Guo 10829da57d7bSbt150084 /* 10839da57d7bSbt150084 * The Insert Ethernet CRC (IFCS) bit and the checksum fields are only 10849da57d7bSbt150084 * valid in the first descriptor of the packet. 108573cd555cSBin Tu - Sun Microsystems - Beijing China * Setting paylen in every first_tbd for all parts. 10867e579c30SDale Ghent * 82599, X540 and X550 require the packet length in paylen field 10877e579c30SDale Ghent * with or without LSO and 82598 will ignore it in non-LSO mode. 10889da57d7bSbt150084 */ 10899da57d7bSbt150084 ASSERT(first_tbd != NULL); 10909da57d7bSbt150084 first_tbd->read.cmd_type_len |= IXGBE_ADVTXD_DCMD_IFCS; 1091da14cebeSEric Cheng 109273cd555cSBin Tu - Sun Microsystems - Beijing China switch (hw->mac.type) { 10935b6dd21fSchenlu chen - Sun Microsystems - Beijing China case ixgbe_mac_82598EB: 10945b6dd21fSchenlu chen - Sun Microsystems - Beijing China if (ctx != NULL && ctx->lso_flag) { 10955b6dd21fSchenlu chen - Sun Microsystems - Beijing China first_tbd->read.cmd_type_len |= IXGBE_ADVTXD_DCMD_TSE; 10965b6dd21fSchenlu chen - Sun Microsystems - Beijing China first_tbd->read.olinfo_status |= 10975b6dd21fSchenlu chen - Sun Microsystems - Beijing China (mbsize - ctx->mac_hdr_len - ctx->ip_hdr_len 10985b6dd21fSchenlu chen - Sun Microsystems - Beijing China - ctx->l4_hdr_len) << IXGBE_ADVTXD_PAYLEN_SHIFT; 10995b6dd21fSchenlu chen - Sun Microsystems - Beijing China } 11005b6dd21fSchenlu chen - Sun Microsystems - Beijing China break; 11015b6dd21fSchenlu chen - Sun Microsystems - Beijing China 110273cd555cSBin Tu - Sun Microsystems - Beijing China case ixgbe_mac_82599EB: 110369b5a878SDan McDonald case ixgbe_mac_X540: 11047e579c30SDale Ghent case ixgbe_mac_X550: 11057e579c30SDale Ghent case ixgbe_mac_X550EM_x: 110673cd555cSBin Tu - Sun Microsystems - Beijing China if (ctx != NULL && ctx->lso_flag) { 110773cd555cSBin Tu - Sun Microsystems - Beijing China first_tbd->read.cmd_type_len |= IXGBE_ADVTXD_DCMD_TSE; 110873cd555cSBin Tu - Sun Microsystems - Beijing China first_tbd->read.olinfo_status |= 110973cd555cSBin Tu - Sun Microsystems - Beijing China (mbsize - ctx->mac_hdr_len - ctx->ip_hdr_len 111073cd555cSBin Tu - Sun Microsystems - Beijing China - ctx->l4_hdr_len) << IXGBE_ADVTXD_PAYLEN_SHIFT; 111173cd555cSBin Tu - Sun Microsystems - Beijing China } else { 111273cd555cSBin Tu - Sun Microsystems - Beijing China first_tbd->read.olinfo_status |= 111373cd555cSBin Tu - Sun Microsystems - Beijing China (mbsize << IXGBE_ADVTXD_PAYLEN_SHIFT); 111473cd555cSBin Tu - Sun Microsystems - Beijing China } 111573cd555cSBin Tu - Sun Microsystems - Beijing China break; 11165b6dd21fSchenlu chen - Sun Microsystems - Beijing China 111773cd555cSBin Tu - Sun Microsystems - Beijing China default: 111873cd555cSBin Tu - Sun Microsystems - Beijing China break; 1119c971fb7eSgg161487 } 1120c971fb7eSgg161487 11219da57d7bSbt150084 /* Set hardware checksum bits */ 11229da57d7bSbt150084 if (hcksum_flags != 0) { 11239da57d7bSbt150084 if (hcksum_flags & HCK_IPV4_HDRCKSUM) 11249da57d7bSbt150084 first_tbd->read.olinfo_status |= 1125c971fb7eSgg161487 IXGBE_ADVTXD_POPTS_IXSM; 11269da57d7bSbt150084 if (hcksum_flags & HCK_PARTIALCKSUM) 11279da57d7bSbt150084 first_tbd->read.olinfo_status |= 1128c971fb7eSgg161487 IXGBE_ADVTXD_POPTS_TXSM; 11299da57d7bSbt150084 } 11309da57d7bSbt150084 11319da57d7bSbt150084 /* 11329da57d7bSbt150084 * The last descriptor of packet needs End Of Packet (EOP), 11339da57d7bSbt150084 * and Report Status (RS) bits set 11349da57d7bSbt150084 */ 11359da57d7bSbt150084 ASSERT(tbd != NULL); 11369da57d7bSbt150084 tbd->read.cmd_type_len |= 11379da57d7bSbt150084 IXGBE_ADVTXD_DCMD_EOP | IXGBE_ADVTXD_DCMD_RS; 11389da57d7bSbt150084 11399da57d7bSbt150084 /* 11409da57d7bSbt150084 * Sync the DMA buffer of the tx descriptor ring 11419da57d7bSbt150084 */ 11429da57d7bSbt150084 DMA_SYNC(&tx_ring->tbd_area, DDI_DMA_SYNC_FORDEV); 11439da57d7bSbt150084 11449da57d7bSbt150084 /* 11459da57d7bSbt150084 * Update the number of the free tx descriptors. 11469da57d7bSbt150084 * The mutual exclusion between the transmission and the recycling 11479da57d7bSbt150084 * (for the tx descriptor ring and the work list) is implemented 11489da57d7bSbt150084 * with the atomic operation on the number of the free tx descriptors. 11499da57d7bSbt150084 * 11509da57d7bSbt150084 * Note: we should always decrement the counter tbd_free before 11519da57d7bSbt150084 * advancing the hardware TDT pointer to avoid the race condition - 11529da57d7bSbt150084 * before the counter tbd_free is decremented, the transmit of the 11539da57d7bSbt150084 * tx descriptors has done and the counter tbd_free is increased by 11549da57d7bSbt150084 * the tx recycling. 11559da57d7bSbt150084 */ 11569da57d7bSbt150084 i = ixgbe_atomic_reserve(&tx_ring->tbd_free, desc_num); 11579da57d7bSbt150084 ASSERT(i >= 0); 11589da57d7bSbt150084 11599da57d7bSbt150084 tx_ring->tbd_tail = index; 11609da57d7bSbt150084 11619da57d7bSbt150084 /* 11629da57d7bSbt150084 * Advance the hardware TDT pointer of the tx descriptor ring 11639da57d7bSbt150084 */ 11649da57d7bSbt150084 IXGBE_WRITE_REG(hw, IXGBE_TDT(tx_ring->index), index); 11659da57d7bSbt150084 11669da57d7bSbt150084 if (ixgbe_check_acc_handle(tx_ring->ixgbe->osdep.reg_handle) != 11679da57d7bSbt150084 DDI_FM_OK) { 11689da57d7bSbt150084 ddi_fm_service_impact(tx_ring->ixgbe->dip, 11699da57d7bSbt150084 DDI_SERVICE_DEGRADED); 117062e6e1adSPaul Guo atomic_or_32(&tx_ring->ixgbe->ixgbe_state, IXGBE_ERROR); 11719da57d7bSbt150084 } 11729da57d7bSbt150084 11739da57d7bSbt150084 return (desc_num); 11749da57d7bSbt150084 } 11759da57d7bSbt150084 11769da57d7bSbt150084 /* 11779da57d7bSbt150084 * ixgbe_save_desc 11789da57d7bSbt150084 * 11799da57d7bSbt150084 * Save the address/length pair to the private array 11809da57d7bSbt150084 * of the tx control block. The address/length pairs 11819da57d7bSbt150084 * will be filled into the tx descriptor ring later. 11829da57d7bSbt150084 */ 11839da57d7bSbt150084 static void 11849da57d7bSbt150084 ixgbe_save_desc(tx_control_block_t *tcb, uint64_t address, size_t length) 11859da57d7bSbt150084 { 11869da57d7bSbt150084 sw_desc_t *desc; 11879da57d7bSbt150084 11889da57d7bSbt150084 desc = &tcb->desc[tcb->desc_num]; 11899da57d7bSbt150084 desc->address = address; 11909da57d7bSbt150084 desc->length = length; 11919da57d7bSbt150084 11929da57d7bSbt150084 tcb->desc_num++; 11939da57d7bSbt150084 } 11949da57d7bSbt150084 11959da57d7bSbt150084 /* 11969da57d7bSbt150084 * ixgbe_tx_recycle_legacy 11979da57d7bSbt150084 * 11989da57d7bSbt150084 * Recycle the tx descriptors and tx control blocks. 11999da57d7bSbt150084 * 12009da57d7bSbt150084 * The work list is traversed to check if the corresponding 12019da57d7bSbt150084 * tx descriptors have been transmitted. If so, the resources 12029da57d7bSbt150084 * bound to the tx control blocks will be freed, and those 12039da57d7bSbt150084 * tx control blocks will be returned to the free list. 12049da57d7bSbt150084 */ 12059da57d7bSbt150084 uint32_t 12069da57d7bSbt150084 ixgbe_tx_recycle_legacy(ixgbe_tx_ring_t *tx_ring) 12079da57d7bSbt150084 { 1208edf70dc9SPaul Guo uint32_t index, last_index, prev_index; 12099da57d7bSbt150084 int desc_num; 12109da57d7bSbt150084 boolean_t desc_done; 12119da57d7bSbt150084 tx_control_block_t *tcb; 12129da57d7bSbt150084 link_list_t pending_list; 1213ea65739eSchenlu chen - Sun Microsystems - Beijing China ixgbe_t *ixgbe = tx_ring->ixgbe; 12149da57d7bSbt150084 1215da14cebeSEric Cheng mutex_enter(&tx_ring->recycle_lock); 12169da57d7bSbt150084 12179da57d7bSbt150084 ASSERT(tx_ring->tbd_free <= tx_ring->ring_size); 12189da57d7bSbt150084 12199da57d7bSbt150084 if (tx_ring->tbd_free == tx_ring->ring_size) { 12209da57d7bSbt150084 tx_ring->recycle_fail = 0; 12219da57d7bSbt150084 tx_ring->stall_watchdog = 0; 1222da14cebeSEric Cheng if (tx_ring->reschedule) { 1223da14cebeSEric Cheng tx_ring->reschedule = B_FALSE; 1224ea65739eSchenlu chen - Sun Microsystems - Beijing China mac_tx_ring_update(ixgbe->mac_hdl, 1225da14cebeSEric Cheng tx_ring->ring_handle); 1226da14cebeSEric Cheng } 12279da57d7bSbt150084 mutex_exit(&tx_ring->recycle_lock); 12289da57d7bSbt150084 return (0); 12299da57d7bSbt150084 } 12309da57d7bSbt150084 12319da57d7bSbt150084 /* 12329da57d7bSbt150084 * Sync the DMA buffer of the tx descriptor ring 12339da57d7bSbt150084 */ 12349da57d7bSbt150084 DMA_SYNC(&tx_ring->tbd_area, DDI_DMA_SYNC_FORKERNEL); 12359da57d7bSbt150084 12369da57d7bSbt150084 if (ixgbe_check_dma_handle(tx_ring->tbd_area.dma_handle) != DDI_FM_OK) { 123719843f01SPaul Guo mutex_exit(&tx_ring->recycle_lock); 1238ea65739eSchenlu chen - Sun Microsystems - Beijing China ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_DEGRADED); 123962e6e1adSPaul Guo atomic_or_32(&ixgbe->ixgbe_state, IXGBE_ERROR); 124062e6e1adSPaul Guo return (0); 12419da57d7bSbt150084 } 12429da57d7bSbt150084 12439da57d7bSbt150084 LINK_LIST_INIT(&pending_list); 12449da57d7bSbt150084 desc_num = 0; 12459da57d7bSbt150084 index = tx_ring->tbd_head; /* Index of next tbd/tcb to recycle */ 12469da57d7bSbt150084 12479da57d7bSbt150084 tcb = tx_ring->work_list[index]; 12489da57d7bSbt150084 ASSERT(tcb != NULL); 12499da57d7bSbt150084 1250edf70dc9SPaul Guo while (tcb != NULL) { 12519da57d7bSbt150084 /* 1252edf70dc9SPaul Guo * Get the last tx descriptor of this packet. 1253edf70dc9SPaul Guo * If the last tx descriptor is done, then 1254edf70dc9SPaul Guo * we can recycle all descriptors of a packet 1255edf70dc9SPaul Guo * which usually includes several tx control blocks. 1256edf70dc9SPaul Guo * For 82599, LSO descriptors can not be recycled 1257edf70dc9SPaul Guo * unless the whole packet's transmission is done. 1258edf70dc9SPaul Guo * That's why packet level recycling is used here. 1259edf70dc9SPaul Guo * For 82598, there's not such limit. 12609da57d7bSbt150084 */ 1261edf70dc9SPaul Guo last_index = tcb->last_index; 1262edf70dc9SPaul Guo /* 1263edf70dc9SPaul Guo * MAX_TX_RING_SIZE is used to judge whether 1264edf70dc9SPaul Guo * the index is a valid value or not. 1265edf70dc9SPaul Guo */ 1266edf70dc9SPaul Guo if (last_index == MAX_TX_RING_SIZE) 1267edf70dc9SPaul Guo break; 12689da57d7bSbt150084 12699da57d7bSbt150084 /* 12709da57d7bSbt150084 * Check if the Descriptor Done bit is set 12719da57d7bSbt150084 */ 12729da57d7bSbt150084 desc_done = tx_ring->tbd_ring[last_index].wb.status & 12739da57d7bSbt150084 IXGBE_TXD_STAT_DD; 12749da57d7bSbt150084 if (desc_done) { 12759da57d7bSbt150084 /* 1276edf70dc9SPaul Guo * recycle all descriptors of the packet 1277edf70dc9SPaul Guo */ 1278edf70dc9SPaul Guo while (tcb != NULL) { 1279edf70dc9SPaul Guo /* 1280edf70dc9SPaul Guo * Strip off the tx control block from 1281edf70dc9SPaul Guo * the work list, and add it to the 1282edf70dc9SPaul Guo * pending list. 12839da57d7bSbt150084 */ 12849da57d7bSbt150084 tx_ring->work_list[index] = NULL; 12859da57d7bSbt150084 LIST_PUSH_TAIL(&pending_list, &tcb->link); 12869da57d7bSbt150084 12879da57d7bSbt150084 /* 1288edf70dc9SPaul Guo * Count the total number of the tx 1289edf70dc9SPaul Guo * descriptors recycled 12909da57d7bSbt150084 */ 12919da57d7bSbt150084 desc_num += tcb->desc_num; 12929da57d7bSbt150084 1293edf70dc9SPaul Guo index = NEXT_INDEX(index, tcb->desc_num, 1294edf70dc9SPaul Guo tx_ring->ring_size); 12959da57d7bSbt150084 12969da57d7bSbt150084 tcb = tx_ring->work_list[index]; 1297edf70dc9SPaul Guo 1298edf70dc9SPaul Guo prev_index = PREV_INDEX(index, 1, 1299edf70dc9SPaul Guo tx_ring->ring_size); 1300edf70dc9SPaul Guo if (prev_index == last_index) 1301edf70dc9SPaul Guo break; 1302edf70dc9SPaul Guo } 1303edf70dc9SPaul Guo } else { 1304edf70dc9SPaul Guo break; 13059da57d7bSbt150084 } 13069da57d7bSbt150084 } 13079da57d7bSbt150084 13089da57d7bSbt150084 /* 13099da57d7bSbt150084 * If no tx descriptors are recycled, no need to do more processing 13109da57d7bSbt150084 */ 13119da57d7bSbt150084 if (desc_num == 0) { 13129da57d7bSbt150084 tx_ring->recycle_fail++; 13139da57d7bSbt150084 mutex_exit(&tx_ring->recycle_lock); 13149da57d7bSbt150084 return (0); 13159da57d7bSbt150084 } 13169da57d7bSbt150084 13179da57d7bSbt150084 tx_ring->recycle_fail = 0; 13189da57d7bSbt150084 tx_ring->stall_watchdog = 0; 13199da57d7bSbt150084 13209da57d7bSbt150084 /* 13219da57d7bSbt150084 * Update the head index of the tx descriptor ring 13229da57d7bSbt150084 */ 13239da57d7bSbt150084 tx_ring->tbd_head = index; 13249da57d7bSbt150084 13259da57d7bSbt150084 /* 13269da57d7bSbt150084 * Update the number of the free tx descriptors with atomic operations 13279da57d7bSbt150084 */ 13289da57d7bSbt150084 atomic_add_32(&tx_ring->tbd_free, desc_num); 13299da57d7bSbt150084 1330ea65739eSchenlu chen - Sun Microsystems - Beijing China if ((tx_ring->tbd_free >= ixgbe->tx_resched_thresh) && 1331da14cebeSEric Cheng (tx_ring->reschedule)) { 1332da14cebeSEric Cheng tx_ring->reschedule = B_FALSE; 1333ea65739eSchenlu chen - Sun Microsystems - Beijing China mac_tx_ring_update(ixgbe->mac_hdl, 1334da14cebeSEric Cheng tx_ring->ring_handle); 1335da14cebeSEric Cheng } 13369da57d7bSbt150084 mutex_exit(&tx_ring->recycle_lock); 13379da57d7bSbt150084 13389da57d7bSbt150084 /* 13399da57d7bSbt150084 * Free the resources used by the tx control blocks 13409da57d7bSbt150084 * in the pending list 13419da57d7bSbt150084 */ 13429da57d7bSbt150084 tcb = (tx_control_block_t *)LIST_GET_HEAD(&pending_list); 13439da57d7bSbt150084 while (tcb != NULL) { 13449da57d7bSbt150084 /* 13459da57d7bSbt150084 * Release the resources occupied by the tx control block 13469da57d7bSbt150084 */ 13479da57d7bSbt150084 ixgbe_free_tcb(tcb); 13489da57d7bSbt150084 13499da57d7bSbt150084 tcb = (tx_control_block_t *) 13509da57d7bSbt150084 LIST_GET_NEXT(&pending_list, &tcb->link); 13519da57d7bSbt150084 } 13529da57d7bSbt150084 13539da57d7bSbt150084 /* 13549da57d7bSbt150084 * Add the tx control blocks in the pending list to the free list. 13559da57d7bSbt150084 */ 13569da57d7bSbt150084 ixgbe_put_free_list(tx_ring, &pending_list); 13579da57d7bSbt150084 13589da57d7bSbt150084 return (desc_num); 13599da57d7bSbt150084 } 13609da57d7bSbt150084 13619da57d7bSbt150084 /* 13629da57d7bSbt150084 * ixgbe_tx_recycle_head_wb 13639da57d7bSbt150084 * 13649da57d7bSbt150084 * Check the head write-back, and recycle all the transmitted 13659da57d7bSbt150084 * tx descriptors and tx control blocks. 13669da57d7bSbt150084 */ 13679da57d7bSbt150084 uint32_t 13689da57d7bSbt150084 ixgbe_tx_recycle_head_wb(ixgbe_tx_ring_t *tx_ring) 13699da57d7bSbt150084 { 13709da57d7bSbt150084 uint32_t index; 13719da57d7bSbt150084 uint32_t head_wb; 13729da57d7bSbt150084 int desc_num; 13739da57d7bSbt150084 tx_control_block_t *tcb; 13749da57d7bSbt150084 link_list_t pending_list; 1375ea65739eSchenlu chen - Sun Microsystems - Beijing China ixgbe_t *ixgbe = tx_ring->ixgbe; 13769da57d7bSbt150084 1377da14cebeSEric Cheng mutex_enter(&tx_ring->recycle_lock); 13789da57d7bSbt150084 13799da57d7bSbt150084 ASSERT(tx_ring->tbd_free <= tx_ring->ring_size); 13809da57d7bSbt150084 13819da57d7bSbt150084 if (tx_ring->tbd_free == tx_ring->ring_size) { 13829da57d7bSbt150084 tx_ring->recycle_fail = 0; 13839da57d7bSbt150084 tx_ring->stall_watchdog = 0; 1384da14cebeSEric Cheng if (tx_ring->reschedule) { 1385da14cebeSEric Cheng tx_ring->reschedule = B_FALSE; 1386ea65739eSchenlu chen - Sun Microsystems - Beijing China mac_tx_ring_update(ixgbe->mac_hdl, 1387da14cebeSEric Cheng tx_ring->ring_handle); 1388da14cebeSEric Cheng } 13899da57d7bSbt150084 mutex_exit(&tx_ring->recycle_lock); 13909da57d7bSbt150084 return (0); 13919da57d7bSbt150084 } 13929da57d7bSbt150084 13939da57d7bSbt150084 /* 13949da57d7bSbt150084 * Sync the DMA buffer of the tx descriptor ring 13959da57d7bSbt150084 * 13969da57d7bSbt150084 * Note: For head write-back mode, the tx descriptors will not 13979da57d7bSbt150084 * be written back, but the head write-back value is stored at 13989da57d7bSbt150084 * the last extra tbd at the end of the DMA area, we still need 13999da57d7bSbt150084 * to sync the head write-back value for kernel. 14009da57d7bSbt150084 * 14019da57d7bSbt150084 * DMA_SYNC(&tx_ring->tbd_area, DDI_DMA_SYNC_FORKERNEL); 14029da57d7bSbt150084 */ 14039da57d7bSbt150084 (void) ddi_dma_sync(tx_ring->tbd_area.dma_handle, 14049da57d7bSbt150084 sizeof (union ixgbe_adv_tx_desc) * tx_ring->ring_size, 14059da57d7bSbt150084 sizeof (uint32_t), 14069da57d7bSbt150084 DDI_DMA_SYNC_FORKERNEL); 14079da57d7bSbt150084 14089da57d7bSbt150084 if (ixgbe_check_dma_handle(tx_ring->tbd_area.dma_handle) != DDI_FM_OK) { 140919843f01SPaul Guo mutex_exit(&tx_ring->recycle_lock); 1410ea65739eSchenlu chen - Sun Microsystems - Beijing China ddi_fm_service_impact(ixgbe->dip, 14119da57d7bSbt150084 DDI_SERVICE_DEGRADED); 141262e6e1adSPaul Guo atomic_or_32(&ixgbe->ixgbe_state, IXGBE_ERROR); 141362e6e1adSPaul Guo return (0); 14149da57d7bSbt150084 } 14159da57d7bSbt150084 14169da57d7bSbt150084 LINK_LIST_INIT(&pending_list); 14179da57d7bSbt150084 desc_num = 0; 14189da57d7bSbt150084 index = tx_ring->tbd_head; /* Next index to clean */ 14199da57d7bSbt150084 14209da57d7bSbt150084 /* 14219da57d7bSbt150084 * Get the value of head write-back 14229da57d7bSbt150084 */ 14239da57d7bSbt150084 head_wb = *tx_ring->tbd_head_wb; 14249da57d7bSbt150084 while (index != head_wb) { 14259da57d7bSbt150084 tcb = tx_ring->work_list[index]; 14269da57d7bSbt150084 ASSERT(tcb != NULL); 14279da57d7bSbt150084 14289da57d7bSbt150084 if (OFFSET(index, head_wb, tx_ring->ring_size) < 14299da57d7bSbt150084 tcb->desc_num) { 14309da57d7bSbt150084 /* 14319da57d7bSbt150084 * The current tx control block is not 14329da57d7bSbt150084 * completely transmitted, stop recycling 14339da57d7bSbt150084 */ 14349da57d7bSbt150084 break; 14359da57d7bSbt150084 } 14369da57d7bSbt150084 14379da57d7bSbt150084 /* 14389da57d7bSbt150084 * Strip off the tx control block from the work list, 14399da57d7bSbt150084 * and add it to the pending list. 14409da57d7bSbt150084 */ 14419da57d7bSbt150084 tx_ring->work_list[index] = NULL; 14429da57d7bSbt150084 LIST_PUSH_TAIL(&pending_list, &tcb->link); 14439da57d7bSbt150084 14449da57d7bSbt150084 /* 14459da57d7bSbt150084 * Advance the index of the tx descriptor ring 14469da57d7bSbt150084 */ 14479da57d7bSbt150084 index = NEXT_INDEX(index, tcb->desc_num, tx_ring->ring_size); 14489da57d7bSbt150084 14499da57d7bSbt150084 /* 14509da57d7bSbt150084 * Count the total number of the tx descriptors recycled 14519da57d7bSbt150084 */ 14529da57d7bSbt150084 desc_num += tcb->desc_num; 14539da57d7bSbt150084 } 14549da57d7bSbt150084 14559da57d7bSbt150084 /* 14569da57d7bSbt150084 * If no tx descriptors are recycled, no need to do more processing 14579da57d7bSbt150084 */ 14589da57d7bSbt150084 if (desc_num == 0) { 14599da57d7bSbt150084 tx_ring->recycle_fail++; 14609da57d7bSbt150084 mutex_exit(&tx_ring->recycle_lock); 14619da57d7bSbt150084 return (0); 14629da57d7bSbt150084 } 14639da57d7bSbt150084 14649da57d7bSbt150084 tx_ring->recycle_fail = 0; 14659da57d7bSbt150084 tx_ring->stall_watchdog = 0; 14669da57d7bSbt150084 14679da57d7bSbt150084 /* 14689da57d7bSbt150084 * Update the head index of the tx descriptor ring 14699da57d7bSbt150084 */ 14709da57d7bSbt150084 tx_ring->tbd_head = index; 14719da57d7bSbt150084 14729da57d7bSbt150084 /* 14739da57d7bSbt150084 * Update the number of the free tx descriptors with atomic operations 14749da57d7bSbt150084 */ 14759da57d7bSbt150084 atomic_add_32(&tx_ring->tbd_free, desc_num); 14769da57d7bSbt150084 1477ea65739eSchenlu chen - Sun Microsystems - Beijing China if ((tx_ring->tbd_free >= ixgbe->tx_resched_thresh) && 1478da14cebeSEric Cheng (tx_ring->reschedule)) { 1479da14cebeSEric Cheng tx_ring->reschedule = B_FALSE; 1480ea65739eSchenlu chen - Sun Microsystems - Beijing China mac_tx_ring_update(ixgbe->mac_hdl, 1481da14cebeSEric Cheng tx_ring->ring_handle); 1482da14cebeSEric Cheng } 14839da57d7bSbt150084 mutex_exit(&tx_ring->recycle_lock); 14849da57d7bSbt150084 14859da57d7bSbt150084 /* 14869da57d7bSbt150084 * Free the resources used by the tx control blocks 14879da57d7bSbt150084 * in the pending list 14889da57d7bSbt150084 */ 14899da57d7bSbt150084 tcb = (tx_control_block_t *)LIST_GET_HEAD(&pending_list); 14909da57d7bSbt150084 while (tcb) { 14919da57d7bSbt150084 /* 14929da57d7bSbt150084 * Release the resources occupied by the tx control block 14939da57d7bSbt150084 */ 14949da57d7bSbt150084 ixgbe_free_tcb(tcb); 14959da57d7bSbt150084 14969da57d7bSbt150084 tcb = (tx_control_block_t *) 14979da57d7bSbt150084 LIST_GET_NEXT(&pending_list, &tcb->link); 14989da57d7bSbt150084 } 14999da57d7bSbt150084 15009da57d7bSbt150084 /* 15019da57d7bSbt150084 * Add the tx control blocks in the pending list to the free list. 15029da57d7bSbt150084 */ 15039da57d7bSbt150084 ixgbe_put_free_list(tx_ring, &pending_list); 15049da57d7bSbt150084 15059da57d7bSbt150084 return (desc_num); 15069da57d7bSbt150084 } 15079da57d7bSbt150084 15089da57d7bSbt150084 /* 15099da57d7bSbt150084 * ixgbe_free_tcb - free up the tx control block 15109da57d7bSbt150084 * 15119da57d7bSbt150084 * Free the resources of the tx control block, including 15129da57d7bSbt150084 * unbind the previously bound DMA handle, and reset other 15139da57d7bSbt150084 * control fields. 15149da57d7bSbt150084 */ 15159da57d7bSbt150084 void 15169da57d7bSbt150084 ixgbe_free_tcb(tx_control_block_t *tcb) 15179da57d7bSbt150084 { 15189da57d7bSbt150084 switch (tcb->tx_type) { 15199da57d7bSbt150084 case USE_COPY: 15209da57d7bSbt150084 /* 15219da57d7bSbt150084 * Reset the buffer length that is used for copy 15229da57d7bSbt150084 */ 15239da57d7bSbt150084 tcb->tx_buf.len = 0; 15249da57d7bSbt150084 break; 15259da57d7bSbt150084 case USE_DMA: 15269da57d7bSbt150084 /* 15279da57d7bSbt150084 * Release the DMA resource that is used for 15289da57d7bSbt150084 * DMA binding. 15299da57d7bSbt150084 */ 15309da57d7bSbt150084 (void) ddi_dma_unbind_handle(tcb->tx_dma_handle); 15319da57d7bSbt150084 break; 15329da57d7bSbt150084 default: 15339da57d7bSbt150084 break; 15349da57d7bSbt150084 } 15359da57d7bSbt150084 15369da57d7bSbt150084 /* 15379da57d7bSbt150084 * Free the mblk 15389da57d7bSbt150084 */ 15399da57d7bSbt150084 if (tcb->mp != NULL) { 15409da57d7bSbt150084 freemsg(tcb->mp); 15419da57d7bSbt150084 tcb->mp = NULL; 15429da57d7bSbt150084 } 15439da57d7bSbt150084 15449da57d7bSbt150084 tcb->tx_type = USE_NONE; 1545edf70dc9SPaul Guo tcb->last_index = MAX_TX_RING_SIZE; 15469da57d7bSbt150084 tcb->frag_num = 0; 15479da57d7bSbt150084 tcb->desc_num = 0; 15489da57d7bSbt150084 } 15499da57d7bSbt150084 15509da57d7bSbt150084 /* 15519da57d7bSbt150084 * ixgbe_get_free_list - Get a free tx control block from the free list 15529da57d7bSbt150084 * 15539da57d7bSbt150084 * The atomic operation on the number of the available tx control block 15549da57d7bSbt150084 * in the free list is used to keep this routine mutual exclusive with 15559da57d7bSbt150084 * the routine ixgbe_put_check_list. 15569da57d7bSbt150084 */ 15579da57d7bSbt150084 static tx_control_block_t * 15589da57d7bSbt150084 ixgbe_get_free_list(ixgbe_tx_ring_t *tx_ring) 15599da57d7bSbt150084 { 15609da57d7bSbt150084 tx_control_block_t *tcb; 15619da57d7bSbt150084 15629da57d7bSbt150084 /* 15639da57d7bSbt150084 * Check and update the number of the free tx control block 15649da57d7bSbt150084 * in the free list. 15659da57d7bSbt150084 */ 15669da57d7bSbt150084 if (ixgbe_atomic_reserve(&tx_ring->tcb_free, 1) < 0) 15679da57d7bSbt150084 return (NULL); 15689da57d7bSbt150084 15699da57d7bSbt150084 mutex_enter(&tx_ring->tcb_head_lock); 15709da57d7bSbt150084 15719da57d7bSbt150084 tcb = tx_ring->free_list[tx_ring->tcb_head]; 15729da57d7bSbt150084 ASSERT(tcb != NULL); 15739da57d7bSbt150084 tx_ring->free_list[tx_ring->tcb_head] = NULL; 15749da57d7bSbt150084 tx_ring->tcb_head = NEXT_INDEX(tx_ring->tcb_head, 1, 15759da57d7bSbt150084 tx_ring->free_list_size); 15769da57d7bSbt150084 15779da57d7bSbt150084 mutex_exit(&tx_ring->tcb_head_lock); 15789da57d7bSbt150084 15799da57d7bSbt150084 return (tcb); 15809da57d7bSbt150084 } 15819da57d7bSbt150084 15829da57d7bSbt150084 /* 15839da57d7bSbt150084 * ixgbe_put_free_list 15849da57d7bSbt150084 * 15859da57d7bSbt150084 * Put a list of used tx control blocks back to the free list 15869da57d7bSbt150084 * 15879da57d7bSbt150084 * A mutex is used here to ensure the serialization. The mutual exclusion 15889da57d7bSbt150084 * between ixgbe_get_free_list and ixgbe_put_free_list is implemented with 15899da57d7bSbt150084 * the atomic operation on the counter tcb_free. 15909da57d7bSbt150084 */ 15919da57d7bSbt150084 void 15929da57d7bSbt150084 ixgbe_put_free_list(ixgbe_tx_ring_t *tx_ring, link_list_t *pending_list) 15939da57d7bSbt150084 { 15949da57d7bSbt150084 uint32_t index; 15959da57d7bSbt150084 int tcb_num; 15969da57d7bSbt150084 tx_control_block_t *tcb; 15979da57d7bSbt150084 15989da57d7bSbt150084 mutex_enter(&tx_ring->tcb_tail_lock); 15999da57d7bSbt150084 16009da57d7bSbt150084 index = tx_ring->tcb_tail; 16019da57d7bSbt150084 16029da57d7bSbt150084 tcb_num = 0; 16039da57d7bSbt150084 tcb = (tx_control_block_t *)LIST_POP_HEAD(pending_list); 16049da57d7bSbt150084 while (tcb != NULL) { 16059da57d7bSbt150084 ASSERT(tx_ring->free_list[index] == NULL); 16069da57d7bSbt150084 tx_ring->free_list[index] = tcb; 16079da57d7bSbt150084 16089da57d7bSbt150084 tcb_num++; 16099da57d7bSbt150084 16109da57d7bSbt150084 index = NEXT_INDEX(index, 1, tx_ring->free_list_size); 16119da57d7bSbt150084 16129da57d7bSbt150084 tcb = (tx_control_block_t *)LIST_POP_HEAD(pending_list); 16139da57d7bSbt150084 } 16149da57d7bSbt150084 16159da57d7bSbt150084 tx_ring->tcb_tail = index; 16169da57d7bSbt150084 16179da57d7bSbt150084 /* 16189da57d7bSbt150084 * Update the number of the free tx control block 16199da57d7bSbt150084 * in the free list. This operation must be placed 16209da57d7bSbt150084 * under the protection of the lock. 16219da57d7bSbt150084 */ 16229da57d7bSbt150084 atomic_add_32(&tx_ring->tcb_free, tcb_num); 16239da57d7bSbt150084 16249da57d7bSbt150084 mutex_exit(&tx_ring->tcb_tail_lock); 16259da57d7bSbt150084 } 1626