xref: /illumos-gate/usr/src/uts/common/io/vioif/vioif_tx.c (revision 3a82bea40a29a7c4bb431967dbfb7b4f723fa485)
1*3a82bea4SAndy Fiddaman /*
2*3a82bea4SAndy Fiddaman  * This file and its contents are supplied under the terms of the
3*3a82bea4SAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
4*3a82bea4SAndy Fiddaman  * You may only use this file in accordance with the terms of version
5*3a82bea4SAndy Fiddaman  * 1.0 of the CDDL.
6*3a82bea4SAndy Fiddaman  *
7*3a82bea4SAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
8*3a82bea4SAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
9*3a82bea4SAndy Fiddaman  * http://www.illumos.org/license/CDDL.
10*3a82bea4SAndy Fiddaman  */
11*3a82bea4SAndy Fiddaman 
12*3a82bea4SAndy Fiddaman /*
13*3a82bea4SAndy Fiddaman  * Copyright 2013 Nexenta Inc.  All rights reserved.
14*3a82bea4SAndy Fiddaman  * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
15*3a82bea4SAndy Fiddaman  * Copyright 2021 Joyent, Inc.
16*3a82bea4SAndy Fiddaman  * Copyright 2019 Joshua M. Clulow <josh@sysmgr.org>
17*3a82bea4SAndy Fiddaman  * Copyright 2025 Hans Rosenfeld
18*3a82bea4SAndy Fiddaman  * Copyright 2026 Oxide Computer Company
19*3a82bea4SAndy Fiddaman  */
20*3a82bea4SAndy Fiddaman 
21*3a82bea4SAndy Fiddaman /* Based on the NetBSD virtio driver by Minoura Makoto. */
22*3a82bea4SAndy Fiddaman /*
23*3a82bea4SAndy Fiddaman  * Copyright (c) 2010 Minoura Makoto.
24*3a82bea4SAndy Fiddaman  * All rights reserved.
25*3a82bea4SAndy Fiddaman  *
26*3a82bea4SAndy Fiddaman  * Redistribution and use in source and binary forms, with or without
27*3a82bea4SAndy Fiddaman  * modification, are permitted provided that the following conditions
28*3a82bea4SAndy Fiddaman  * are met:
29*3a82bea4SAndy Fiddaman  * 1. Redistributions of source code must retain the above copyright
30*3a82bea4SAndy Fiddaman  *    notice, this list of conditions and the following disclaimer.
31*3a82bea4SAndy Fiddaman  * 2. Redistributions in binary form must reproduce the above copyright
32*3a82bea4SAndy Fiddaman  *    notice, this list of conditions and the following disclaimer in the
33*3a82bea4SAndy Fiddaman  *    documentation and/or other materials provided with the distribution.
34*3a82bea4SAndy Fiddaman  *
35*3a82bea4SAndy Fiddaman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
36*3a82bea4SAndy Fiddaman  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37*3a82bea4SAndy Fiddaman  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
38*3a82bea4SAndy Fiddaman  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
39*3a82bea4SAndy Fiddaman  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
40*3a82bea4SAndy Fiddaman  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
41*3a82bea4SAndy Fiddaman  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
42*3a82bea4SAndy Fiddaman  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
43*3a82bea4SAndy Fiddaman  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
44*3a82bea4SAndy Fiddaman  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45*3a82bea4SAndy Fiddaman  */
46*3a82bea4SAndy Fiddaman 
47*3a82bea4SAndy Fiddaman /*
48*3a82bea4SAndy Fiddaman  * VIRTIO NETWORK DRIVER: TRANSMIT PATH
49*3a82bea4SAndy Fiddaman  *
50*3a82bea4SAndy Fiddaman  * Transmit buffer management, frame transmission and descriptor reclamation,
51*3a82bea4SAndy Fiddaman  * including the transmit ring entrypoints that we provide to MAC.
52*3a82bea4SAndy Fiddaman  */
53*3a82bea4SAndy Fiddaman 
54*3a82bea4SAndy Fiddaman #include <sys/types.h>
55*3a82bea4SAndy Fiddaman #include <sys/errno.h>
56*3a82bea4SAndy Fiddaman #include <sys/param.h>
57*3a82bea4SAndy Fiddaman #include <sys/stropts.h>
58*3a82bea4SAndy Fiddaman #include <sys/stream.h>
59*3a82bea4SAndy Fiddaman #include <sys/strsubr.h>
60*3a82bea4SAndy Fiddaman #include <sys/strsun.h>
61*3a82bea4SAndy Fiddaman #include <sys/kmem.h>
62*3a82bea4SAndy Fiddaman #include <sys/ksynch.h>
63*3a82bea4SAndy Fiddaman #include <sys/debug.h>
64*3a82bea4SAndy Fiddaman #include <sys/ethernet.h>
65*3a82bea4SAndy Fiddaman #include <sys/vlan.h>
66*3a82bea4SAndy Fiddaman #include <sys/sysmacros.h>
67*3a82bea4SAndy Fiddaman #include <sys/byteorder.h>
68*3a82bea4SAndy Fiddaman #include <sys/list.h>
69*3a82bea4SAndy Fiddaman #include <sys/pattr.h>
70*3a82bea4SAndy Fiddaman #include <sys/ddi.h>
71*3a82bea4SAndy Fiddaman #include <sys/sunddi.h>
72*3a82bea4SAndy Fiddaman 
73*3a82bea4SAndy Fiddaman #include <inet/tcp.h>
74*3a82bea4SAndy Fiddaman 
75*3a82bea4SAndy Fiddaman #include <sys/mac.h>
76*3a82bea4SAndy Fiddaman #include <sys/mac_provider.h>
77*3a82bea4SAndy Fiddaman 
78*3a82bea4SAndy Fiddaman #include "virtio.h"
79*3a82bea4SAndy Fiddaman #include "vioif.h"
80*3a82bea4SAndy Fiddaman 
81*3a82bea4SAndy Fiddaman /*
82*3a82bea4SAndy Fiddaman  * Interval for the periodic TX reclaim.
83*3a82bea4SAndy Fiddaman  */
84*3a82bea4SAndy Fiddaman uint_t vioif_reclaim_ms = 200;
85*3a82bea4SAndy Fiddaman 
86*3a82bea4SAndy Fiddaman /*
87*3a82bea4SAndy Fiddaman  * DMA attributes for mapping larger transmit buffers from the networking
88*3a82bea4SAndy Fiddaman  * stack.  The requirements are quite loose, but note that the SGL entry length
89*3a82bea4SAndy Fiddaman  * field is 32-bit.
90*3a82bea4SAndy Fiddaman  */
91*3a82bea4SAndy Fiddaman static ddi_dma_attr_t vioif_dma_attr_external = {
92*3a82bea4SAndy Fiddaman 	.dma_attr_version =		DMA_ATTR_V0,
93*3a82bea4SAndy Fiddaman 	.dma_attr_addr_lo =		0x0000000000000000,
94*3a82bea4SAndy Fiddaman 	.dma_attr_addr_hi =		0xFFFFFFFFFFFFFFFF,
95*3a82bea4SAndy Fiddaman 	.dma_attr_count_max =		0x00000000FFFFFFFF,
96*3a82bea4SAndy Fiddaman 	.dma_attr_align =		1,
97*3a82bea4SAndy Fiddaman 	.dma_attr_burstsizes =		1,
98*3a82bea4SAndy Fiddaman 	.dma_attr_minxfer =		1,
99*3a82bea4SAndy Fiddaman 	.dma_attr_maxxfer =		0x00000000FFFFFFFF,
100*3a82bea4SAndy Fiddaman 	.dma_attr_seg =			0x00000000FFFFFFFF,
101*3a82bea4SAndy Fiddaman 	.dma_attr_sgllen =		VIOIF_MAX_SEGS - 1,
102*3a82bea4SAndy Fiddaman 	.dma_attr_granular =		1,
103*3a82bea4SAndy Fiddaman 	.dma_attr_flags =		0
104*3a82bea4SAndy Fiddaman };
105*3a82bea4SAndy Fiddaman 
106*3a82bea4SAndy Fiddaman /*
107*3a82bea4SAndy Fiddaman  * Result of an attempt to map a message for transmission by reference.
108*3a82bea4SAndy Fiddaman  */
109*3a82bea4SAndy Fiddaman typedef enum vioif_txext_result {
110*3a82bea4SAndy Fiddaman 	VIOIF_TXEXT_OK,		/* mapped and appended to the chain */
111*3a82bea4SAndy Fiddaman 	VIOIF_TXEXT_FAIL,	/* failed; the message has been freed */
112*3a82bea4SAndy Fiddaman 	VIOIF_TXEXT_TOOBIG	/* too many segments; message retained */
113*3a82bea4SAndy Fiddaman } vioif_txext_result_t;
114*3a82bea4SAndy Fiddaman 
115*3a82bea4SAndy Fiddaman static void vioif_reclaim_restart(vioif_txq_t *);
116*3a82bea4SAndy Fiddaman 
117*3a82bea4SAndy Fiddaman static vioif_txbuf_t *
vioif_txbuf_alloc(vioif_txq_t * txq)118*3a82bea4SAndy Fiddaman vioif_txbuf_alloc(vioif_txq_t *txq)
119*3a82bea4SAndy Fiddaman {
120*3a82bea4SAndy Fiddaman 	vioif_txbuf_t *tb;
121*3a82bea4SAndy Fiddaman 
122*3a82bea4SAndy Fiddaman 	VERIFY(MUTEX_HELD(&txq->vtq_mutex));
123*3a82bea4SAndy Fiddaman 
124*3a82bea4SAndy Fiddaman 	if ((tb = list_remove_head(&txq->vtq_bufs)) != NULL) {
125*3a82bea4SAndy Fiddaman 		txq->vtq_nbufs_alloc++;
126*3a82bea4SAndy Fiddaman 	}
127*3a82bea4SAndy Fiddaman 
128*3a82bea4SAndy Fiddaman 	return (tb);
129*3a82bea4SAndy Fiddaman }
130*3a82bea4SAndy Fiddaman 
131*3a82bea4SAndy Fiddaman static void
vioif_txbuf_free(vioif_txq_t * txq,vioif_txbuf_t * tb)132*3a82bea4SAndy Fiddaman vioif_txbuf_free(vioif_txq_t *txq, vioif_txbuf_t *tb)
133*3a82bea4SAndy Fiddaman {
134*3a82bea4SAndy Fiddaman 	VERIFY(MUTEX_HELD(&txq->vtq_mutex));
135*3a82bea4SAndy Fiddaman 
136*3a82bea4SAndy Fiddaman 	VERIFY3U(txq->vtq_nbufs_alloc, >, 0);
137*3a82bea4SAndy Fiddaman 	txq->vtq_nbufs_alloc--;
138*3a82bea4SAndy Fiddaman 
139*3a82bea4SAndy Fiddaman 	virtio_chain_clear(tb->tb_chain);
140*3a82bea4SAndy Fiddaman 	list_insert_head(&txq->vtq_bufs, tb);
141*3a82bea4SAndy Fiddaman }
142*3a82bea4SAndy Fiddaman 
143*3a82bea4SAndy Fiddaman int
vioif_alloc_txq_bufs(vioif_txq_t * txq)144*3a82bea4SAndy Fiddaman vioif_alloc_txq_bufs(vioif_txq_t *txq)
145*3a82bea4SAndy Fiddaman {
146*3a82bea4SAndy Fiddaman 	vioif_t *vif = txq->vtq_vif;
147*3a82bea4SAndy Fiddaman 
148*3a82bea4SAndy Fiddaman 	/*
149*3a82bea4SAndy Fiddaman 	 * Allocate one contiguous chunk of memory for the transmit buffer
150*3a82bea4SAndy Fiddaman 	 * tracking objects. If the ring is unusually small, we'll reduce
151*3a82bea4SAndy Fiddaman 	 * our target buffer count accordingly.
152*3a82bea4SAndy Fiddaman 	 */
153*3a82bea4SAndy Fiddaman 	txq->vtq_bufs_capacity = MIN(VIRTIO_NET_TX_BUFS,
154*3a82bea4SAndy Fiddaman 	    virtio_queue_size(txq->vtq_vq));
155*3a82bea4SAndy Fiddaman 	txq->vtq_bufs_mem = kmem_zalloc(
156*3a82bea4SAndy Fiddaman 	    sizeof (vioif_txbuf_t) * txq->vtq_bufs_capacity, KM_SLEEP);
157*3a82bea4SAndy Fiddaman 	list_create(&txq->vtq_bufs, sizeof (vioif_txbuf_t),
158*3a82bea4SAndy Fiddaman 	    offsetof(vioif_txbuf_t, tb_link));
159*3a82bea4SAndy Fiddaman 
160*3a82bea4SAndy Fiddaman 	/*
161*3a82bea4SAndy Fiddaman 	 * Put everything in the free list straight away in order to simplify
162*3a82bea4SAndy Fiddaman 	 * the use of vioif_free_txq_bufs() for cleanup on allocation failure.
163*3a82bea4SAndy Fiddaman 	 */
164*3a82bea4SAndy Fiddaman 	for (uint_t i = 0; i < txq->vtq_bufs_capacity; i++) {
165*3a82bea4SAndy Fiddaman 		list_insert_tail(&txq->vtq_bufs, &txq->vtq_bufs_mem[i]);
166*3a82bea4SAndy Fiddaman 	}
167*3a82bea4SAndy Fiddaman 
168*3a82bea4SAndy Fiddaman 	/*
169*3a82bea4SAndy Fiddaman 	 * The transmit inline buffer is small (less than a page), so it's
170*3a82bea4SAndy Fiddaman 	 * reasonable to request a single cookie.
171*3a82bea4SAndy Fiddaman 	 */
172*3a82bea4SAndy Fiddaman 	ddi_dma_attr_t attr = vioif_dma_attr_bufs;
173*3a82bea4SAndy Fiddaman 	attr.dma_attr_sgllen = 1;
174*3a82bea4SAndy Fiddaman 
175*3a82bea4SAndy Fiddaman 	for (vioif_txbuf_t *tb = list_head(&txq->vtq_bufs); tb != NULL;
176*3a82bea4SAndy Fiddaman 	    tb = list_next(&txq->vtq_bufs, tb)) {
177*3a82bea4SAndy Fiddaman 		if ((tb->tb_dma = virtio_dma_alloc(vif->vif_virtio,
178*3a82bea4SAndy Fiddaman 		    VIOIF_TX_INLINE_SIZE, &attr,
179*3a82bea4SAndy Fiddaman 		    DDI_DMA_STREAMING | DDI_DMA_WRITE, KM_SLEEP)) == NULL) {
180*3a82bea4SAndy Fiddaman 			return (ENOMEM);
181*3a82bea4SAndy Fiddaman 		}
182*3a82bea4SAndy Fiddaman 		VERIFY3U(virtio_dma_ncookies(tb->tb_dma), ==, 1);
183*3a82bea4SAndy Fiddaman 
184*3a82bea4SAndy Fiddaman 		if ((tb->tb_chain = virtio_chain_alloc(txq->vtq_vq,
185*3a82bea4SAndy Fiddaman 		    KM_SLEEP)) == NULL) {
186*3a82bea4SAndy Fiddaman 			return (ENOMEM);
187*3a82bea4SAndy Fiddaman 		}
188*3a82bea4SAndy Fiddaman 		virtio_chain_data_set(tb->tb_chain, tb);
189*3a82bea4SAndy Fiddaman 
190*3a82bea4SAndy Fiddaman 		tb->tb_dmaext_capacity = VIOIF_MAX_SEGS - 1;
191*3a82bea4SAndy Fiddaman 		tb->tb_dmaext = kmem_zalloc(
192*3a82bea4SAndy Fiddaman 		    sizeof (virtio_dma_t *) * tb->tb_dmaext_capacity,
193*3a82bea4SAndy Fiddaman 		    KM_SLEEP);
194*3a82bea4SAndy Fiddaman 	}
195*3a82bea4SAndy Fiddaman 
196*3a82bea4SAndy Fiddaman 	return (0);
197*3a82bea4SAndy Fiddaman }
198*3a82bea4SAndy Fiddaman 
199*3a82bea4SAndy Fiddaman void
vioif_free_txq_bufs(vioif_txq_t * txq)200*3a82bea4SAndy Fiddaman vioif_free_txq_bufs(vioif_txq_t *txq)
201*3a82bea4SAndy Fiddaman {
202*3a82bea4SAndy Fiddaman 	if (txq->vtq_bufs_mem == NULL)
203*3a82bea4SAndy Fiddaman 		return;
204*3a82bea4SAndy Fiddaman 
205*3a82bea4SAndy Fiddaman 	VERIFY3U(txq->vtq_nbufs_alloc, ==, 0);
206*3a82bea4SAndy Fiddaman 	for (uint_t i = 0; i < txq->vtq_bufs_capacity; i++) {
207*3a82bea4SAndy Fiddaman 		vioif_txbuf_t *tb = &txq->vtq_bufs_mem[i];
208*3a82bea4SAndy Fiddaman 
209*3a82bea4SAndy Fiddaman 		/*
210*3a82bea4SAndy Fiddaman 		 * Ensure that this txbuf is now in the free list:
211*3a82bea4SAndy Fiddaman 		 */
212*3a82bea4SAndy Fiddaman 		VERIFY(list_link_active(&tb->tb_link));
213*3a82bea4SAndy Fiddaman 		list_remove(&txq->vtq_bufs, tb);
214*3a82bea4SAndy Fiddaman 
215*3a82bea4SAndy Fiddaman 		/*
216*3a82bea4SAndy Fiddaman 		 * We should not have an mblk chain at this point.
217*3a82bea4SAndy Fiddaman 		 */
218*3a82bea4SAndy Fiddaman 		VERIFY3P(tb->tb_mp, ==, NULL);
219*3a82bea4SAndy Fiddaman 
220*3a82bea4SAndy Fiddaman 		if (tb->tb_dma != NULL) {
221*3a82bea4SAndy Fiddaman 			virtio_dma_free(tb->tb_dma);
222*3a82bea4SAndy Fiddaman 			tb->tb_dma = NULL;
223*3a82bea4SAndy Fiddaman 		}
224*3a82bea4SAndy Fiddaman 
225*3a82bea4SAndy Fiddaman 		if (tb->tb_chain != NULL) {
226*3a82bea4SAndy Fiddaman 			virtio_chain_free(tb->tb_chain);
227*3a82bea4SAndy Fiddaman 			tb->tb_chain = NULL;
228*3a82bea4SAndy Fiddaman 		}
229*3a82bea4SAndy Fiddaman 
230*3a82bea4SAndy Fiddaman 		if (tb->tb_dmaext != NULL) {
231*3a82bea4SAndy Fiddaman 			for (uint_t j = 0; j < tb->tb_dmaext_capacity; j++) {
232*3a82bea4SAndy Fiddaman 				if (tb->tb_dmaext[j] != NULL) {
233*3a82bea4SAndy Fiddaman 					virtio_dma_free(tb->tb_dmaext[j]);
234*3a82bea4SAndy Fiddaman 					tb->tb_dmaext[j] = NULL;
235*3a82bea4SAndy Fiddaman 				}
236*3a82bea4SAndy Fiddaman 			}
237*3a82bea4SAndy Fiddaman 
238*3a82bea4SAndy Fiddaman 			kmem_free(tb->tb_dmaext,
239*3a82bea4SAndy Fiddaman 			    sizeof (virtio_dma_t *) * tb->tb_dmaext_capacity);
240*3a82bea4SAndy Fiddaman 			tb->tb_dmaext = NULL;
241*3a82bea4SAndy Fiddaman 			tb->tb_dmaext_capacity = 0;
242*3a82bea4SAndy Fiddaman 		}
243*3a82bea4SAndy Fiddaman 	}
244*3a82bea4SAndy Fiddaman 	VERIFY(list_is_empty(&txq->vtq_bufs));
245*3a82bea4SAndy Fiddaman 	list_destroy(&txq->vtq_bufs);
246*3a82bea4SAndy Fiddaman 
247*3a82bea4SAndy Fiddaman 	kmem_free(txq->vtq_bufs_mem,
248*3a82bea4SAndy Fiddaman 	    sizeof (vioif_txbuf_t) * txq->vtq_bufs_capacity);
249*3a82bea4SAndy Fiddaman 	txq->vtq_bufs_mem = NULL;
250*3a82bea4SAndy Fiddaman 	txq->vtq_bufs_capacity = 0;
251*3a82bea4SAndy Fiddaman }
252*3a82bea4SAndy Fiddaman 
253*3a82bea4SAndy Fiddaman static uint_t
vioif_reclaim_used_tx(vioif_txq_t * txq)254*3a82bea4SAndy Fiddaman vioif_reclaim_used_tx(vioif_txq_t *txq)
255*3a82bea4SAndy Fiddaman {
256*3a82bea4SAndy Fiddaman 	virtio_chain_t *vic;
257*3a82bea4SAndy Fiddaman 	uint_t num_reclaimed = 0;
258*3a82bea4SAndy Fiddaman 
259*3a82bea4SAndy Fiddaman 	VERIFY(MUTEX_NOT_HELD(&txq->vtq_mutex));
260*3a82bea4SAndy Fiddaman 
261*3a82bea4SAndy Fiddaman 	while ((vic = virtio_queue_poll(txq->vtq_vq)) != NULL) {
262*3a82bea4SAndy Fiddaman 		vioif_txbuf_t *tb = virtio_chain_data(vic);
263*3a82bea4SAndy Fiddaman 
264*3a82bea4SAndy Fiddaman 		if (tb->tb_mp != NULL) {
265*3a82bea4SAndy Fiddaman 			/*
266*3a82bea4SAndy Fiddaman 			 * Unbind the external mapping.
267*3a82bea4SAndy Fiddaman 			 */
268*3a82bea4SAndy Fiddaman 			for (uint_t i = 0; i < tb->tb_dmaext_capacity; i++) {
269*3a82bea4SAndy Fiddaman 				if (tb->tb_dmaext[i] == NULL) {
270*3a82bea4SAndy Fiddaman 					continue;
271*3a82bea4SAndy Fiddaman 				}
272*3a82bea4SAndy Fiddaman 
273*3a82bea4SAndy Fiddaman 				virtio_dma_unbind(tb->tb_dmaext[i]);
274*3a82bea4SAndy Fiddaman 			}
275*3a82bea4SAndy Fiddaman 
276*3a82bea4SAndy Fiddaman 			freemsg(tb->tb_mp);
277*3a82bea4SAndy Fiddaman 			tb->tb_mp = NULL;
278*3a82bea4SAndy Fiddaman 		}
279*3a82bea4SAndy Fiddaman 
280*3a82bea4SAndy Fiddaman 		/*
281*3a82bea4SAndy Fiddaman 		 * Return this transmit buffer to the free list for reuse.
282*3a82bea4SAndy Fiddaman 		 */
283*3a82bea4SAndy Fiddaman 		mutex_enter(&txq->vtq_mutex);
284*3a82bea4SAndy Fiddaman 		vioif_txbuf_free(txq, tb);
285*3a82bea4SAndy Fiddaman 		mutex_exit(&txq->vtq_mutex);
286*3a82bea4SAndy Fiddaman 
287*3a82bea4SAndy Fiddaman 		num_reclaimed++;
288*3a82bea4SAndy Fiddaman 	}
289*3a82bea4SAndy Fiddaman 
290*3a82bea4SAndy Fiddaman 	/* Return ring to transmitting state if descriptors were reclaimed. */
291*3a82bea4SAndy Fiddaman 	if (num_reclaimed > 0) {
292*3a82bea4SAndy Fiddaman 		boolean_t do_update = B_FALSE;
293*3a82bea4SAndy Fiddaman 
294*3a82bea4SAndy Fiddaman 		mutex_enter(&txq->vtq_mutex);
295*3a82bea4SAndy Fiddaman 		txq->vtq_stat_tx_reclaim += num_reclaimed;
296*3a82bea4SAndy Fiddaman 		if (txq->vtq_corked) {
297*3a82bea4SAndy Fiddaman 			/*
298*3a82bea4SAndy Fiddaman 			 * TX was corked on a lack of available descriptors.
299*3a82bea4SAndy Fiddaman 			 * That dire state has passed so the TX interrupt can
300*3a82bea4SAndy Fiddaman 			 * be disabled and MAC can be notified that
301*3a82bea4SAndy Fiddaman 			 * transmission is possible again.
302*3a82bea4SAndy Fiddaman 			 */
303*3a82bea4SAndy Fiddaman 			txq->vtq_corked = false;
304*3a82bea4SAndy Fiddaman 			virtio_queue_no_interrupt(txq->vtq_vq, B_TRUE);
305*3a82bea4SAndy Fiddaman 			do_update = B_TRUE;
306*3a82bea4SAndy Fiddaman 		}
307*3a82bea4SAndy Fiddaman 
308*3a82bea4SAndy Fiddaman 		mutex_exit(&txq->vtq_mutex);
309*3a82bea4SAndy Fiddaman 		if (do_update) {
310*3a82bea4SAndy Fiddaman 			mac_tx_ring_update(txq->vtq_vif->vif_mac_handle,
311*3a82bea4SAndy Fiddaman 			    txq->vtq_ringh);
312*3a82bea4SAndy Fiddaman 		}
313*3a82bea4SAndy Fiddaman 	}
314*3a82bea4SAndy Fiddaman 
315*3a82bea4SAndy Fiddaman 	return (num_reclaimed);
316*3a82bea4SAndy Fiddaman }
317*3a82bea4SAndy Fiddaman 
318*3a82bea4SAndy Fiddaman static void
vioif_reclaim_periodic(void * arg)319*3a82bea4SAndy Fiddaman vioif_reclaim_periodic(void *arg)
320*3a82bea4SAndy Fiddaman {
321*3a82bea4SAndy Fiddaman 	vioif_txq_t *txq = arg;
322*3a82bea4SAndy Fiddaman 	uint_t num_reclaimed;
323*3a82bea4SAndy Fiddaman 
324*3a82bea4SAndy Fiddaman 	num_reclaimed = vioif_reclaim_used_tx(txq);
325*3a82bea4SAndy Fiddaman 
326*3a82bea4SAndy Fiddaman 	mutex_enter(&txq->vtq_mutex);
327*3a82bea4SAndy Fiddaman 	txq->vtq_reclaim_tid = 0;
328*3a82bea4SAndy Fiddaman 	/*
329*3a82bea4SAndy Fiddaman 	 * If used descriptors were reclaimed or TX descriptors appear to be
330*3a82bea4SAndy Fiddaman 	 * outstanding, the ring is considered active and periodic reclamation
331*3a82bea4SAndy Fiddaman 	 * is necessary for now.
332*3a82bea4SAndy Fiddaman 	 */
333*3a82bea4SAndy Fiddaman 	if (num_reclaimed != 0 || virtio_queue_nactive(txq->vtq_vq) != 0) {
334*3a82bea4SAndy Fiddaman 		/* Do not reschedule if the ring is being drained. */
335*3a82bea4SAndy Fiddaman 		if (!txq->vtq_drain) {
336*3a82bea4SAndy Fiddaman 			vioif_reclaim_restart(txq);
337*3a82bea4SAndy Fiddaman 		}
338*3a82bea4SAndy Fiddaman 	}
339*3a82bea4SAndy Fiddaman 	mutex_exit(&txq->vtq_mutex);
340*3a82bea4SAndy Fiddaman }
341*3a82bea4SAndy Fiddaman 
342*3a82bea4SAndy Fiddaman static void
vioif_reclaim_restart(vioif_txq_t * txq)343*3a82bea4SAndy Fiddaman vioif_reclaim_restart(vioif_txq_t *txq)
344*3a82bea4SAndy Fiddaman {
345*3a82bea4SAndy Fiddaman 	VERIFY(MUTEX_HELD(&txq->vtq_mutex));
346*3a82bea4SAndy Fiddaman 	VERIFY(!txq->vtq_drain);
347*3a82bea4SAndy Fiddaman 
348*3a82bea4SAndy Fiddaman 	if (txq->vtq_reclaim_tid == 0) {
349*3a82bea4SAndy Fiddaman 		txq->vtq_reclaim_tid = timeout(vioif_reclaim_periodic, txq,
350*3a82bea4SAndy Fiddaman 		    MSEC_TO_TICK_ROUNDUP(vioif_reclaim_ms));
351*3a82bea4SAndy Fiddaman 	}
352*3a82bea4SAndy Fiddaman }
353*3a82bea4SAndy Fiddaman 
354*3a82bea4SAndy Fiddaman void
vioif_tx_drain(vioif_txq_t * txq)355*3a82bea4SAndy Fiddaman vioif_tx_drain(vioif_txq_t *txq)
356*3a82bea4SAndy Fiddaman {
357*3a82bea4SAndy Fiddaman 	VERIFY(MUTEX_HELD(&txq->vtq_mutex));
358*3a82bea4SAndy Fiddaman 
359*3a82bea4SAndy Fiddaman 	txq->vtq_drain = true;
360*3a82bea4SAndy Fiddaman 	/* Put a stop to the periodic reclaim if it is running */
361*3a82bea4SAndy Fiddaman 	if (txq->vtq_reclaim_tid != 0) {
362*3a82bea4SAndy Fiddaman 		timeout_id_t tid = txq->vtq_reclaim_tid;
363*3a82bea4SAndy Fiddaman 
364*3a82bea4SAndy Fiddaman 		/*
365*3a82bea4SAndy Fiddaman 		 * With vtq_drain set, there is no risk that a racing
366*3a82bea4SAndy Fiddaman 		 * vioif_reclaim_periodic() call will reschedule itself.
367*3a82bea4SAndy Fiddaman 		 *
368*3a82bea4SAndy Fiddaman 		 * Being part of the mc_stop hook also guarantees that
369*3a82bea4SAndy Fiddaman 		 * vioif_ring_tx() will not be called to restart it.
370*3a82bea4SAndy Fiddaman 		 */
371*3a82bea4SAndy Fiddaman 		txq->vtq_reclaim_tid = 0;
372*3a82bea4SAndy Fiddaman 		mutex_exit(&txq->vtq_mutex);
373*3a82bea4SAndy Fiddaman 		(void) untimeout(tid);
374*3a82bea4SAndy Fiddaman 		mutex_enter(&txq->vtq_mutex);
375*3a82bea4SAndy Fiddaman 	}
376*3a82bea4SAndy Fiddaman 	virtio_queue_no_interrupt(txq->vtq_vq, B_TRUE);
377*3a82bea4SAndy Fiddaman 
378*3a82bea4SAndy Fiddaman 	/*
379*3a82bea4SAndy Fiddaman 	 * Wait for all of the TX descriptors to be processed by the host so
380*3a82bea4SAndy Fiddaman 	 * they can be reclaimed.
381*3a82bea4SAndy Fiddaman 	 */
382*3a82bea4SAndy Fiddaman 	while (txq->vtq_nbufs_alloc > 0) {
383*3a82bea4SAndy Fiddaman 		mutex_exit(&txq->vtq_mutex);
384*3a82bea4SAndy Fiddaman 		(void) vioif_reclaim_used_tx(txq);
385*3a82bea4SAndy Fiddaman 		delay(5);
386*3a82bea4SAndy Fiddaman 		mutex_enter(&txq->vtq_mutex);
387*3a82bea4SAndy Fiddaman 	}
388*3a82bea4SAndy Fiddaman 	VERIFY(!txq->vtq_corked);
389*3a82bea4SAndy Fiddaman 	VERIFY3U(txq->vtq_reclaim_tid, ==, 0);
390*3a82bea4SAndy Fiddaman 	VERIFY3U(virtio_queue_nactive(txq->vtq_vq), ==, 0);
391*3a82bea4SAndy Fiddaman }
392*3a82bea4SAndy Fiddaman 
393*3a82bea4SAndy Fiddaman /*
394*3a82bea4SAndy Fiddaman  * Add the descriptor entry for the virtio net header, which is held at the
395*3a82bea4SAndy Fiddaman  * start of the transmit buffer's inline DMA memory. For legacy devices, and
396*3a82bea4SAndy Fiddaman  * those that have not negotiated VIRTIO_F_ANY_LAYOUT, the header must appear
397*3a82bea4SAndy Fiddaman  * in a separate descriptor entry to the rest of the buffer. We do that for
398*3a82bea4SAndy Fiddaman  * modern devices too.
399*3a82bea4SAndy Fiddaman  */
400*3a82bea4SAndy Fiddaman static int
vioif_tx_header_append(vioif_t * vif,vioif_txbuf_t * tb)401*3a82bea4SAndy Fiddaman vioif_tx_header_append(vioif_t *vif, vioif_txbuf_t *tb)
402*3a82bea4SAndy Fiddaman {
403*3a82bea4SAndy Fiddaman 	return (virtio_chain_append(tb->tb_chain,
404*3a82bea4SAndy Fiddaman 	    virtio_dma_cookie_pa(tb->tb_dma, 0), vif->vif_rxbuf_hdrlen,
405*3a82bea4SAndy Fiddaman 	    VIRTIO_DIR_DEVICE_READS));
406*3a82bea4SAndy Fiddaman }
407*3a82bea4SAndy Fiddaman 
408*3a82bea4SAndy Fiddaman static int
vioif_tx_inline(vioif_txq_t * txq,vioif_txbuf_t * tb,mblk_t * mp,size_t msg_size)409*3a82bea4SAndy Fiddaman vioif_tx_inline(vioif_txq_t *txq, vioif_txbuf_t *tb, mblk_t *mp,
410*3a82bea4SAndy Fiddaman     size_t msg_size)
411*3a82bea4SAndy Fiddaman {
412*3a82bea4SAndy Fiddaman 	VERIFY(MUTEX_NOT_HELD(&txq->vtq_mutex));
413*3a82bea4SAndy Fiddaman 
414*3a82bea4SAndy Fiddaman 	VERIFY3U(msg_size, <=, virtio_dma_size(tb->tb_dma) - VIOIF_HEADER_SKIP);
415*3a82bea4SAndy Fiddaman 
416*3a82bea4SAndy Fiddaman 	/*
417*3a82bea4SAndy Fiddaman 	 * Copy the message into the inline buffer and then free the message.
418*3a82bea4SAndy Fiddaman 	 */
419*3a82bea4SAndy Fiddaman 	mcopymsg(mp, virtio_dma_va(tb->tb_dma, VIOIF_HEADER_SKIP));
420*3a82bea4SAndy Fiddaman 
421*3a82bea4SAndy Fiddaman 	if (virtio_chain_append(tb->tb_chain,
422*3a82bea4SAndy Fiddaman 	    virtio_dma_cookie_pa(tb->tb_dma, 0) + VIOIF_HEADER_SKIP,
423*3a82bea4SAndy Fiddaman 	    msg_size, VIRTIO_DIR_DEVICE_READS) != DDI_SUCCESS) {
424*3a82bea4SAndy Fiddaman 		return (DDI_FAILURE);
425*3a82bea4SAndy Fiddaman 	}
426*3a82bea4SAndy Fiddaman 
427*3a82bea4SAndy Fiddaman 	return (DDI_SUCCESS);
428*3a82bea4SAndy Fiddaman }
429*3a82bea4SAndy Fiddaman 
430*3a82bea4SAndy Fiddaman /*
431*3a82bea4SAndy Fiddaman  * Map a message for transmission by reference, binding each of its mblks for
432*3a82bea4SAndy Fiddaman  * DMA and appending the resulting cookies to the transmit buffer's
433*3a82bea4SAndy Fiddaman  * descriptor chain. Returns VIOIF_TXEXT_TOOBIG, with the message retained
434*3a82bea4SAndy Fiddaman  * and the chain cleared, if it has more disjoint segments than a chain can
435*3a82bea4SAndy Fiddaman  * carry. The caller may pull the message up into contiguous storage and try
436*3a82bea4SAndy Fiddaman  * again. On any other failure the message is freed.
437*3a82bea4SAndy Fiddaman  */
438*3a82bea4SAndy Fiddaman static vioif_txext_result_t
vioif_tx_external(vioif_txq_t * txq,vioif_txbuf_t * tb,mblk_t * mp,size_t msg_size)439*3a82bea4SAndy Fiddaman vioif_tx_external(vioif_txq_t *txq, vioif_txbuf_t *tb, mblk_t *mp,
440*3a82bea4SAndy Fiddaman     size_t msg_size)
441*3a82bea4SAndy Fiddaman {
442*3a82bea4SAndy Fiddaman 	vioif_t *vif = txq->vtq_vif;
443*3a82bea4SAndy Fiddaman 	vioif_txext_result_t res = VIOIF_TXEXT_FAIL;
444*3a82bea4SAndy Fiddaman 
445*3a82bea4SAndy Fiddaman 	VERIFY(MUTEX_NOT_HELD(&txq->vtq_mutex));
446*3a82bea4SAndy Fiddaman 
447*3a82bea4SAndy Fiddaman 	mblk_t *nmp = mp;
448*3a82bea4SAndy Fiddaman 	tb->tb_ndmaext = 0;
449*3a82bea4SAndy Fiddaman 
450*3a82bea4SAndy Fiddaman 	while (nmp != NULL) {
451*3a82bea4SAndy Fiddaman 		size_t len;
452*3a82bea4SAndy Fiddaman 
453*3a82bea4SAndy Fiddaman 		if ((len = MBLKL(nmp)) == 0) {
454*3a82bea4SAndy Fiddaman 			/*
455*3a82bea4SAndy Fiddaman 			 * Skip any zero-length entries in the chain.
456*3a82bea4SAndy Fiddaman 			 */
457*3a82bea4SAndy Fiddaman 			nmp = nmp->b_cont;
458*3a82bea4SAndy Fiddaman 			continue;
459*3a82bea4SAndy Fiddaman 		}
460*3a82bea4SAndy Fiddaman 
461*3a82bea4SAndy Fiddaman 		if (tb->tb_ndmaext >= tb->tb_dmaext_capacity) {
462*3a82bea4SAndy Fiddaman 			mutex_enter(&txq->vtq_mutex);
463*3a82bea4SAndy Fiddaman 			txq->vtq_txfail_indirect_limit++;
464*3a82bea4SAndy Fiddaman 			mutex_exit(&txq->vtq_mutex);
465*3a82bea4SAndy Fiddaman 			res = VIOIF_TXEXT_TOOBIG;
466*3a82bea4SAndy Fiddaman 			goto fail;
467*3a82bea4SAndy Fiddaman 		}
468*3a82bea4SAndy Fiddaman 
469*3a82bea4SAndy Fiddaman 		if (tb->tb_dmaext[tb->tb_ndmaext] == NULL) {
470*3a82bea4SAndy Fiddaman 			/*
471*3a82bea4SAndy Fiddaman 			 * Allocate a DMA handle for this slot.
472*3a82bea4SAndy Fiddaman 			 */
473*3a82bea4SAndy Fiddaman 			if ((tb->tb_dmaext[tb->tb_ndmaext] =
474*3a82bea4SAndy Fiddaman 			    virtio_dma_alloc_nomem(vif->vif_virtio,
475*3a82bea4SAndy Fiddaman 			    &vioif_dma_attr_external, KM_SLEEP)) == NULL) {
476*3a82bea4SAndy Fiddaman 				mutex_enter(&txq->vtq_mutex);
477*3a82bea4SAndy Fiddaman 				txq->vtq_notxbuf++;
478*3a82bea4SAndy Fiddaman 				mutex_exit(&txq->vtq_mutex);
479*3a82bea4SAndy Fiddaman 				goto fail;
480*3a82bea4SAndy Fiddaman 			}
481*3a82bea4SAndy Fiddaman 		}
482*3a82bea4SAndy Fiddaman 		virtio_dma_t *extdma = tb->tb_dmaext[tb->tb_ndmaext++];
483*3a82bea4SAndy Fiddaman 
484*3a82bea4SAndy Fiddaman 		if (virtio_dma_bind(extdma, nmp->b_rptr, len,
485*3a82bea4SAndy Fiddaman 		    DDI_DMA_WRITE | DDI_DMA_STREAMING, KM_SLEEP) !=
486*3a82bea4SAndy Fiddaman 		    DDI_SUCCESS) {
487*3a82bea4SAndy Fiddaman 			mutex_enter(&txq->vtq_mutex);
488*3a82bea4SAndy Fiddaman 			txq->vtq_txfail_dma_bind++;
489*3a82bea4SAndy Fiddaman 			mutex_exit(&txq->vtq_mutex);
490*3a82bea4SAndy Fiddaman 			goto fail;
491*3a82bea4SAndy Fiddaman 		}
492*3a82bea4SAndy Fiddaman 
493*3a82bea4SAndy Fiddaman 		for (uint_t n = 0; n < virtio_dma_ncookies(extdma); n++) {
494*3a82bea4SAndy Fiddaman 			uint64_t pa = virtio_dma_cookie_pa(extdma, n);
495*3a82bea4SAndy Fiddaman 			size_t sz = virtio_dma_cookie_size(extdma, n);
496*3a82bea4SAndy Fiddaman 
497*3a82bea4SAndy Fiddaman 			if (virtio_chain_append(tb->tb_chain, pa, sz,
498*3a82bea4SAndy Fiddaman 			    VIRTIO_DIR_DEVICE_READS) != DDI_SUCCESS) {
499*3a82bea4SAndy Fiddaman 				mutex_enter(&txq->vtq_mutex);
500*3a82bea4SAndy Fiddaman 				txq->vtq_txfail_indirect_limit++;
501*3a82bea4SAndy Fiddaman 				mutex_exit(&txq->vtq_mutex);
502*3a82bea4SAndy Fiddaman 				res = VIOIF_TXEXT_TOOBIG;
503*3a82bea4SAndy Fiddaman 				goto fail;
504*3a82bea4SAndy Fiddaman 			}
505*3a82bea4SAndy Fiddaman 		}
506*3a82bea4SAndy Fiddaman 
507*3a82bea4SAndy Fiddaman 		nmp = nmp->b_cont;
508*3a82bea4SAndy Fiddaman 	}
509*3a82bea4SAndy Fiddaman 
510*3a82bea4SAndy Fiddaman 	/*
511*3a82bea4SAndy Fiddaman 	 * We need to keep the message around until we reclaim the buffer from
512*3a82bea4SAndy Fiddaman 	 * the device before freeing it.
513*3a82bea4SAndy Fiddaman 	 */
514*3a82bea4SAndy Fiddaman 	tb->tb_mp = mp;
515*3a82bea4SAndy Fiddaman 
516*3a82bea4SAndy Fiddaman 	return (VIOIF_TXEXT_OK);
517*3a82bea4SAndy Fiddaman 
518*3a82bea4SAndy Fiddaman fail:
519*3a82bea4SAndy Fiddaman 	for (uint_t n = 0; n < tb->tb_ndmaext; n++) {
520*3a82bea4SAndy Fiddaman 		if (tb->tb_dmaext[n] != NULL) {
521*3a82bea4SAndy Fiddaman 			virtio_dma_unbind(tb->tb_dmaext[n]);
522*3a82bea4SAndy Fiddaman 		}
523*3a82bea4SAndy Fiddaman 	}
524*3a82bea4SAndy Fiddaman 	tb->tb_ndmaext = 0;
525*3a82bea4SAndy Fiddaman 
526*3a82bea4SAndy Fiddaman 	if (res == VIOIF_TXEXT_TOOBIG) {
527*3a82bea4SAndy Fiddaman 		/*
528*3a82bea4SAndy Fiddaman 		 * Clear the chain, which will have entries for the virtio net
529*3a82bea4SAndy Fiddaman 		 * header and any segments appended before the capacity was
530*3a82bea4SAndy Fiddaman 		 * reached, so that the caller can rebuild it and retry with a
531*3a82bea4SAndy Fiddaman 		 * pulled up copy of the message. The other failure paths can
532*3a82bea4SAndy Fiddaman 		 * leave the chain as it is, since the buffer is going back to
533*3a82bea4SAndy Fiddaman 		 * the free list, which clears the chain as part of returning
534*3a82bea4SAndy Fiddaman 		 * it.
535*3a82bea4SAndy Fiddaman 		 */
536*3a82bea4SAndy Fiddaman 		virtio_chain_clear(tb->tb_chain);
537*3a82bea4SAndy Fiddaman 	} else {
538*3a82bea4SAndy Fiddaman 		freemsg(mp);
539*3a82bea4SAndy Fiddaman 	}
540*3a82bea4SAndy Fiddaman 
541*3a82bea4SAndy Fiddaman 	return (res);
542*3a82bea4SAndy Fiddaman }
543*3a82bea4SAndy Fiddaman 
544*3a82bea4SAndy Fiddaman /*
545*3a82bea4SAndy Fiddaman  * Attempt to transmit a single message on a transmit ring. Returns B_TRUE
546*3a82bea4SAndy Fiddaman  * if the message was consumed, whether it was transmitted or dropped as
547*3a82bea4SAndy Fiddaman  * untransmittable. Returns B_FALSE, with the message retained, only when no
548*3a82bea4SAndy Fiddaman  * transmit buffer is available. The caller may retry the same message once
549*3a82bea4SAndy Fiddaman  * descriptors have been reclaimed.
550*3a82bea4SAndy Fiddaman  */
551*3a82bea4SAndy Fiddaman static boolean_t
vioif_send(vioif_txq_t * txq,mblk_t * mp)552*3a82bea4SAndy Fiddaman vioif_send(vioif_txq_t *txq, mblk_t *mp)
553*3a82bea4SAndy Fiddaman {
554*3a82bea4SAndy Fiddaman 	vioif_t *vif = txq->vtq_vif;
555*3a82bea4SAndy Fiddaman 
556*3a82bea4SAndy Fiddaman 	VERIFY(MUTEX_NOT_HELD(&txq->vtq_mutex));
557*3a82bea4SAndy Fiddaman 
558*3a82bea4SAndy Fiddaman 	vioif_txbuf_t *tb = NULL;
559*3a82bea4SAndy Fiddaman 	struct virtio_net_hdr *vnh = NULL;
560*3a82bea4SAndy Fiddaman 	size_t msg_size = 0;
561*3a82bea4SAndy Fiddaman 	uint32_t csum_start;
562*3a82bea4SAndy Fiddaman 	uint32_t csum_stuff;
563*3a82bea4SAndy Fiddaman 	uint32_t csum_flags;
564*3a82bea4SAndy Fiddaman 	uint32_t lso_flags;
565*3a82bea4SAndy Fiddaman 	uint32_t lso_mss;
566*3a82bea4SAndy Fiddaman 	mblk_t *nmp;
567*3a82bea4SAndy Fiddaman 	int ret;
568*3a82bea4SAndy Fiddaman 	boolean_t lso_required = B_FALSE;
569*3a82bea4SAndy Fiddaman 	struct ether_header *ether = (void *)mp->b_rptr;
570*3a82bea4SAndy Fiddaman 
571*3a82bea4SAndy Fiddaman 	for (nmp = mp; nmp; nmp = nmp->b_cont)
572*3a82bea4SAndy Fiddaman 		msg_size += MBLKL(nmp);
573*3a82bea4SAndy Fiddaman 
574*3a82bea4SAndy Fiddaman 	if (vif->vif_tx_tso4 || vif->vif_tx_tso6) {
575*3a82bea4SAndy Fiddaman 		mac_lso_get(mp, &lso_mss, &lso_flags);
576*3a82bea4SAndy Fiddaman 		lso_required = (lso_flags & HW_LSO) != 0;
577*3a82bea4SAndy Fiddaman 	}
578*3a82bea4SAndy Fiddaman 
579*3a82bea4SAndy Fiddaman 	mutex_enter(&txq->vtq_mutex);
580*3a82bea4SAndy Fiddaman 	if ((tb = vioif_txbuf_alloc(txq)) == NULL) {
581*3a82bea4SAndy Fiddaman 		txq->vtq_notxbuf++;
582*3a82bea4SAndy Fiddaman 		mutex_exit(&txq->vtq_mutex);
583*3a82bea4SAndy Fiddaman 		return (B_FALSE);
584*3a82bea4SAndy Fiddaman 	}
585*3a82bea4SAndy Fiddaman 	mutex_exit(&txq->vtq_mutex);
586*3a82bea4SAndy Fiddaman 
587*3a82bea4SAndy Fiddaman 	/*
588*3a82bea4SAndy Fiddaman 	 * Use the inline buffer for the virtio net header.  Zero the portion
589*3a82bea4SAndy Fiddaman 	 * of our DMA allocation prior to the packet data.
590*3a82bea4SAndy Fiddaman 	 */
591*3a82bea4SAndy Fiddaman 	vnh = virtio_dma_va(tb->tb_dma, 0);
592*3a82bea4SAndy Fiddaman 	bzero(vnh, VIOIF_HEADER_SKIP);
593*3a82bea4SAndy Fiddaman 
594*3a82bea4SAndy Fiddaman 	if (vioif_tx_header_append(vif, tb) != DDI_SUCCESS)
595*3a82bea4SAndy Fiddaman 		goto drop;
596*3a82bea4SAndy Fiddaman 
597*3a82bea4SAndy Fiddaman 	mac_hcksum_get(mp, &csum_start, &csum_stuff, NULL, NULL, &csum_flags);
598*3a82bea4SAndy Fiddaman 
599*3a82bea4SAndy Fiddaman 	/*
600*3a82bea4SAndy Fiddaman 	 * They want us to do the TCP/UDP csum calculation.
601*3a82bea4SAndy Fiddaman 	 */
602*3a82bea4SAndy Fiddaman 	if (csum_flags & HCK_PARTIALCKSUM) {
603*3a82bea4SAndy Fiddaman 		int eth_hsize;
604*3a82bea4SAndy Fiddaman 
605*3a82bea4SAndy Fiddaman 		/*
606*3a82bea4SAndy Fiddaman 		 * Did we ask for it?
607*3a82bea4SAndy Fiddaman 		 */
608*3a82bea4SAndy Fiddaman 		ASSERT(vif->vif_tx_csum);
609*3a82bea4SAndy Fiddaman 
610*3a82bea4SAndy Fiddaman 		/*
611*3a82bea4SAndy Fiddaman 		 * We only asked for partial csum packets.
612*3a82bea4SAndy Fiddaman 		 */
613*3a82bea4SAndy Fiddaman 		ASSERT(!(csum_flags & HCK_IPV4_HDRCKSUM));
614*3a82bea4SAndy Fiddaman 		ASSERT(!(csum_flags & HCK_FULLCKSUM));
615*3a82bea4SAndy Fiddaman 
616*3a82bea4SAndy Fiddaman 		if (ether->ether_type == htons(ETHERTYPE_VLAN)) {
617*3a82bea4SAndy Fiddaman 			eth_hsize = sizeof (struct ether_vlan_header);
618*3a82bea4SAndy Fiddaman 		} else {
619*3a82bea4SAndy Fiddaman 			eth_hsize = sizeof (struct ether_header);
620*3a82bea4SAndy Fiddaman 		}
621*3a82bea4SAndy Fiddaman 
622*3a82bea4SAndy Fiddaman 		vnh->vnh_flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
623*3a82bea4SAndy Fiddaman 		vnh->vnh_csum_start = eth_hsize + csum_start;
624*3a82bea4SAndy Fiddaman 		vnh->vnh_csum_offset = csum_stuff - csum_start;
625*3a82bea4SAndy Fiddaman 	}
626*3a82bea4SAndy Fiddaman 
627*3a82bea4SAndy Fiddaman 	/*
628*3a82bea4SAndy Fiddaman 	 * Setup LSO fields if required.
629*3a82bea4SAndy Fiddaman 	 */
630*3a82bea4SAndy Fiddaman 	if (lso_required) {
631*3a82bea4SAndy Fiddaman 		const mac_ether_offload_flags_t needed =
632*3a82bea4SAndy Fiddaman 		    MEOI_L2INFO_SET | MEOI_L3INFO_SET | MEOI_L4INFO_SET;
633*3a82bea4SAndy Fiddaman 		mac_ether_offload_info_t meo;
634*3a82bea4SAndy Fiddaman 		uint32_t cksum;
635*3a82bea4SAndy Fiddaman 		size_t len, hdrs_len;
636*3a82bea4SAndy Fiddaman 		tcpha_t *tcpha;
637*3a82bea4SAndy Fiddaman 
638*3a82bea4SAndy Fiddaman 		mac_ether_offload_info(mp, &meo);
639*3a82bea4SAndy Fiddaman 		if ((meo.meoi_flags & needed) != needed) {
640*3a82bea4SAndy Fiddaman 			goto drop;
641*3a82bea4SAndy Fiddaman 		}
642*3a82bea4SAndy Fiddaman 
643*3a82bea4SAndy Fiddaman 		if (meo.meoi_l4proto != IPPROTO_TCP) {
644*3a82bea4SAndy Fiddaman 			goto drop;
645*3a82bea4SAndy Fiddaman 		}
646*3a82bea4SAndy Fiddaman 
647*3a82bea4SAndy Fiddaman 		if (meo.meoi_l3proto == ETHERTYPE_IP && vif->vif_tx_tso4) {
648*3a82bea4SAndy Fiddaman 			vnh->vnh_gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
649*3a82bea4SAndy Fiddaman 		} else if (meo.meoi_l3proto == ETHERTYPE_IPV6 &&
650*3a82bea4SAndy Fiddaman 		    vif->vif_tx_tso6) {
651*3a82bea4SAndy Fiddaman 			vnh->vnh_gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
652*3a82bea4SAndy Fiddaman 		} else {
653*3a82bea4SAndy Fiddaman 			goto drop;
654*3a82bea4SAndy Fiddaman 		}
655*3a82bea4SAndy Fiddaman 
656*3a82bea4SAndy Fiddaman 		/*
657*3a82bea4SAndy Fiddaman 		 * The TCP stack does not include the length in the TCP
658*3a82bea4SAndy Fiddaman 		 * pseudo-header when it is performing LSO since hardware
659*3a82bea4SAndy Fiddaman 		 * generally asks for it to be removed (as it'll change).
660*3a82bea4SAndy Fiddaman 		 * Unfortunately, for virtio, we actually need it. This means we
661*3a82bea4SAndy Fiddaman 		 * need to go through and calculate the actual length and fix
662*3a82bea4SAndy Fiddaman 		 * things up. Because the virtio spec cares about the ECN flag
663*3a82bea4SAndy Fiddaman 		 * and indicating that, at least this means we'll have that
664*3a82bea4SAndy Fiddaman 		 * available as well.
665*3a82bea4SAndy Fiddaman 		 */
666*3a82bea4SAndy Fiddaman 		hdrs_len = meo.meoi_l2hlen + meo.meoi_l3hlen + meo.meoi_l4hlen;
667*3a82bea4SAndy Fiddaman 		if (MBLKL(mp) < hdrs_len) {
668*3a82bea4SAndy Fiddaman 			mblk_t *pullmp;
669*3a82bea4SAndy Fiddaman 
670*3a82bea4SAndy Fiddaman 			/*
671*3a82bea4SAndy Fiddaman 			 * The headers do not all sit within the first mblk.
672*3a82bea4SAndy Fiddaman 			 * Replace the message with an equivalent one in which
673*3a82bea4SAndy Fiddaman 			 * they do, so that the checksum manipulation below
674*3a82bea4SAndy Fiddaman 			 * lands in the message that is transmitted.
675*3a82bea4SAndy Fiddaman 			 */
676*3a82bea4SAndy Fiddaman 			if ((pullmp = msgpullup(mp, hdrs_len)) == NULL)
677*3a82bea4SAndy Fiddaman 				goto drop;
678*3a82bea4SAndy Fiddaman 			freemsg(mp);
679*3a82bea4SAndy Fiddaman 			mp = pullmp;
680*3a82bea4SAndy Fiddaman 			ether = (void *)mp->b_rptr;
681*3a82bea4SAndy Fiddaman 		}
682*3a82bea4SAndy Fiddaman 		tcpha = (tcpha_t *)(mp->b_rptr + meo.meoi_l2hlen +
683*3a82bea4SAndy Fiddaman 		    meo.meoi_l3hlen);
684*3a82bea4SAndy Fiddaman 
685*3a82bea4SAndy Fiddaman 		len = meo.meoi_len - meo.meoi_l2hlen - meo.meoi_l3hlen;
686*3a82bea4SAndy Fiddaman 		cksum = ntohs(tcpha->tha_sum) + len;
687*3a82bea4SAndy Fiddaman 		cksum = (cksum >> 16) + (cksum & 0xffff);
688*3a82bea4SAndy Fiddaman 		cksum = (cksum >> 16) + (cksum & 0xffff);
689*3a82bea4SAndy Fiddaman 		tcpha->tha_sum = htons(cksum);
690*3a82bea4SAndy Fiddaman 
691*3a82bea4SAndy Fiddaman 		if (tcpha->tha_flags & TH_CWR) {
692*3a82bea4SAndy Fiddaman 			vnh->vnh_gso_type |= VIRTIO_NET_HDR_GSO_ECN;
693*3a82bea4SAndy Fiddaman 		}
694*3a82bea4SAndy Fiddaman 		vnh->vnh_gso_size = (uint16_t)lso_mss;
695*3a82bea4SAndy Fiddaman 		vnh->vnh_hdr_len = hdrs_len;
696*3a82bea4SAndy Fiddaman 	}
697*3a82bea4SAndy Fiddaman 
698*3a82bea4SAndy Fiddaman 	/*
699*3a82bea4SAndy Fiddaman 	 * The device does not maintain its own statistics about broadcast or
700*3a82bea4SAndy Fiddaman 	 * multicast packets, so we have to check the destination address
701*3a82bea4SAndy Fiddaman 	 * ourselves.
702*3a82bea4SAndy Fiddaman 	 */
703*3a82bea4SAndy Fiddaman 	if ((ether->ether_dhost.ether_addr_octet[0] & 0x01) != 0) {
704*3a82bea4SAndy Fiddaman 		mutex_enter(&txq->vtq_mutex);
705*3a82bea4SAndy Fiddaman 		if (ether_cmp(&ether->ether_dhost, vioif_broadcast) == 0) {
706*3a82bea4SAndy Fiddaman 			txq->vtq_brdcstxmt++;
707*3a82bea4SAndy Fiddaman 		} else {
708*3a82bea4SAndy Fiddaman 			txq->vtq_multixmt++;
709*3a82bea4SAndy Fiddaman 		}
710*3a82bea4SAndy Fiddaman 		mutex_exit(&txq->vtq_mutex);
711*3a82bea4SAndy Fiddaman 	}
712*3a82bea4SAndy Fiddaman 
713*3a82bea4SAndy Fiddaman 	/*
714*3a82bea4SAndy Fiddaman 	 * For small packets, copy into the preallocated inline buffer rather
715*3a82bea4SAndy Fiddaman 	 * than incur the overhead of mapping.  Note that both of these
716*3a82bea4SAndy Fiddaman 	 * functions ensure that "mp" is freed before returning.
717*3a82bea4SAndy Fiddaman 	 */
718*3a82bea4SAndy Fiddaman 	if (msg_size < vif->vif_txcopy_thresh) {
719*3a82bea4SAndy Fiddaman 		ret = vioif_tx_inline(txq, tb, mp, msg_size);
720*3a82bea4SAndy Fiddaman 		mp = NULL;
721*3a82bea4SAndy Fiddaman 	} else {
722*3a82bea4SAndy Fiddaman 		vioif_txext_result_t xres;
723*3a82bea4SAndy Fiddaman 
724*3a82bea4SAndy Fiddaman 		xres = vioif_tx_external(txq, tb, mp, msg_size);
725*3a82bea4SAndy Fiddaman 		if (xres == VIOIF_TXEXT_TOOBIG) {
726*3a82bea4SAndy Fiddaman 			mblk_t *pulled;
727*3a82bea4SAndy Fiddaman 
728*3a82bea4SAndy Fiddaman 			/*
729*3a82bea4SAndy Fiddaman 			 * The message is spread across more disjoint pieces
730*3a82bea4SAndy Fiddaman 			 * of memory than a descriptor chain can carry.
731*3a82bea4SAndy Fiddaman 			 * Rebuild it as a single contiguous message, re-add
732*3a82bea4SAndy Fiddaman 			 * the header descriptor that was lost when the chain
733*3a82bea4SAndy Fiddaman 			 * was cleared, and try once more. The copy is
734*3a82bea4SAndy Fiddaman 			 * expensive, but better than dropping the frame.
735*3a82bea4SAndy Fiddaman 			 */
736*3a82bea4SAndy Fiddaman 			if ((pulled = msgpullup(mp, -1)) == NULL)
737*3a82bea4SAndy Fiddaman 				goto drop;
738*3a82bea4SAndy Fiddaman 			freemsg(mp);
739*3a82bea4SAndy Fiddaman 			mp = pulled;
740*3a82bea4SAndy Fiddaman 
741*3a82bea4SAndy Fiddaman 			if (vioif_tx_header_append(vif, tb) != DDI_SUCCESS)
742*3a82bea4SAndy Fiddaman 				goto drop;
743*3a82bea4SAndy Fiddaman 			xres = vioif_tx_external(txq, tb, mp, msg_size);
744*3a82bea4SAndy Fiddaman 		}
745*3a82bea4SAndy Fiddaman 
746*3a82bea4SAndy Fiddaman 		switch (xres) {
747*3a82bea4SAndy Fiddaman 		case VIOIF_TXEXT_OK:
748*3a82bea4SAndy Fiddaman 			ret = DDI_SUCCESS;
749*3a82bea4SAndy Fiddaman 			/*
750*3a82bea4SAndy Fiddaman 			 * The message is held in `tb_mp` until reclaim and
751*3a82bea4SAndy Fiddaman 			 * we no longer own it.
752*3a82bea4SAndy Fiddaman 			 */
753*3a82bea4SAndy Fiddaman 			mp = NULL;
754*3a82bea4SAndy Fiddaman 			break;
755*3a82bea4SAndy Fiddaman 		case VIOIF_TXEXT_FAIL:
756*3a82bea4SAndy Fiddaman 			/* The message has already been freed. */
757*3a82bea4SAndy Fiddaman 			ret = DDI_FAILURE;
758*3a82bea4SAndy Fiddaman 			mp = NULL;
759*3a82bea4SAndy Fiddaman 			break;
760*3a82bea4SAndy Fiddaman 		case VIOIF_TXEXT_TOOBIG:
761*3a82bea4SAndy Fiddaman 		default:
762*3a82bea4SAndy Fiddaman 			/*
763*3a82bea4SAndy Fiddaman 			 * A single contiguous message cannot exceed the chain
764*3a82bea4SAndy Fiddaman 			 * capacity, so this should not recur after the pullup
765*3a82bea4SAndy Fiddaman 			 * above. Drop the frame if it somehow does.
766*3a82bea4SAndy Fiddaman 			 */
767*3a82bea4SAndy Fiddaman 			goto drop;
768*3a82bea4SAndy Fiddaman 		}
769*3a82bea4SAndy Fiddaman 	}
770*3a82bea4SAndy Fiddaman 
771*3a82bea4SAndy Fiddaman 	if (ret != DDI_SUCCESS) {
772*3a82bea4SAndy Fiddaman 		goto drop;
773*3a82bea4SAndy Fiddaman 	}
774*3a82bea4SAndy Fiddaman 
775*3a82bea4SAndy Fiddaman 	mutex_enter(&txq->vtq_mutex);
776*3a82bea4SAndy Fiddaman 	txq->vtq_opackets++;
777*3a82bea4SAndy Fiddaman 	txq->vtq_obytes += msg_size;
778*3a82bea4SAndy Fiddaman 	mutex_exit(&txq->vtq_mutex);
779*3a82bea4SAndy Fiddaman 
780*3a82bea4SAndy Fiddaman 	virtio_dma_sync(tb->tb_dma, DDI_DMA_SYNC_FORDEV);
781*3a82bea4SAndy Fiddaman 	virtio_chain_submit(tb->tb_chain, B_TRUE);
782*3a82bea4SAndy Fiddaman 
783*3a82bea4SAndy Fiddaman 	return (B_TRUE);
784*3a82bea4SAndy Fiddaman 
785*3a82bea4SAndy Fiddaman drop:
786*3a82bea4SAndy Fiddaman 	/*
787*3a82bea4SAndy Fiddaman 	 * This message cannot be transmitted. Consume and drop it, counting
788*3a82bea4SAndy Fiddaman 	 * it against the ring's error statistic. Returning it to MAC would
789*3a82bea4SAndy Fiddaman 	 * block the ring behind a condition that descriptor reclamation
790*3a82bea4SAndy Fiddaman 	 * cannot resolve.
791*3a82bea4SAndy Fiddaman 	 */
792*3a82bea4SAndy Fiddaman 	freemsg(mp);
793*3a82bea4SAndy Fiddaman 
794*3a82bea4SAndy Fiddaman 	mutex_enter(&txq->vtq_mutex);
795*3a82bea4SAndy Fiddaman 	txq->vtq_oerrors++;
796*3a82bea4SAndy Fiddaman 	vioif_txbuf_free(txq, tb);
797*3a82bea4SAndy Fiddaman 	mutex_exit(&txq->vtq_mutex);
798*3a82bea4SAndy Fiddaman 
799*3a82bea4SAndy Fiddaman 	return (B_TRUE);
800*3a82bea4SAndy Fiddaman }
801*3a82bea4SAndy Fiddaman 
802*3a82bea4SAndy Fiddaman /*
803*3a82bea4SAndy Fiddaman  * This is the MAC transmit entrypoint for a single transmit ring; MAC hands
804*3a82bea4SAndy Fiddaman  * us one frame at a time. Returning NULL indicates the frame was consumed,
805*3a82bea4SAndy Fiddaman  * whether it was transmitted or dropped; returning the message blocks the
806*3a82bea4SAndy Fiddaman  * ring until we call mac_tx_ring_update().
807*3a82bea4SAndy Fiddaman  */
808*3a82bea4SAndy Fiddaman static mblk_t *
vioif_ring_tx(void * arg,mblk_t * mp)809*3a82bea4SAndy Fiddaman vioif_ring_tx(void *arg, mblk_t *mp)
810*3a82bea4SAndy Fiddaman {
811*3a82bea4SAndy Fiddaman 	vioif_txq_t *txq = arg;
812*3a82bea4SAndy Fiddaman 
813*3a82bea4SAndy Fiddaman 	VERIFY3P(mp->b_next, ==, NULL);
814*3a82bea4SAndy Fiddaman 
815*3a82bea4SAndy Fiddaman 	/*
816*3a82bea4SAndy Fiddaman 	 * Prior to attempting to send any more frames, do a reclaim to pick up
817*3a82bea4SAndy Fiddaman 	 * any descriptors which have been processed by the host.
818*3a82bea4SAndy Fiddaman 	 */
819*3a82bea4SAndy Fiddaman 	if (virtio_queue_nactive(txq->vtq_vq) != 0) {
820*3a82bea4SAndy Fiddaman 		(void) vioif_reclaim_used_tx(txq);
821*3a82bea4SAndy Fiddaman 	}
822*3a82bea4SAndy Fiddaman 
823*3a82bea4SAndy Fiddaman 	for (;;) {
824*3a82bea4SAndy Fiddaman 		if (vioif_send(txq, mp)) {
825*3a82bea4SAndy Fiddaman 			break;
826*3a82bea4SAndy Fiddaman 		}
827*3a82bea4SAndy Fiddaman 
828*3a82bea4SAndy Fiddaman 		/*
829*3a82bea4SAndy Fiddaman 		 * If there are no descriptors available, try to reclaim some,
830*3a82bea4SAndy Fiddaman 		 * allowing a retry of the send if some are found.
831*3a82bea4SAndy Fiddaman 		 */
832*3a82bea4SAndy Fiddaman 		if (vioif_reclaim_used_tx(txq) != 0) {
833*3a82bea4SAndy Fiddaman 			continue;
834*3a82bea4SAndy Fiddaman 		}
835*3a82bea4SAndy Fiddaman 
836*3a82bea4SAndy Fiddaman 		mutex_enter(&txq->vtq_mutex);
837*3a82bea4SAndy Fiddaman 		if (!list_is_empty(&txq->vtq_bufs)) {
838*3a82bea4SAndy Fiddaman 			/*
839*3a82bea4SAndy Fiddaman 			 * A racing reclaim returned buffers to the free list
840*3a82bea4SAndy Fiddaman 			 * after our own reclaim found nothing. Retry the
841*3a82bea4SAndy Fiddaman 			 * send.
842*3a82bea4SAndy Fiddaman 			 */
843*3a82bea4SAndy Fiddaman 			mutex_exit(&txq->vtq_mutex);
844*3a82bea4SAndy Fiddaman 			continue;
845*3a82bea4SAndy Fiddaman 		}
846*3a82bea4SAndy Fiddaman 
847*3a82bea4SAndy Fiddaman 		/*
848*3a82bea4SAndy Fiddaman 		 * Otherwise, enable the TX ring interrupt so that as soon as
849*3a82bea4SAndy Fiddaman 		 * a descriptor becomes available, transmission can begin
850*3a82bea4SAndy Fiddaman 		 * again. For safety, make sure the periodic reclaim is
851*3a82bea4SAndy Fiddaman 		 * running as well.
852*3a82bea4SAndy Fiddaman 		 */
853*3a82bea4SAndy Fiddaman 		txq->vtq_corked = true;
854*3a82bea4SAndy Fiddaman 		virtio_queue_no_interrupt(txq->vtq_vq, B_FALSE);
855*3a82bea4SAndy Fiddaman 		vioif_reclaim_restart(txq);
856*3a82bea4SAndy Fiddaman 		mutex_exit(&txq->vtq_mutex);
857*3a82bea4SAndy Fiddaman 
858*3a82bea4SAndy Fiddaman 		/*
859*3a82bea4SAndy Fiddaman 		 * Descriptors returned by the device before the interrupt was
860*3a82bea4SAndy Fiddaman 		 * enabled above will never raise it. Reclaim once more now.
861*3a82bea4SAndy Fiddaman 		 * If anything is found this also clears the cork and notifies
862*3a82bea4SAndy Fiddaman 		 * MAC, and the send can be retried immediately.
863*3a82bea4SAndy Fiddaman 		 */
864*3a82bea4SAndy Fiddaman 		if (vioif_reclaim_used_tx(txq) != 0) {
865*3a82bea4SAndy Fiddaman 			continue;
866*3a82bea4SAndy Fiddaman 		}
867*3a82bea4SAndy Fiddaman 
868*3a82bea4SAndy Fiddaman 		return (mp);
869*3a82bea4SAndy Fiddaman 	}
870*3a82bea4SAndy Fiddaman 
871*3a82bea4SAndy Fiddaman 	/* Ensure the periodic reclaim has been started. */
872*3a82bea4SAndy Fiddaman 	mutex_enter(&txq->vtq_mutex);
873*3a82bea4SAndy Fiddaman 	vioif_reclaim_restart(txq);
874*3a82bea4SAndy Fiddaman 	mutex_exit(&txq->vtq_mutex);
875*3a82bea4SAndy Fiddaman 
876*3a82bea4SAndy Fiddaman 	return (NULL);
877*3a82bea4SAndy Fiddaman }
878*3a82bea4SAndy Fiddaman 
879*3a82bea4SAndy Fiddaman uint_t
vioif_tx_handler(caddr_t arg0,caddr_t arg1 __unused)880*3a82bea4SAndy Fiddaman vioif_tx_handler(caddr_t arg0, caddr_t arg1 __unused)
881*3a82bea4SAndy Fiddaman {
882*3a82bea4SAndy Fiddaman 	vioif_txq_t *txq = (vioif_txq_t *)arg0;
883*3a82bea4SAndy Fiddaman 
884*3a82bea4SAndy Fiddaman 	/*
885*3a82bea4SAndy Fiddaman 	 * The TX interrupt could race with other reclamation activity, so
886*3a82bea4SAndy Fiddaman 	 * interpreting the return value is unimportant.
887*3a82bea4SAndy Fiddaman 	 */
888*3a82bea4SAndy Fiddaman 	(void) vioif_reclaim_used_tx(txq);
889*3a82bea4SAndy Fiddaman 
890*3a82bea4SAndy Fiddaman 	return (DDI_INTR_CLAIMED);
891*3a82bea4SAndy Fiddaman }
892*3a82bea4SAndy Fiddaman 
893*3a82bea4SAndy Fiddaman static int
vioif_tx_ring_stat(mac_ring_driver_t rh,uint_t stat,uint64_t * val)894*3a82bea4SAndy Fiddaman vioif_tx_ring_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val)
895*3a82bea4SAndy Fiddaman {
896*3a82bea4SAndy Fiddaman 	vioif_txq_t *txq = (vioif_txq_t *)rh;
897*3a82bea4SAndy Fiddaman 
898*3a82bea4SAndy Fiddaman 	switch (stat) {
899*3a82bea4SAndy Fiddaman 	case MAC_STAT_OBYTES:
900*3a82bea4SAndy Fiddaman 		*val = txq->vtq_obytes;
901*3a82bea4SAndy Fiddaman 		break;
902*3a82bea4SAndy Fiddaman 	case MAC_STAT_OPACKETS:
903*3a82bea4SAndy Fiddaman 		*val = txq->vtq_opackets;
904*3a82bea4SAndy Fiddaman 		break;
905*3a82bea4SAndy Fiddaman 	default:
906*3a82bea4SAndy Fiddaman 		*val = 0;
907*3a82bea4SAndy Fiddaman 		return (ENOTSUP);
908*3a82bea4SAndy Fiddaman 	}
909*3a82bea4SAndy Fiddaman 
910*3a82bea4SAndy Fiddaman 	return (0);
911*3a82bea4SAndy Fiddaman }
912*3a82bea4SAndy Fiddaman 
913*3a82bea4SAndy Fiddaman void
vioif_fill_tx_ring(void * arg,mac_ring_type_t rtype,const int group_index,const int ring_index,mac_ring_info_t * infop,mac_ring_handle_t rh)914*3a82bea4SAndy Fiddaman vioif_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
915*3a82bea4SAndy Fiddaman     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
916*3a82bea4SAndy Fiddaman {
917*3a82bea4SAndy Fiddaman 	vioif_t *vif = arg;
918*3a82bea4SAndy Fiddaman 	vioif_txq_t *txq;
919*3a82bea4SAndy Fiddaman 
920*3a82bea4SAndy Fiddaman 	VERIFY3S(rtype, ==, MAC_RING_TYPE_TX);
921*3a82bea4SAndy Fiddaman 	/*
922*3a82bea4SAndy Fiddaman 	 * We do not provide transmit groups, so the group index here is
923*3a82bea4SAndy Fiddaman 	 * expected to be -1.
924*3a82bea4SAndy Fiddaman 	 */
925*3a82bea4SAndy Fiddaman 	VERIFY3S(group_index, ==, -1);
926*3a82bea4SAndy Fiddaman 	VERIFY3S(ring_index, >=, 0);
927*3a82bea4SAndy Fiddaman 	VERIFY3U(ring_index, <, vif->vif_nqpairs);
928*3a82bea4SAndy Fiddaman 
929*3a82bea4SAndy Fiddaman 	txq = &vif->vif_txqs[ring_index];
930*3a82bea4SAndy Fiddaman 	txq->vtq_ringh = rh;
931*3a82bea4SAndy Fiddaman 
932*3a82bea4SAndy Fiddaman 	infop->mri_driver = (mac_ring_driver_t)txq;
933*3a82bea4SAndy Fiddaman 	infop->mri_start = NULL;
934*3a82bea4SAndy Fiddaman 	infop->mri_stop = NULL;
935*3a82bea4SAndy Fiddaman 	infop->mri_tx = vioif_ring_tx;
936*3a82bea4SAndy Fiddaman 	infop->mri_stat = vioif_tx_ring_stat;
937*3a82bea4SAndy Fiddaman 
938*3a82bea4SAndy Fiddaman 	/*
939*3a82bea4SAndy Fiddaman 	 * Provide the interrupt handle for the MSI-X vector servicing this
940*3a82bea4SAndy Fiddaman 	 * queue, if it has one, so that MAC can retarget the interrupt to
941*3a82bea4SAndy Fiddaman 	 * the CPU associated with the ring.
942*3a82bea4SAndy Fiddaman 	 */
943*3a82bea4SAndy Fiddaman 	infop->mri_intr.mi_ddi_handle = virtio_queue_intr_handle(txq->vtq_vq);
944*3a82bea4SAndy Fiddaman }
945