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(ðer->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