1*62dadd65SYuri Pankov /*
2*62dadd65SYuri Pankov  * Copyright (C) 2007 VMware, Inc. All rights reserved.
3*62dadd65SYuri Pankov  *
4*62dadd65SYuri Pankov  * The contents of this file are subject to the terms of the Common
5*62dadd65SYuri Pankov  * Development and Distribution License (the "License") version 1.0
6*62dadd65SYuri Pankov  * and no later version.  You may not use this file except in
7*62dadd65SYuri Pankov  * compliance with the License.
8*62dadd65SYuri Pankov  *
9*62dadd65SYuri Pankov  * You can obtain a copy of the License at
10*62dadd65SYuri Pankov  *         http://www.opensource.org/licenses/cddl1.php
11*62dadd65SYuri Pankov  *
12*62dadd65SYuri Pankov  * See the License for the specific language governing permissions
13*62dadd65SYuri Pankov  * and limitations under the License.
14*62dadd65SYuri Pankov  */
15*62dadd65SYuri Pankov 
16*62dadd65SYuri Pankov /*
17*62dadd65SYuri Pankov  * Copyright (c) 2012 by Delphix. All rights reserved.
18*62dadd65SYuri Pankov  */
19*62dadd65SYuri Pankov 
20*62dadd65SYuri Pankov #include <vmxnet3.h>
21*62dadd65SYuri Pankov 
22*62dadd65SYuri Pankov typedef enum vmxnet3_txstatus {
23*62dadd65SYuri Pankov 	VMXNET3_TX_OK,
24*62dadd65SYuri Pankov 	VMXNET3_TX_FAILURE,
25*62dadd65SYuri Pankov 	VMXNET3_TX_PULLUP,
26*62dadd65SYuri Pankov 	VMXNET3_TX_RINGFULL
27*62dadd65SYuri Pankov } vmxnet3_txstatus;
28*62dadd65SYuri Pankov 
29*62dadd65SYuri Pankov typedef struct vmxnet3_offload_t {
30*62dadd65SYuri Pankov 	uint16_t om;
31*62dadd65SYuri Pankov 	uint16_t hlen;
32*62dadd65SYuri Pankov 	uint16_t msscof;
33*62dadd65SYuri Pankov } vmxnet3_offload_t;
34*62dadd65SYuri Pankov 
35*62dadd65SYuri Pankov /*
36*62dadd65SYuri Pankov  * vmxnet3_txqueue_init --
37*62dadd65SYuri Pankov  *
38*62dadd65SYuri Pankov  *    Initialize a TxQueue. Currently nothing needs to be done.
39*62dadd65SYuri Pankov  *
40*62dadd65SYuri Pankov  * Results:
41*62dadd65SYuri Pankov  *    DDI_SUCCESS.
42*62dadd65SYuri Pankov  *
43*62dadd65SYuri Pankov  * Side effects:
44*62dadd65SYuri Pankov  *    None.
45*62dadd65SYuri Pankov  */
46*62dadd65SYuri Pankov /* ARGSUSED */
47*62dadd65SYuri Pankov int
48*62dadd65SYuri Pankov vmxnet3_txqueue_init(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq)
49*62dadd65SYuri Pankov {
50*62dadd65SYuri Pankov 	return (DDI_SUCCESS);
51*62dadd65SYuri Pankov }
52*62dadd65SYuri Pankov 
53*62dadd65SYuri Pankov /*
54*62dadd65SYuri Pankov  * vmxnet3_txqueue_fini --
55*62dadd65SYuri Pankov  *
56*62dadd65SYuri Pankov  *    Finish a TxQueue by freeing all pending Tx.
57*62dadd65SYuri Pankov  *
58*62dadd65SYuri Pankov  * Results:
59*62dadd65SYuri Pankov  *    DDI_SUCCESS.
60*62dadd65SYuri Pankov  *
61*62dadd65SYuri Pankov  * Side effects:
62*62dadd65SYuri Pankov  *    None.
63*62dadd65SYuri Pankov  */
64*62dadd65SYuri Pankov void
65*62dadd65SYuri Pankov vmxnet3_txqueue_fini(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq)
66*62dadd65SYuri Pankov {
67*62dadd65SYuri Pankov 	unsigned int i;
68*62dadd65SYuri Pankov 
69*62dadd65SYuri Pankov 	ASSERT(!dp->devEnabled);
70*62dadd65SYuri Pankov 
71*62dadd65SYuri Pankov 	for (i = 0; i < txq->cmdRing.size; i++) {
72*62dadd65SYuri Pankov 		mblk_t *mp = txq->metaRing[i].mp;
73*62dadd65SYuri Pankov 		if (mp) {
74*62dadd65SYuri Pankov 			freemsg(mp);
75*62dadd65SYuri Pankov 		}
76*62dadd65SYuri Pankov 	}
77*62dadd65SYuri Pankov }
78*62dadd65SYuri Pankov 
79*62dadd65SYuri Pankov /*
80*62dadd65SYuri Pankov  * vmxnet3_tx_prepare_offload --
81*62dadd65SYuri Pankov  *
82*62dadd65SYuri Pankov  *    Build the offload context of a msg.
83*62dadd65SYuri Pankov  *
84*62dadd65SYuri Pankov  * Results:
85*62dadd65SYuri Pankov  *    0 if everything went well.
86*62dadd65SYuri Pankov  *    +n if n bytes need to be pulled up.
87*62dadd65SYuri Pankov  *    -1 in case of error (not used).
88*62dadd65SYuri Pankov  *
89*62dadd65SYuri Pankov  * Side effects:
90*62dadd65SYuri Pankov  *    None.
91*62dadd65SYuri Pankov  */
92*62dadd65SYuri Pankov static int
93*62dadd65SYuri Pankov vmxnet3_tx_prepare_offload(vmxnet3_softc_t *dp, vmxnet3_offload_t *ol,
94*62dadd65SYuri Pankov     mblk_t *mp)
95*62dadd65SYuri Pankov {
96*62dadd65SYuri Pankov 	int ret = 0;
97*62dadd65SYuri Pankov 	uint32_t start, stuff, value, flags, lsoflags, mss;
98*62dadd65SYuri Pankov 
99*62dadd65SYuri Pankov 	ol->om = VMXNET3_OM_NONE;
100*62dadd65SYuri Pankov 	ol->hlen = 0;
101*62dadd65SYuri Pankov 	ol->msscof = 0;
102*62dadd65SYuri Pankov 
103*62dadd65SYuri Pankov 	hcksum_retrieve(mp, NULL, NULL, &start, &stuff, NULL, &value, &flags);
104*62dadd65SYuri Pankov 
105*62dadd65SYuri Pankov 	mac_lso_get(mp, &mss, &lsoflags);
106*62dadd65SYuri Pankov 	if (lsoflags & HW_LSO) {
107*62dadd65SYuri Pankov 		flags |= HW_LSO;
108*62dadd65SYuri Pankov 	}
109*62dadd65SYuri Pankov 
110*62dadd65SYuri Pankov 	if (flags) {
111*62dadd65SYuri Pankov 		struct ether_vlan_header *eth = (void *) mp->b_rptr;
112*62dadd65SYuri Pankov 		uint8_t ethLen;
113*62dadd65SYuri Pankov 
114*62dadd65SYuri Pankov 		if (eth->ether_tpid == htons(ETHERTYPE_VLAN)) {
115*62dadd65SYuri Pankov 			ethLen = sizeof (struct ether_vlan_header);
116*62dadd65SYuri Pankov 		} else {
117*62dadd65SYuri Pankov 			ethLen = sizeof (struct ether_header);
118*62dadd65SYuri Pankov 		}
119*62dadd65SYuri Pankov 
120*62dadd65SYuri Pankov 		VMXNET3_DEBUG(dp, 4, "flags=0x%x, ethLen=%u, start=%u, "
121*62dadd65SYuri Pankov 		    "stuff=%u, value=%u\n", flags, ethLen, start, stuff, value);
122*62dadd65SYuri Pankov 
123*62dadd65SYuri Pankov 		if (flags & HCK_PARTIALCKSUM) {
124*62dadd65SYuri Pankov 			ol->om = VMXNET3_OM_CSUM;
125*62dadd65SYuri Pankov 			ol->hlen = start + ethLen;
126*62dadd65SYuri Pankov 			ol->msscof = stuff + ethLen;
127*62dadd65SYuri Pankov 		}
128*62dadd65SYuri Pankov 		if (flags & HW_LSO) {
129*62dadd65SYuri Pankov 			mblk_t *mblk = mp;
130*62dadd65SYuri Pankov 			uint8_t *ip, *tcp;
131*62dadd65SYuri Pankov 			uint8_t ipLen, tcpLen;
132*62dadd65SYuri Pankov 
133*62dadd65SYuri Pankov 			/*
134*62dadd65SYuri Pankov 			 * Copy e1000g's behavior:
135*62dadd65SYuri Pankov 			 * - Do not assume all the headers are in the same mblk.
136*62dadd65SYuri Pankov 			 * - Assume each header is always within one mblk.
137*62dadd65SYuri Pankov 			 * - Assume the ethernet header is in the first mblk.
138*62dadd65SYuri Pankov 			 */
139*62dadd65SYuri Pankov 			ip = mblk->b_rptr + ethLen;
140*62dadd65SYuri Pankov 			if (ip >= mblk->b_wptr) {
141*62dadd65SYuri Pankov 				mblk = mblk->b_cont;
142*62dadd65SYuri Pankov 				ip = mblk->b_rptr;
143*62dadd65SYuri Pankov 			}
144*62dadd65SYuri Pankov 			ipLen = IPH_HDR_LENGTH((ipha_t *)ip);
145*62dadd65SYuri Pankov 			tcp = ip + ipLen;
146*62dadd65SYuri Pankov 			if (tcp >= mblk->b_wptr) {
147*62dadd65SYuri Pankov 				mblk = mblk->b_cont;
148*62dadd65SYuri Pankov 				tcp = mblk->b_rptr;
149*62dadd65SYuri Pankov 			}
150*62dadd65SYuri Pankov 			tcpLen = TCP_HDR_LENGTH((tcph_t *)tcp);
151*62dadd65SYuri Pankov 			/* Careful, '>' instead of '>=' here */
152*62dadd65SYuri Pankov 			if (tcp + tcpLen > mblk->b_wptr) {
153*62dadd65SYuri Pankov 				mblk = mblk->b_cont;
154*62dadd65SYuri Pankov 			}
155*62dadd65SYuri Pankov 
156*62dadd65SYuri Pankov 			ol->om = VMXNET3_OM_TSO;
157*62dadd65SYuri Pankov 			ol->hlen = ethLen + ipLen + tcpLen;
158*62dadd65SYuri Pankov 			ol->msscof = DB_LSOMSS(mp);
159*62dadd65SYuri Pankov 
160*62dadd65SYuri Pankov 			if (mblk != mp) {
161*62dadd65SYuri Pankov 				ret = ol->hlen;
162*62dadd65SYuri Pankov 			}
163*62dadd65SYuri Pankov 		}
164*62dadd65SYuri Pankov 	}
165*62dadd65SYuri Pankov 
166*62dadd65SYuri Pankov 	return (ret);
167*62dadd65SYuri Pankov }
168*62dadd65SYuri Pankov 
169*62dadd65SYuri Pankov /*
170*62dadd65SYuri Pankov  * vmxnet3_tx_one --
171*62dadd65SYuri Pankov  *
172*62dadd65SYuri Pankov  *    Map a msg into the Tx command ring of a vmxnet3 device.
173*62dadd65SYuri Pankov  *
174*62dadd65SYuri Pankov  * Results:
175*62dadd65SYuri Pankov  *    VMXNET3_TX_OK if everything went well.
176*62dadd65SYuri Pankov  *    VMXNET3_TX_RINGFULL if the ring is nearly full.
177*62dadd65SYuri Pankov  *    VMXNET3_TX_PULLUP if the msg is overfragmented.
178*62dadd65SYuri Pankov  *    VMXNET3_TX_FAILURE if there was a DMA or offload error.
179*62dadd65SYuri Pankov  *
180*62dadd65SYuri Pankov  * Side effects:
181*62dadd65SYuri Pankov  *    The ring is filled if VMXNET3_TX_OK is returned.
182*62dadd65SYuri Pankov  */
183*62dadd65SYuri Pankov static vmxnet3_txstatus
184*62dadd65SYuri Pankov vmxnet3_tx_one(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq,
185*62dadd65SYuri Pankov     vmxnet3_offload_t *ol, mblk_t *mp)
186*62dadd65SYuri Pankov {
187*62dadd65SYuri Pankov 	int ret = VMXNET3_TX_OK;
188*62dadd65SYuri Pankov 	unsigned int frags = 0, totLen = 0;
189*62dadd65SYuri Pankov 	vmxnet3_cmdring_t *cmdRing = &txq->cmdRing;
190*62dadd65SYuri Pankov 	Vmxnet3_TxQueueCtrl *txqCtrl = txq->sharedCtrl;
191*62dadd65SYuri Pankov 	Vmxnet3_GenericDesc *txDesc;
192*62dadd65SYuri Pankov 	uint16_t sopIdx, eopIdx;
193*62dadd65SYuri Pankov 	uint8_t sopGen, curGen;
194*62dadd65SYuri Pankov 	mblk_t *mblk;
195*62dadd65SYuri Pankov 
196*62dadd65SYuri Pankov 	mutex_enter(&dp->txLock);
197*62dadd65SYuri Pankov 
198*62dadd65SYuri Pankov 	sopIdx = eopIdx = cmdRing->next2fill;
199*62dadd65SYuri Pankov 	sopGen = cmdRing->gen;
200*62dadd65SYuri Pankov 	curGen = !cmdRing->gen;
201*62dadd65SYuri Pankov 
202*62dadd65SYuri Pankov 	for (mblk = mp; mblk != NULL; mblk = mblk->b_cont) {
203*62dadd65SYuri Pankov 		unsigned int len = MBLKL(mblk);
204*62dadd65SYuri Pankov 		ddi_dma_cookie_t cookie;
205*62dadd65SYuri Pankov 		uint_t cookieCount;
206*62dadd65SYuri Pankov 
207*62dadd65SYuri Pankov 		if (len) {
208*62dadd65SYuri Pankov 			totLen += len;
209*62dadd65SYuri Pankov 		} else {
210*62dadd65SYuri Pankov 			continue;
211*62dadd65SYuri Pankov 		}
212*62dadd65SYuri Pankov 
213*62dadd65SYuri Pankov 		if (ddi_dma_addr_bind_handle(dp->txDmaHandle, NULL,
214*62dadd65SYuri Pankov 		    (caddr_t)mblk->b_rptr, len,
215*62dadd65SYuri Pankov 		    DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL,
216*62dadd65SYuri Pankov 		    &cookie, &cookieCount) != DDI_DMA_MAPPED) {
217*62dadd65SYuri Pankov 			VMXNET3_WARN(dp, "ddi_dma_addr_bind_handle() failed\n");
218*62dadd65SYuri Pankov 			ret = VMXNET3_TX_FAILURE;
219*62dadd65SYuri Pankov 			goto error;
220*62dadd65SYuri Pankov 		}
221*62dadd65SYuri Pankov 
222*62dadd65SYuri Pankov 		ASSERT(cookieCount);
223*62dadd65SYuri Pankov 
224*62dadd65SYuri Pankov 		do {
225*62dadd65SYuri Pankov 			uint64_t addr = cookie.dmac_laddress;
226*62dadd65SYuri Pankov 			size_t len = cookie.dmac_size;
227*62dadd65SYuri Pankov 
228*62dadd65SYuri Pankov 			do {
229*62dadd65SYuri Pankov 				uint32_t dw2, dw3;
230*62dadd65SYuri Pankov 				size_t chunkLen;
231*62dadd65SYuri Pankov 
232*62dadd65SYuri Pankov 				ASSERT(!txq->metaRing[eopIdx].mp);
233*62dadd65SYuri Pankov 				ASSERT(cmdRing->avail - frags);
234*62dadd65SYuri Pankov 
235*62dadd65SYuri Pankov 				if (frags >= cmdRing->size - 1 ||
236*62dadd65SYuri Pankov 				    (ol->om != VMXNET3_OM_TSO &&
237*62dadd65SYuri Pankov 				    frags >= VMXNET3_MAX_TXD_PER_PKT)) {
238*62dadd65SYuri Pankov 					VMXNET3_DEBUG(dp, 2,
239*62dadd65SYuri Pankov 					    "overfragmented mp (%u)\n", frags);
240*62dadd65SYuri Pankov 					(void) ddi_dma_unbind_handle(
241*62dadd65SYuri Pankov 					    dp->txDmaHandle);
242*62dadd65SYuri Pankov 					ret = VMXNET3_TX_PULLUP;
243*62dadd65SYuri Pankov 					goto error;
244*62dadd65SYuri Pankov 				}
245*62dadd65SYuri Pankov 				if (cmdRing->avail - frags <= 1) {
246*62dadd65SYuri Pankov 					dp->txMustResched = B_TRUE;
247*62dadd65SYuri Pankov 					(void) ddi_dma_unbind_handle(
248*62dadd65SYuri Pankov 					    dp->txDmaHandle);
249*62dadd65SYuri Pankov 					ret = VMXNET3_TX_RINGFULL;
250*62dadd65SYuri Pankov 					goto error;
251*62dadd65SYuri Pankov 				}
252*62dadd65SYuri Pankov 
253*62dadd65SYuri Pankov 				if (len > VMXNET3_MAX_TX_BUF_SIZE) {
254*62dadd65SYuri Pankov 					chunkLen = VMXNET3_MAX_TX_BUF_SIZE;
255*62dadd65SYuri Pankov 				} else {
256*62dadd65SYuri Pankov 					chunkLen = len;
257*62dadd65SYuri Pankov 				}
258*62dadd65SYuri Pankov 
259*62dadd65SYuri Pankov 				frags++;
260*62dadd65SYuri Pankov 				eopIdx = cmdRing->next2fill;
261*62dadd65SYuri Pankov 
262*62dadd65SYuri Pankov 				txDesc = VMXNET3_GET_DESC(cmdRing, eopIdx);
263*62dadd65SYuri Pankov 				ASSERT(txDesc->txd.gen != cmdRing->gen);
264*62dadd65SYuri Pankov 
265*62dadd65SYuri Pankov 				/* txd.addr */
266*62dadd65SYuri Pankov 				txDesc->txd.addr = addr;
267*62dadd65SYuri Pankov 				/* txd.dw2 */
268*62dadd65SYuri Pankov 				dw2 = chunkLen == VMXNET3_MAX_TX_BUF_SIZE ?
269*62dadd65SYuri Pankov 				    0 : chunkLen;
270*62dadd65SYuri Pankov 				dw2 |= curGen << VMXNET3_TXD_GEN_SHIFT;
271*62dadd65SYuri Pankov 				txDesc->dword[2] = dw2;
272*62dadd65SYuri Pankov 				ASSERT(txDesc->txd.len == len ||
273*62dadd65SYuri Pankov 				    txDesc->txd.len == 0);
274*62dadd65SYuri Pankov 				/* txd.dw3 */
275*62dadd65SYuri Pankov 				dw3 = 0;
276*62dadd65SYuri Pankov 				txDesc->dword[3] = dw3;
277*62dadd65SYuri Pankov 
278*62dadd65SYuri Pankov 				VMXNET3_INC_RING_IDX(cmdRing,
279*62dadd65SYuri Pankov 				    cmdRing->next2fill);
280*62dadd65SYuri Pankov 				curGen = cmdRing->gen;
281*62dadd65SYuri Pankov 
282*62dadd65SYuri Pankov 				addr += chunkLen;
283*62dadd65SYuri Pankov 				len -= chunkLen;
284*62dadd65SYuri Pankov 			} while (len);
285*62dadd65SYuri Pankov 
286*62dadd65SYuri Pankov 			if (--cookieCount) {
287*62dadd65SYuri Pankov 				ddi_dma_nextcookie(dp->txDmaHandle, &cookie);
288*62dadd65SYuri Pankov 			}
289*62dadd65SYuri Pankov 		} while (cookieCount);
290*62dadd65SYuri Pankov 
291*62dadd65SYuri Pankov 		(void) ddi_dma_unbind_handle(dp->txDmaHandle);
292*62dadd65SYuri Pankov 	}
293*62dadd65SYuri Pankov 
294*62dadd65SYuri Pankov 	/* Update the EOP descriptor */
295*62dadd65SYuri Pankov 	txDesc = VMXNET3_GET_DESC(cmdRing, eopIdx);
296*62dadd65SYuri Pankov 	txDesc->dword[3] |= VMXNET3_TXD_CQ | VMXNET3_TXD_EOP;
297*62dadd65SYuri Pankov 
298*62dadd65SYuri Pankov 	/* Update the SOP descriptor. Must be done last */
299*62dadd65SYuri Pankov 	txDesc = VMXNET3_GET_DESC(cmdRing, sopIdx);
300*62dadd65SYuri Pankov 	if (ol->om == VMXNET3_OM_TSO && txDesc->txd.len != 0 &&
301*62dadd65SYuri Pankov 	    txDesc->txd.len < ol->hlen) {
302*62dadd65SYuri Pankov 		ret = VMXNET3_TX_FAILURE;
303*62dadd65SYuri Pankov 		goto error;
304*62dadd65SYuri Pankov 	}
305*62dadd65SYuri Pankov 	txDesc->txd.om = ol->om;
306*62dadd65SYuri Pankov 	txDesc->txd.hlen = ol->hlen;
307*62dadd65SYuri Pankov 	txDesc->txd.msscof = ol->msscof;
308*62dadd65SYuri Pankov 	membar_producer();
309*62dadd65SYuri Pankov 	txDesc->txd.gen = sopGen;
310*62dadd65SYuri Pankov 
311*62dadd65SYuri Pankov 	/* Update the meta ring & metadata */
312*62dadd65SYuri Pankov 	txq->metaRing[sopIdx].mp = mp;
313*62dadd65SYuri Pankov 	txq->metaRing[eopIdx].sopIdx = sopIdx;
314*62dadd65SYuri Pankov 	txq->metaRing[eopIdx].frags = frags;
315*62dadd65SYuri Pankov 	cmdRing->avail -= frags;
316*62dadd65SYuri Pankov 	if (ol->om == VMXNET3_OM_TSO) {
317*62dadd65SYuri Pankov 		txqCtrl->txNumDeferred +=
318*62dadd65SYuri Pankov 		    (totLen - ol->hlen + ol->msscof - 1) / ol->msscof;
319*62dadd65SYuri Pankov 	} else {
320*62dadd65SYuri Pankov 		txqCtrl->txNumDeferred++;
321*62dadd65SYuri Pankov 	}
322*62dadd65SYuri Pankov 
323*62dadd65SYuri Pankov 	VMXNET3_DEBUG(dp, 3, "tx 0x%p on [%u;%u]\n", mp, sopIdx, eopIdx);
324*62dadd65SYuri Pankov 
325*62dadd65SYuri Pankov 	goto done;
326*62dadd65SYuri Pankov 
327*62dadd65SYuri Pankov error:
328*62dadd65SYuri Pankov 	/* Reverse the generation bits */
329*62dadd65SYuri Pankov 	while (sopIdx != cmdRing->next2fill) {
330*62dadd65SYuri Pankov 		VMXNET3_DEC_RING_IDX(cmdRing, cmdRing->next2fill);
331*62dadd65SYuri Pankov 		txDesc = VMXNET3_GET_DESC(cmdRing, cmdRing->next2fill);
332*62dadd65SYuri Pankov 		txDesc->txd.gen = !cmdRing->gen;
333*62dadd65SYuri Pankov 	}
334*62dadd65SYuri Pankov 
335*62dadd65SYuri Pankov done:
336*62dadd65SYuri Pankov 	mutex_exit(&dp->txLock);
337*62dadd65SYuri Pankov 
338*62dadd65SYuri Pankov 	return (ret);
339*62dadd65SYuri Pankov }
340*62dadd65SYuri Pankov 
341*62dadd65SYuri Pankov /*
342*62dadd65SYuri Pankov  * vmxnet3_tx --
343*62dadd65SYuri Pankov  *
344*62dadd65SYuri Pankov  *    Send packets on a vmxnet3 device.
345*62dadd65SYuri Pankov  *
346*62dadd65SYuri Pankov  * Results:
347*62dadd65SYuri Pankov  *    NULL in case of success or failure.
348*62dadd65SYuri Pankov  *    The mps to be retransmitted later if the ring is full.
349*62dadd65SYuri Pankov  *
350*62dadd65SYuri Pankov  * Side effects:
351*62dadd65SYuri Pankov  *    None.
352*62dadd65SYuri Pankov  */
353*62dadd65SYuri Pankov mblk_t *
354*62dadd65SYuri Pankov vmxnet3_tx(void *data, mblk_t *mps)
355*62dadd65SYuri Pankov {
356*62dadd65SYuri Pankov 	vmxnet3_softc_t *dp = data;
357*62dadd65SYuri Pankov 	vmxnet3_txqueue_t *txq = &dp->txQueue;
358*62dadd65SYuri Pankov 	vmxnet3_cmdring_t *cmdRing = &txq->cmdRing;
359*62dadd65SYuri Pankov 	Vmxnet3_TxQueueCtrl *txqCtrl = txq->sharedCtrl;
360*62dadd65SYuri Pankov 	vmxnet3_txstatus status = VMXNET3_TX_OK;
361*62dadd65SYuri Pankov 	mblk_t *mp;
362*62dadd65SYuri Pankov 
363*62dadd65SYuri Pankov 	ASSERT(mps != NULL);
364*62dadd65SYuri Pankov 
365*62dadd65SYuri Pankov 	do {
366*62dadd65SYuri Pankov 		vmxnet3_offload_t ol;
367*62dadd65SYuri Pankov 		int pullup;
368*62dadd65SYuri Pankov 
369*62dadd65SYuri Pankov 		mp = mps;
370*62dadd65SYuri Pankov 		mps = mp->b_next;
371*62dadd65SYuri Pankov 		mp->b_next = NULL;
372*62dadd65SYuri Pankov 
373*62dadd65SYuri Pankov 		if (DB_TYPE(mp) != M_DATA) {
374*62dadd65SYuri Pankov 			/*
375*62dadd65SYuri Pankov 			 * PR #315560: M_PROTO mblks could be passed for
376*62dadd65SYuri Pankov 			 * some reason. Drop them because we don't understand
377*62dadd65SYuri Pankov 			 * them and because their contents are not Ethernet
378*62dadd65SYuri Pankov 			 * frames anyway.
379*62dadd65SYuri Pankov 			 */
380*62dadd65SYuri Pankov 			ASSERT(B_FALSE);
381*62dadd65SYuri Pankov 			freemsg(mp);
382*62dadd65SYuri Pankov 			continue;
383*62dadd65SYuri Pankov 		}
384*62dadd65SYuri Pankov 
385*62dadd65SYuri Pankov 		/*
386*62dadd65SYuri Pankov 		 * Prepare the offload while we're still handling the original
387*62dadd65SYuri Pankov 		 * message -- msgpullup() discards the metadata afterwards.
388*62dadd65SYuri Pankov 		 */
389*62dadd65SYuri Pankov 		pullup = vmxnet3_tx_prepare_offload(dp, &ol, mp);
390*62dadd65SYuri Pankov 		if (pullup) {
391*62dadd65SYuri Pankov 			mblk_t *new_mp = msgpullup(mp, pullup);
392*62dadd65SYuri Pankov 			atomic_inc_32(&dp->tx_pullup_needed);
393*62dadd65SYuri Pankov 			freemsg(mp);
394*62dadd65SYuri Pankov 			if (new_mp) {
395*62dadd65SYuri Pankov 				mp = new_mp;
396*62dadd65SYuri Pankov 			} else {
397*62dadd65SYuri Pankov 				atomic_inc_32(&dp->tx_pullup_failed);
398*62dadd65SYuri Pankov 				continue;
399*62dadd65SYuri Pankov 			}
400*62dadd65SYuri Pankov 		}
401*62dadd65SYuri Pankov 
402*62dadd65SYuri Pankov 		/*
403*62dadd65SYuri Pankov 		 * Try to map the message in the Tx ring.
404*62dadd65SYuri Pankov 		 * This call might fail for non-fatal reasons.
405*62dadd65SYuri Pankov 		 */
406*62dadd65SYuri Pankov 		status = vmxnet3_tx_one(dp, txq, &ol, mp);
407*62dadd65SYuri Pankov 		if (status == VMXNET3_TX_PULLUP) {
408*62dadd65SYuri Pankov 			/*
409*62dadd65SYuri Pankov 			 * Try one more time after flattening
410*62dadd65SYuri Pankov 			 * the message with msgpullup().
411*62dadd65SYuri Pankov 			 */
412*62dadd65SYuri Pankov 			if (mp->b_cont != NULL) {
413*62dadd65SYuri Pankov 				mblk_t *new_mp = msgpullup(mp, -1);
414*62dadd65SYuri Pankov 				atomic_inc_32(&dp->tx_pullup_needed);
415*62dadd65SYuri Pankov 				freemsg(mp);
416*62dadd65SYuri Pankov 				if (new_mp) {
417*62dadd65SYuri Pankov 					mp = new_mp;
418*62dadd65SYuri Pankov 					status = vmxnet3_tx_one(dp, txq, &ol,
419*62dadd65SYuri Pankov 					    mp);
420*62dadd65SYuri Pankov 				} else {
421*62dadd65SYuri Pankov 					atomic_inc_32(&dp->tx_pullup_failed);
422*62dadd65SYuri Pankov 					continue;
423*62dadd65SYuri Pankov 				}
424*62dadd65SYuri Pankov 			}
425*62dadd65SYuri Pankov 		}
426*62dadd65SYuri Pankov 		if (status != VMXNET3_TX_OK && status != VMXNET3_TX_RINGFULL) {
427*62dadd65SYuri Pankov 			/* Fatal failure, drop it */
428*62dadd65SYuri Pankov 			atomic_inc_32(&dp->tx_error);
429*62dadd65SYuri Pankov 			freemsg(mp);
430*62dadd65SYuri Pankov 		}
431*62dadd65SYuri Pankov 	} while (mps && status != VMXNET3_TX_RINGFULL);
432*62dadd65SYuri Pankov 
433*62dadd65SYuri Pankov 	if (status == VMXNET3_TX_RINGFULL) {
434*62dadd65SYuri Pankov 		atomic_inc_32(&dp->tx_ring_full);
435*62dadd65SYuri Pankov 		mp->b_next = mps;
436*62dadd65SYuri Pankov 		mps = mp;
437*62dadd65SYuri Pankov 	} else {
438*62dadd65SYuri Pankov 		ASSERT(!mps);
439*62dadd65SYuri Pankov 	}
440*62dadd65SYuri Pankov 
441*62dadd65SYuri Pankov 	/* Notify the device */
442*62dadd65SYuri Pankov 	mutex_enter(&dp->txLock);
443*62dadd65SYuri Pankov 	if (txqCtrl->txNumDeferred >= txqCtrl->txThreshold) {
444*62dadd65SYuri Pankov 		txqCtrl->txNumDeferred = 0;
445*62dadd65SYuri Pankov 		VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_TXPROD, cmdRing->next2fill);
446*62dadd65SYuri Pankov 	}
447*62dadd65SYuri Pankov 	mutex_exit(&dp->txLock);
448*62dadd65SYuri Pankov 
449*62dadd65SYuri Pankov 	return (mps);
450*62dadd65SYuri Pankov }
451*62dadd65SYuri Pankov 
452*62dadd65SYuri Pankov /*
453*62dadd65SYuri Pankov  * vmxnet3_tx_complete --
454*62dadd65SYuri Pankov  *
455*62dadd65SYuri Pankov  *    Parse a transmit queue and complete packets.
456*62dadd65SYuri Pankov  *
457*62dadd65SYuri Pankov  * Results:
458*62dadd65SYuri Pankov  *    B_TRUE if Tx must be updated or B_FALSE if no action is required.
459*62dadd65SYuri Pankov  *
460*62dadd65SYuri Pankov  * Side effects:
461*62dadd65SYuri Pankov  *    None.
462*62dadd65SYuri Pankov  */
463*62dadd65SYuri Pankov boolean_t
464*62dadd65SYuri Pankov vmxnet3_tx_complete(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq)
465*62dadd65SYuri Pankov {
466*62dadd65SYuri Pankov 	vmxnet3_cmdring_t *cmdRing = &txq->cmdRing;
467*62dadd65SYuri Pankov 	vmxnet3_compring_t *compRing = &txq->compRing;
468*62dadd65SYuri Pankov 	Vmxnet3_GenericDesc *compDesc;
469*62dadd65SYuri Pankov 	boolean_t completedTx = B_FALSE;
470*62dadd65SYuri Pankov 	boolean_t ret = B_FALSE;
471*62dadd65SYuri Pankov 
472*62dadd65SYuri Pankov 	mutex_enter(&dp->txLock);
473*62dadd65SYuri Pankov 
474*62dadd65SYuri Pankov 	compDesc = VMXNET3_GET_DESC(compRing, compRing->next2comp);
475*62dadd65SYuri Pankov 	while (compDesc->tcd.gen == compRing->gen) {
476*62dadd65SYuri Pankov 		vmxnet3_metatx_t *sopMetaDesc, *eopMetaDesc;
477*62dadd65SYuri Pankov 		uint16_t sopIdx, eopIdx;
478*62dadd65SYuri Pankov 		mblk_t *mp;
479*62dadd65SYuri Pankov 
480*62dadd65SYuri Pankov 		eopIdx = compDesc->tcd.txdIdx;
481*62dadd65SYuri Pankov 		eopMetaDesc = &txq->metaRing[eopIdx];
482*62dadd65SYuri Pankov 		sopIdx = eopMetaDesc->sopIdx;
483*62dadd65SYuri Pankov 		sopMetaDesc = &txq->metaRing[sopIdx];
484*62dadd65SYuri Pankov 
485*62dadd65SYuri Pankov 		ASSERT(eopMetaDesc->frags);
486*62dadd65SYuri Pankov 		cmdRing->avail += eopMetaDesc->frags;
487*62dadd65SYuri Pankov 
488*62dadd65SYuri Pankov 		ASSERT(sopMetaDesc->mp);
489*62dadd65SYuri Pankov 		mp = sopMetaDesc->mp;
490*62dadd65SYuri Pankov 		freemsg(mp);
491*62dadd65SYuri Pankov 
492*62dadd65SYuri Pankov 		eopMetaDesc->sopIdx = 0;
493*62dadd65SYuri Pankov 		eopMetaDesc->frags = 0;
494*62dadd65SYuri Pankov 		sopMetaDesc->mp = NULL;
495*62dadd65SYuri Pankov 
496*62dadd65SYuri Pankov 		completedTx = B_TRUE;
497*62dadd65SYuri Pankov 
498*62dadd65SYuri Pankov 		VMXNET3_DEBUG(dp, 3, "cp 0x%p on [%u;%u]\n", mp, sopIdx,
499*62dadd65SYuri Pankov 		    eopIdx);
500*62dadd65SYuri Pankov 
501*62dadd65SYuri Pankov 		VMXNET3_INC_RING_IDX(compRing, compRing->next2comp);
502*62dadd65SYuri Pankov 		compDesc = VMXNET3_GET_DESC(compRing, compRing->next2comp);
503*62dadd65SYuri Pankov 	}
504*62dadd65SYuri Pankov 
505*62dadd65SYuri Pankov 	if (dp->txMustResched && completedTx) {
506*62dadd65SYuri Pankov 		dp->txMustResched = B_FALSE;
507*62dadd65SYuri Pankov 		ret = B_TRUE;
508*62dadd65SYuri Pankov 	}
509*62dadd65SYuri Pankov 
510*62dadd65SYuri Pankov 	mutex_exit(&dp->txLock);
511*62dadd65SYuri Pankov 
512*62dadd65SYuri Pankov 	return (ret);
513*62dadd65SYuri Pankov }
514