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: RECEIVE PATH
49*3a82bea4SAndy Fiddaman *
50*3a82bea4SAndy Fiddaman * Receive buffer management and frame receipt, including the receive ring
51*3a82bea4SAndy Fiddaman * 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/systm.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/sysmacros.h>
66*3a82bea4SAndy Fiddaman #include <sys/list.h>
67*3a82bea4SAndy Fiddaman #include <sys/ddi.h>
68*3a82bea4SAndy Fiddaman #include <sys/sunddi.h>
69*3a82bea4SAndy Fiddaman
70*3a82bea4SAndy Fiddaman #include <sys/mac.h>
71*3a82bea4SAndy Fiddaman #include <sys/mac_provider.h>
72*3a82bea4SAndy Fiddaman
73*3a82bea4SAndy Fiddaman #include "virtio.h"
74*3a82bea4SAndy Fiddaman #include "vioif.h"
75*3a82bea4SAndy Fiddaman
76*3a82bea4SAndy Fiddaman static uint_t vioif_add_rx(vioif_rxq_t *);
77*3a82bea4SAndy Fiddaman
78*3a82bea4SAndy Fiddaman static vioif_rxbuf_t *
vioif_rxbuf_alloc(vioif_rxq_t * rxq)79*3a82bea4SAndy Fiddaman vioif_rxbuf_alloc(vioif_rxq_t *rxq)
80*3a82bea4SAndy Fiddaman {
81*3a82bea4SAndy Fiddaman vioif_rxbuf_t *rb;
82*3a82bea4SAndy Fiddaman
83*3a82bea4SAndy Fiddaman VERIFY(MUTEX_HELD(&rxq->vrq_mutex));
84*3a82bea4SAndy Fiddaman
85*3a82bea4SAndy Fiddaman if ((rb = list_remove_head(&rxq->vrq_bufs)) != NULL) {
86*3a82bea4SAndy Fiddaman rxq->vrq_nbufs_alloc++;
87*3a82bea4SAndy Fiddaman }
88*3a82bea4SAndy Fiddaman
89*3a82bea4SAndy Fiddaman return (rb);
90*3a82bea4SAndy Fiddaman }
91*3a82bea4SAndy Fiddaman
92*3a82bea4SAndy Fiddaman void
vioif_rxbuf_free(vioif_rxq_t * rxq,vioif_rxbuf_t * rb)93*3a82bea4SAndy Fiddaman vioif_rxbuf_free(vioif_rxq_t *rxq, vioif_rxbuf_t *rb)
94*3a82bea4SAndy Fiddaman {
95*3a82bea4SAndy Fiddaman VERIFY(MUTEX_HELD(&rxq->vrq_mutex));
96*3a82bea4SAndy Fiddaman
97*3a82bea4SAndy Fiddaman VERIFY3U(rxq->vrq_nbufs_alloc, >, 0);
98*3a82bea4SAndy Fiddaman rxq->vrq_nbufs_alloc--;
99*3a82bea4SAndy Fiddaman
100*3a82bea4SAndy Fiddaman virtio_chain_clear(rb->rb_chain);
101*3a82bea4SAndy Fiddaman list_insert_head(&rxq->vrq_bufs, rb);
102*3a82bea4SAndy Fiddaman }
103*3a82bea4SAndy Fiddaman
104*3a82bea4SAndy Fiddaman static void
vioif_rx_free_callback(caddr_t free_arg)105*3a82bea4SAndy Fiddaman vioif_rx_free_callback(caddr_t free_arg)
106*3a82bea4SAndy Fiddaman {
107*3a82bea4SAndy Fiddaman vioif_rxbuf_t *rb = (vioif_rxbuf_t *)free_arg;
108*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = rb->rb_rxq;
109*3a82bea4SAndy Fiddaman
110*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
111*3a82bea4SAndy Fiddaman
112*3a82bea4SAndy Fiddaman /*
113*3a82bea4SAndy Fiddaman * Return this receive buffer to the free list.
114*3a82bea4SAndy Fiddaman */
115*3a82bea4SAndy Fiddaman vioif_rxbuf_free(rxq, rb);
116*3a82bea4SAndy Fiddaman
117*3a82bea4SAndy Fiddaman VERIFY3U(rxq->vrq_nbufs_onloan, >, 0);
118*3a82bea4SAndy Fiddaman rxq->vrq_nbufs_onloan--;
119*3a82bea4SAndy Fiddaman
120*3a82bea4SAndy Fiddaman /*
121*3a82bea4SAndy Fiddaman * Attempt to replenish the receive queue with at least the buffer we
122*3a82bea4SAndy Fiddaman * just freed. There isn't a great way to deal with failure here,
123*3a82bea4SAndy Fiddaman * though because we'll only loan at most half of the buffers there
124*3a82bea4SAndy Fiddaman * should always be at least some available even if this fails.
125*3a82bea4SAndy Fiddaman */
126*3a82bea4SAndy Fiddaman (void) vioif_add_rx(rxq);
127*3a82bea4SAndy Fiddaman
128*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
129*3a82bea4SAndy Fiddaman }
130*3a82bea4SAndy Fiddaman
131*3a82bea4SAndy Fiddaman int
vioif_alloc_rxq_bufs(vioif_rxq_t * rxq)132*3a82bea4SAndy Fiddaman vioif_alloc_rxq_bufs(vioif_rxq_t *rxq)
133*3a82bea4SAndy Fiddaman {
134*3a82bea4SAndy Fiddaman vioif_t *vif = rxq->vrq_vif;
135*3a82bea4SAndy Fiddaman
136*3a82bea4SAndy Fiddaman rxq->vrq_bufs_capacity = MIN(VIRTIO_NET_RX_BUFS,
137*3a82bea4SAndy Fiddaman virtio_queue_size(rxq->vrq_vq));
138*3a82bea4SAndy Fiddaman rxq->vrq_bufs_mem = kmem_zalloc(
139*3a82bea4SAndy Fiddaman sizeof (vioif_rxbuf_t) * rxq->vrq_bufs_capacity, KM_SLEEP);
140*3a82bea4SAndy Fiddaman list_create(&rxq->vrq_bufs, sizeof (vioif_rxbuf_t),
141*3a82bea4SAndy Fiddaman offsetof(vioif_rxbuf_t, rb_link));
142*3a82bea4SAndy Fiddaman
143*3a82bea4SAndy Fiddaman /*
144*3a82bea4SAndy Fiddaman * Do not loan more than half of our allocated receive buffers into
145*3a82bea4SAndy Fiddaman * the networking stack.
146*3a82bea4SAndy Fiddaman */
147*3a82bea4SAndy Fiddaman rxq->vrq_nbufs_onloan_max = rxq->vrq_bufs_capacity / 2;
148*3a82bea4SAndy Fiddaman
149*3a82bea4SAndy Fiddaman for (uint_t i = 0; i < rxq->vrq_bufs_capacity; i++) {
150*3a82bea4SAndy Fiddaman list_insert_tail(&rxq->vrq_bufs, &rxq->vrq_bufs_mem[i]);
151*3a82bea4SAndy Fiddaman }
152*3a82bea4SAndy Fiddaman
153*3a82bea4SAndy Fiddaman /*
154*3a82bea4SAndy Fiddaman * The receive buffers are large, and we can tolerate a large number
155*3a82bea4SAndy Fiddaman * of segments. Adjust the SGL entry count, setting aside one segment
156*3a82bea4SAndy Fiddaman * for the virtio net header.
157*3a82bea4SAndy Fiddaman */
158*3a82bea4SAndy Fiddaman ddi_dma_attr_t attr = vioif_dma_attr_bufs;
159*3a82bea4SAndy Fiddaman attr.dma_attr_sgllen = VIOIF_MAX_SEGS - 1;
160*3a82bea4SAndy Fiddaman
161*3a82bea4SAndy Fiddaman for (vioif_rxbuf_t *rb = list_head(&rxq->vrq_bufs); rb != NULL;
162*3a82bea4SAndy Fiddaman rb = list_next(&rxq->vrq_bufs, rb)) {
163*3a82bea4SAndy Fiddaman if ((rb->rb_dma = virtio_dma_alloc(vif->vif_virtio,
164*3a82bea4SAndy Fiddaman VIOIF_RX_BUF_SIZE, &attr, DDI_DMA_STREAMING | DDI_DMA_READ,
165*3a82bea4SAndy Fiddaman KM_SLEEP)) == NULL) {
166*3a82bea4SAndy Fiddaman return (ENOMEM);
167*3a82bea4SAndy Fiddaman }
168*3a82bea4SAndy Fiddaman
169*3a82bea4SAndy Fiddaman if ((rb->rb_chain = virtio_chain_alloc(rxq->vrq_vq,
170*3a82bea4SAndy Fiddaman KM_SLEEP)) == NULL) {
171*3a82bea4SAndy Fiddaman return (ENOMEM);
172*3a82bea4SAndy Fiddaman }
173*3a82bea4SAndy Fiddaman virtio_chain_data_set(rb->rb_chain, rb);
174*3a82bea4SAndy Fiddaman
175*3a82bea4SAndy Fiddaman /*
176*3a82bea4SAndy Fiddaman * Ensure that the first cookie is sufficient to cover the
177*3a82bea4SAndy Fiddaman * header skip region plus one byte.
178*3a82bea4SAndy Fiddaman */
179*3a82bea4SAndy Fiddaman VERIFY3U(virtio_dma_cookie_size(rb->rb_dma, 0), >=,
180*3a82bea4SAndy Fiddaman VIOIF_HEADER_SKIP + 1);
181*3a82bea4SAndy Fiddaman
182*3a82bea4SAndy Fiddaman /*
183*3a82bea4SAndy Fiddaman * Ensure that the frame data begins at a location with a
184*3a82bea4SAndy Fiddaman * correctly aligned IP header.
185*3a82bea4SAndy Fiddaman */
186*3a82bea4SAndy Fiddaman VERIFY3U((uintptr_t)virtio_dma_va(rb->rb_dma,
187*3a82bea4SAndy Fiddaman VIOIF_HEADER_SKIP) % 4, ==, 2);
188*3a82bea4SAndy Fiddaman
189*3a82bea4SAndy Fiddaman rb->rb_rxq = rxq;
190*3a82bea4SAndy Fiddaman rb->rb_frtn.free_func = vioif_rx_free_callback;
191*3a82bea4SAndy Fiddaman rb->rb_frtn.free_arg = (caddr_t)rb;
192*3a82bea4SAndy Fiddaman }
193*3a82bea4SAndy Fiddaman
194*3a82bea4SAndy Fiddaman return (0);
195*3a82bea4SAndy Fiddaman }
196*3a82bea4SAndy Fiddaman
197*3a82bea4SAndy Fiddaman void
vioif_free_rxq_bufs(vioif_rxq_t * rxq)198*3a82bea4SAndy Fiddaman vioif_free_rxq_bufs(vioif_rxq_t *rxq)
199*3a82bea4SAndy Fiddaman {
200*3a82bea4SAndy Fiddaman if (rxq->vrq_bufs_mem == NULL)
201*3a82bea4SAndy Fiddaman return;
202*3a82bea4SAndy Fiddaman
203*3a82bea4SAndy Fiddaman VERIFY3U(rxq->vrq_nbufs_alloc, ==, 0);
204*3a82bea4SAndy Fiddaman for (uint_t i = 0; i < rxq->vrq_bufs_capacity; i++) {
205*3a82bea4SAndy Fiddaman vioif_rxbuf_t *rb = &rxq->vrq_bufs_mem[i];
206*3a82bea4SAndy Fiddaman
207*3a82bea4SAndy Fiddaman /*
208*3a82bea4SAndy Fiddaman * Ensure that this rxbuf is now in the free list:
209*3a82bea4SAndy Fiddaman */
210*3a82bea4SAndy Fiddaman VERIFY(list_link_active(&rb->rb_link));
211*3a82bea4SAndy Fiddaman list_remove(&rxq->vrq_bufs, rb);
212*3a82bea4SAndy Fiddaman
213*3a82bea4SAndy Fiddaman if (rb->rb_dma != NULL) {
214*3a82bea4SAndy Fiddaman virtio_dma_free(rb->rb_dma);
215*3a82bea4SAndy Fiddaman rb->rb_dma = NULL;
216*3a82bea4SAndy Fiddaman }
217*3a82bea4SAndy Fiddaman
218*3a82bea4SAndy Fiddaman if (rb->rb_chain != NULL) {
219*3a82bea4SAndy Fiddaman virtio_chain_free(rb->rb_chain);
220*3a82bea4SAndy Fiddaman rb->rb_chain = NULL;
221*3a82bea4SAndy Fiddaman }
222*3a82bea4SAndy Fiddaman }
223*3a82bea4SAndy Fiddaman VERIFY(list_is_empty(&rxq->vrq_bufs));
224*3a82bea4SAndy Fiddaman list_destroy(&rxq->vrq_bufs);
225*3a82bea4SAndy Fiddaman
226*3a82bea4SAndy Fiddaman kmem_free(rxq->vrq_bufs_mem,
227*3a82bea4SAndy Fiddaman sizeof (vioif_rxbuf_t) * rxq->vrq_bufs_capacity);
228*3a82bea4SAndy Fiddaman rxq->vrq_bufs_mem = NULL;
229*3a82bea4SAndy Fiddaman rxq->vrq_bufs_capacity = 0;
230*3a82bea4SAndy Fiddaman }
231*3a82bea4SAndy Fiddaman
232*3a82bea4SAndy Fiddaman /*
233*3a82bea4SAndy Fiddaman * The time to wait in vioif_rx_drain() for loaned receive buffers to be
234*3a82bea4SAndy Fiddaman * returned by the networking stack, in milliseconds.
235*3a82bea4SAndy Fiddaman */
236*3a82bea4SAndy Fiddaman uint_t vioif_rx_drain_ms = 200;
237*3a82bea4SAndy Fiddaman
238*3a82bea4SAndy Fiddaman /*
239*3a82bea4SAndy Fiddaman * Wait for receive buffers that were loaned to the networking stack to be
240*3a82bea4SAndy Fiddaman * returned, for up to `vioif_rx_drain_ms` milliseconds. A loaned buffer can
241*3a82bea4SAndy Fiddaman * be held indefinitely, so this is only a best effort. Returns true if every
242*3a82bea4SAndy Fiddaman * buffer was returned.
243*3a82bea4SAndy Fiddaman */
244*3a82bea4SAndy Fiddaman bool
vioif_rx_drain(vioif_t * vif)245*3a82bea4SAndy Fiddaman vioif_rx_drain(vioif_t *vif)
246*3a82bea4SAndy Fiddaman {
247*3a82bea4SAndy Fiddaman const hrtime_t deadline = gethrtime() + MSEC2NSEC(vioif_rx_drain_ms);
248*3a82bea4SAndy Fiddaman
249*3a82bea4SAndy Fiddaman for (;;) {
250*3a82bea4SAndy Fiddaman bool onloan = false;
251*3a82bea4SAndy Fiddaman
252*3a82bea4SAndy Fiddaman for (uint_t i = 0; i < vif->vif_nqpairs; i++) {
253*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = &vif->vif_rxqs[i];
254*3a82bea4SAndy Fiddaman
255*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
256*3a82bea4SAndy Fiddaman if (rxq->vrq_nbufs_onloan > 0)
257*3a82bea4SAndy Fiddaman onloan = true;
258*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
259*3a82bea4SAndy Fiddaman }
260*3a82bea4SAndy Fiddaman
261*3a82bea4SAndy Fiddaman if (!onloan)
262*3a82bea4SAndy Fiddaman return (true);
263*3a82bea4SAndy Fiddaman if (gethrtime() > deadline)
264*3a82bea4SAndy Fiddaman return (false);
265*3a82bea4SAndy Fiddaman
266*3a82bea4SAndy Fiddaman delay(drv_usectohz(1000));
267*3a82bea4SAndy Fiddaman }
268*3a82bea4SAndy Fiddaman }
269*3a82bea4SAndy Fiddaman
270*3a82bea4SAndy Fiddaman static uint_t
vioif_add_rx(vioif_rxq_t * rxq)271*3a82bea4SAndy Fiddaman vioif_add_rx(vioif_rxq_t *rxq)
272*3a82bea4SAndy Fiddaman {
273*3a82bea4SAndy Fiddaman VERIFY(MUTEX_HELD(&rxq->vrq_mutex));
274*3a82bea4SAndy Fiddaman
275*3a82bea4SAndy Fiddaman if (!rxq->vrq_running) {
276*3a82bea4SAndy Fiddaman /*
277*3a82bea4SAndy Fiddaman * If the ring is not running, do not give the device any
278*3a82bea4SAndy Fiddaman * receive buffers.
279*3a82bea4SAndy Fiddaman */
280*3a82bea4SAndy Fiddaman return (0);
281*3a82bea4SAndy Fiddaman }
282*3a82bea4SAndy Fiddaman
283*3a82bea4SAndy Fiddaman uint_t num_added = 0;
284*3a82bea4SAndy Fiddaman
285*3a82bea4SAndy Fiddaman vioif_t *vif = rxq->vrq_vif;
286*3a82bea4SAndy Fiddaman vioif_rxbuf_t *rb;
287*3a82bea4SAndy Fiddaman while ((rb = vioif_rxbuf_alloc(rxq)) != NULL) {
288*3a82bea4SAndy Fiddaman /*
289*3a82bea4SAndy Fiddaman * For legacy devices, and those that have not negotiated
290*3a82bea4SAndy Fiddaman * VIRTIO_F_ANY_LAYOUT, the virtio net header must appear in a
291*3a82bea4SAndy Fiddaman * separate descriptor entry to the rest of the buffer. We do
292*3a82bea4SAndy Fiddaman * the same for modern devices too.
293*3a82bea4SAndy Fiddaman */
294*3a82bea4SAndy Fiddaman if (virtio_chain_append(rb->rb_chain,
295*3a82bea4SAndy Fiddaman virtio_dma_cookie_pa(rb->rb_dma, 0), vif->vif_rxbuf_hdrlen,
296*3a82bea4SAndy Fiddaman VIRTIO_DIR_DEVICE_WRITES) != DDI_SUCCESS) {
297*3a82bea4SAndy Fiddaman goto fail;
298*3a82bea4SAndy Fiddaman }
299*3a82bea4SAndy Fiddaman
300*3a82bea4SAndy Fiddaman for (uint_t n = 0; n < virtio_dma_ncookies(rb->rb_dma); n++) {
301*3a82bea4SAndy Fiddaman uint64_t pa = virtio_dma_cookie_pa(rb->rb_dma, n);
302*3a82bea4SAndy Fiddaman size_t sz = virtio_dma_cookie_size(rb->rb_dma, n);
303*3a82bea4SAndy Fiddaman
304*3a82bea4SAndy Fiddaman if (n == 0) {
305*3a82bea4SAndy Fiddaman pa += VIOIF_HEADER_SKIP;
306*3a82bea4SAndy Fiddaman VERIFY3U(sz, >, VIOIF_HEADER_SKIP);
307*3a82bea4SAndy Fiddaman sz -= VIOIF_HEADER_SKIP;
308*3a82bea4SAndy Fiddaman }
309*3a82bea4SAndy Fiddaman
310*3a82bea4SAndy Fiddaman if (virtio_chain_append(rb->rb_chain, pa, sz,
311*3a82bea4SAndy Fiddaman VIRTIO_DIR_DEVICE_WRITES) != DDI_SUCCESS) {
312*3a82bea4SAndy Fiddaman goto fail;
313*3a82bea4SAndy Fiddaman }
314*3a82bea4SAndy Fiddaman }
315*3a82bea4SAndy Fiddaman
316*3a82bea4SAndy Fiddaman virtio_chain_submit(rb->rb_chain, B_FALSE);
317*3a82bea4SAndy Fiddaman num_added++;
318*3a82bea4SAndy Fiddaman continue;
319*3a82bea4SAndy Fiddaman
320*3a82bea4SAndy Fiddaman fail:
321*3a82bea4SAndy Fiddaman vioif_rxbuf_free(rxq, rb);
322*3a82bea4SAndy Fiddaman rxq->vrq_norecvbuf++;
323*3a82bea4SAndy Fiddaman break;
324*3a82bea4SAndy Fiddaman }
325*3a82bea4SAndy Fiddaman
326*3a82bea4SAndy Fiddaman if (num_added > 0) {
327*3a82bea4SAndy Fiddaman virtio_queue_flush(rxq->vrq_vq);
328*3a82bea4SAndy Fiddaman }
329*3a82bea4SAndy Fiddaman
330*3a82bea4SAndy Fiddaman return (num_added);
331*3a82bea4SAndy Fiddaman }
332*3a82bea4SAndy Fiddaman
333*3a82bea4SAndy Fiddaman /*
334*3a82bea4SAndy Fiddaman * Collect received frames from a receive queue, returning them as a chain of
335*3a82bea4SAndy Fiddaman * messages for delivery to MAC. This is called both from the queue interrupt
336*3a82bea4SAndy Fiddaman * handler and, with a byte budget, on behalf of the MAC poll thread. A
337*3a82bea4SAndy Fiddaman * "poll_bytes" value of "VIOIF_RX_ALL_BYTES" means no budget; collect
338*3a82bea4SAndy Fiddaman * everything the device has returned.
339*3a82bea4SAndy Fiddaman *
340*3a82bea4SAndy Fiddaman * The queue mutex must be held on entry and is held again on return, but it
341*3a82bea4SAndy Fiddaman * is dropped around message allocation and the copying of frame data, which
342*3a82bea4SAndy Fiddaman * for a large frame is a substantial amount of work to perform while holding
343*3a82bea4SAndy Fiddaman * an interrupt-priority mutex that the buffer free callback also requires.
344*3a82bea4SAndy Fiddaman */
345*3a82bea4SAndy Fiddaman #define VIOIF_RX_ALL_BYTES (-1)
346*3a82bea4SAndy Fiddaman
347*3a82bea4SAndy Fiddaman static mblk_t *
vioif_ring_rx(vioif_rxq_t * rxq,int poll_bytes)348*3a82bea4SAndy Fiddaman vioif_ring_rx(vioif_rxq_t *rxq, int poll_bytes)
349*3a82bea4SAndy Fiddaman {
350*3a82bea4SAndy Fiddaman vioif_t *vif = rxq->vrq_vif;
351*3a82bea4SAndy Fiddaman virtio_chain_t *vic;
352*3a82bea4SAndy Fiddaman mblk_t *mphead = NULL, *lastmp = NULL, *mp;
353*3a82bea4SAndy Fiddaman size_t nbytes = 0;
354*3a82bea4SAndy Fiddaman
355*3a82bea4SAndy Fiddaman VERIFY(MUTEX_HELD(&rxq->vrq_mutex));
356*3a82bea4SAndy Fiddaman
357*3a82bea4SAndy Fiddaman while ((vic = virtio_queue_poll(rxq->vrq_vq)) != NULL) {
358*3a82bea4SAndy Fiddaman /*
359*3a82bea4SAndy Fiddaman * We have to use the chain received length here, as the device
360*3a82bea4SAndy Fiddaman * does not tell us the received frame length any other way.
361*3a82bea4SAndy Fiddaman * In a limited survey of hypervisors, virtio network devices
362*3a82bea4SAndy Fiddaman * appear to provide the right value here.
363*3a82bea4SAndy Fiddaman */
364*3a82bea4SAndy Fiddaman size_t len = virtio_chain_received_length(vic);
365*3a82bea4SAndy Fiddaman vioif_rxbuf_t *rb = virtio_chain_data(vic);
366*3a82bea4SAndy Fiddaman
367*3a82bea4SAndy Fiddaman virtio_dma_sync(rb->rb_dma, DDI_DMA_SYNC_FORCPU);
368*3a82bea4SAndy Fiddaman
369*3a82bea4SAndy Fiddaman /*
370*3a82bea4SAndy Fiddaman * If the ring is not running, discard any received frames.
371*3a82bea4SAndy Fiddaman */
372*3a82bea4SAndy Fiddaman if (!rxq->vrq_running) {
373*3a82bea4SAndy Fiddaman vioif_rxbuf_free(rxq, rb);
374*3a82bea4SAndy Fiddaman continue;
375*3a82bea4SAndy Fiddaman }
376*3a82bea4SAndy Fiddaman
377*3a82bea4SAndy Fiddaman if (len <= vif->vif_rxbuf_hdrlen) {
378*3a82bea4SAndy Fiddaman rxq->vrq_rxfail_chain_undersize++;
379*3a82bea4SAndy Fiddaman rxq->vrq_ierrors++;
380*3a82bea4SAndy Fiddaman vioif_rxbuf_free(rxq, rb);
381*3a82bea4SAndy Fiddaman continue;
382*3a82bea4SAndy Fiddaman }
383*3a82bea4SAndy Fiddaman len -= vif->vif_rxbuf_hdrlen;
384*3a82bea4SAndy Fiddaman
385*3a82bea4SAndy Fiddaman /*
386*3a82bea4SAndy Fiddaman * The received length is under the control of the device and
387*3a82bea4SAndy Fiddaman * cannot be trusted. Discard anything that claims to extend
388*3a82bea4SAndy Fiddaman * beyond the data region of the receive buffer rather than
389*3a82bea4SAndy Fiddaman * publish stray kernel memory to the networking stack.
390*3a82bea4SAndy Fiddaman */
391*3a82bea4SAndy Fiddaman if (len > virtio_dma_size(rb->rb_dma) - VIOIF_HEADER_SKIP) {
392*3a82bea4SAndy Fiddaman rxq->vrq_rxfail_chain_oversize++;
393*3a82bea4SAndy Fiddaman rxq->vrq_ierrors++;
394*3a82bea4SAndy Fiddaman vioif_rxbuf_free(rxq, rb);
395*3a82bea4SAndy Fiddaman continue;
396*3a82bea4SAndy Fiddaman }
397*3a82bea4SAndy Fiddaman
398*3a82bea4SAndy Fiddaman /*
399*3a82bea4SAndy Fiddaman * We copy small packets that happen to fit into a single
400*3a82bea4SAndy Fiddaman * cookie and reuse the buffers. For bigger ones, we loan
401*3a82bea4SAndy Fiddaman * the buffers upstream.
402*3a82bea4SAndy Fiddaman */
403*3a82bea4SAndy Fiddaman if (len < vif->vif_rxcopy_thresh ||
404*3a82bea4SAndy Fiddaman rxq->vrq_nbufs_onloan >= rxq->vrq_nbufs_onloan_max) {
405*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
406*3a82bea4SAndy Fiddaman if ((mp = allocb(len, 0)) != NULL) {
407*3a82bea4SAndy Fiddaman bcopy(virtio_dma_va(rb->rb_dma,
408*3a82bea4SAndy Fiddaman VIOIF_HEADER_SKIP), mp->b_rptr, len);
409*3a82bea4SAndy Fiddaman mp->b_wptr = mp->b_rptr + len;
410*3a82bea4SAndy Fiddaman }
411*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
412*3a82bea4SAndy Fiddaman
413*3a82bea4SAndy Fiddaman /*
414*3a82bea4SAndy Fiddaman * As the packet contents was copied rather than
415*3a82bea4SAndy Fiddaman * loaned, we can return the receive buffer resources
416*3a82bea4SAndy Fiddaman * to the free list.
417*3a82bea4SAndy Fiddaman */
418*3a82bea4SAndy Fiddaman vioif_rxbuf_free(rxq, rb);
419*3a82bea4SAndy Fiddaman
420*3a82bea4SAndy Fiddaman if (mp == NULL) {
421*3a82bea4SAndy Fiddaman rxq->vrq_norecvbuf++;
422*3a82bea4SAndy Fiddaman rxq->vrq_ierrors++;
423*3a82bea4SAndy Fiddaman continue;
424*3a82bea4SAndy Fiddaman }
425*3a82bea4SAndy Fiddaman } else {
426*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
427*3a82bea4SAndy Fiddaman if ((mp = desballoc(virtio_dma_va(rb->rb_dma,
428*3a82bea4SAndy Fiddaman VIOIF_HEADER_SKIP), len, 0,
429*3a82bea4SAndy Fiddaman &rb->rb_frtn)) != NULL) {
430*3a82bea4SAndy Fiddaman mp->b_wptr = mp->b_rptr + len;
431*3a82bea4SAndy Fiddaman }
432*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
433*3a82bea4SAndy Fiddaman
434*3a82bea4SAndy Fiddaman if (mp == NULL) {
435*3a82bea4SAndy Fiddaman rxq->vrq_norecvbuf++;
436*3a82bea4SAndy Fiddaman rxq->vrq_ierrors++;
437*3a82bea4SAndy Fiddaman vioif_rxbuf_free(rxq, rb);
438*3a82bea4SAndy Fiddaman continue;
439*3a82bea4SAndy Fiddaman }
440*3a82bea4SAndy Fiddaman
441*3a82bea4SAndy Fiddaman rxq->vrq_nbufs_onloan++;
442*3a82bea4SAndy Fiddaman }
443*3a82bea4SAndy Fiddaman
444*3a82bea4SAndy Fiddaman /*
445*3a82bea4SAndy Fiddaman * virtio-net does not tell us if this packet is multicast
446*3a82bea4SAndy Fiddaman * or broadcast, so we have to check it.
447*3a82bea4SAndy Fiddaman */
448*3a82bea4SAndy Fiddaman if (mp->b_rptr[0] & 0x1) {
449*3a82bea4SAndy Fiddaman if (bcmp(mp->b_rptr, vioif_broadcast, ETHERADDRL) != 0)
450*3a82bea4SAndy Fiddaman rxq->vrq_multircv++;
451*3a82bea4SAndy Fiddaman else
452*3a82bea4SAndy Fiddaman rxq->vrq_brdcstrcv++;
453*3a82bea4SAndy Fiddaman }
454*3a82bea4SAndy Fiddaman
455*3a82bea4SAndy Fiddaman rxq->vrq_rbytes += len;
456*3a82bea4SAndy Fiddaman rxq->vrq_ipackets++;
457*3a82bea4SAndy Fiddaman
458*3a82bea4SAndy Fiddaman if (lastmp == NULL) {
459*3a82bea4SAndy Fiddaman mphead = mp;
460*3a82bea4SAndy Fiddaman } else {
461*3a82bea4SAndy Fiddaman lastmp->b_next = mp;
462*3a82bea4SAndy Fiddaman }
463*3a82bea4SAndy Fiddaman lastmp = mp;
464*3a82bea4SAndy Fiddaman
465*3a82bea4SAndy Fiddaman /*
466*3a82bea4SAndy Fiddaman * When polling, stop once we have met the byte budget. Any
467*3a82bea4SAndy Fiddaman * frames left in the ring will be collected by a subsequent
468*3a82bea4SAndy Fiddaman * poll, or by the interrupt handler once MAC has released the
469*3a82bea4SAndy Fiddaman * ring from polling.
470*3a82bea4SAndy Fiddaman */
471*3a82bea4SAndy Fiddaman nbytes += len;
472*3a82bea4SAndy Fiddaman if (poll_bytes != VIOIF_RX_ALL_BYTES &&
473*3a82bea4SAndy Fiddaman nbytes >= (size_t)poll_bytes) {
474*3a82bea4SAndy Fiddaman break;
475*3a82bea4SAndy Fiddaman }
476*3a82bea4SAndy Fiddaman }
477*3a82bea4SAndy Fiddaman
478*3a82bea4SAndy Fiddaman return (mphead);
479*3a82bea4SAndy Fiddaman }
480*3a82bea4SAndy Fiddaman
481*3a82bea4SAndy Fiddaman /*
482*3a82bea4SAndy Fiddaman * Collect frames from a receive queue and deliver them to MAC, unless the
483*3a82bea4SAndy Fiddaman * MAC poll thread currently owns the ring. This is the body of the queue
484*3a82bea4SAndy Fiddaman * interrupt handler, and is also run from the driver task queue to collect
485*3a82bea4SAndy Fiddaman * frames that were returned by the device without raising an interrupt.
486*3a82bea4SAndy Fiddaman */
487*3a82bea4SAndy Fiddaman static void
vioif_rx_collect(vioif_rxq_t * rxq)488*3a82bea4SAndy Fiddaman vioif_rx_collect(vioif_rxq_t *rxq)
489*3a82bea4SAndy Fiddaman {
490*3a82bea4SAndy Fiddaman mblk_t *mp = NULL;
491*3a82bea4SAndy Fiddaman uint64_t gen = 0;
492*3a82bea4SAndy Fiddaman
493*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
494*3a82bea4SAndy Fiddaman if (!rxq->vrq_polling) {
495*3a82bea4SAndy Fiddaman mp = vioif_ring_rx(rxq, VIOIF_RX_ALL_BYTES);
496*3a82bea4SAndy Fiddaman
497*3a82bea4SAndy Fiddaman /*
498*3a82bea4SAndy Fiddaman * Attempt to replenish the receive queue. If we cannot add
499*3a82bea4SAndy Fiddaman * any descriptors here, it may be because all of the recently
500*3a82bea4SAndy Fiddaman * received packets were loaned up to the networking stack.
501*3a82bea4SAndy Fiddaman */
502*3a82bea4SAndy Fiddaman (void) vioif_add_rx(rxq);
503*3a82bea4SAndy Fiddaman
504*3a82bea4SAndy Fiddaman gen = rxq->vrq_gen;
505*3a82bea4SAndy Fiddaman }
506*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
507*3a82bea4SAndy Fiddaman
508*3a82bea4SAndy Fiddaman if (mp != NULL) {
509*3a82bea4SAndy Fiddaman mac_rx_ring(rxq->vrq_vif->vif_mac_handle, rxq->vrq_ringh,
510*3a82bea4SAndy Fiddaman mp, gen);
511*3a82bea4SAndy Fiddaman }
512*3a82bea4SAndy Fiddaman }
513*3a82bea4SAndy Fiddaman
514*3a82bea4SAndy Fiddaman static void
vioif_rx_collect_task(void * arg)515*3a82bea4SAndy Fiddaman vioif_rx_collect_task(void *arg)
516*3a82bea4SAndy Fiddaman {
517*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = arg;
518*3a82bea4SAndy Fiddaman
519*3a82bea4SAndy Fiddaman /*
520*3a82bea4SAndy Fiddaman * Clear the flag before collecting, so that a wakeup arriving during
521*3a82bea4SAndy Fiddaman * the collection queues the task entry again rather than being lost.
522*3a82bea4SAndy Fiddaman */
523*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
524*3a82bea4SAndy Fiddaman rxq->vrq_collect_queued = false;
525*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
526*3a82bea4SAndy Fiddaman
527*3a82bea4SAndy Fiddaman vioif_rx_collect(rxq);
528*3a82bea4SAndy Fiddaman }
529*3a82bea4SAndy Fiddaman
530*3a82bea4SAndy Fiddaman /*
531*3a82bea4SAndy Fiddaman * The device makes its virtqueue interrupt suppression decision only at the
532*3a82bea4SAndy Fiddaman * moment it returns a frame, so no interrupt is ever generated for frames
533*3a82bea4SAndy Fiddaman * returned while interrupts were suppressed and re-enabling them is not
534*3a82bea4SAndy Fiddaman * retroactive. We re-enable them when a ring is started and when MAC releases
535*3a82bea4SAndy Fiddaman * a ring from polled mode. At those points, any frames already in the used
536*3a82bea4SAndy Fiddaman * ring would otherwise sit there until a future frame arrived and raised an
537*3a82bea4SAndy Fiddaman * interrupt, or indefinitely if the queue has run out of receive buffers.
538*3a82bea4SAndy Fiddaman * Collect them from a task queue. We cannot do so directly since MAC calls
539*3a82bea4SAndy Fiddaman * the interrupt enable entrypoint with SRS locks held that mac_rx_ring()
540*3a82bea4SAndy Fiddaman * also requires.
541*3a82bea4SAndy Fiddaman */
542*3a82bea4SAndy Fiddaman static void
vioif_rx_dispatch_collect(vioif_rxq_t * rxq)543*3a82bea4SAndy Fiddaman vioif_rx_dispatch_collect(vioif_rxq_t *rxq)
544*3a82bea4SAndy Fiddaman {
545*3a82bea4SAndy Fiddaman if (!virtio_queue_pending(rxq->vrq_vq))
546*3a82bea4SAndy Fiddaman return;
547*3a82bea4SAndy Fiddaman
548*3a82bea4SAndy Fiddaman /*
549*3a82bea4SAndy Fiddaman * Despatch on the pre-allocated task entry, which cannot fail and
550*3a82bea4SAndy Fiddaman * does not sleep. The flag prevents the entry from being queued a
551*3a82bea4SAndy Fiddaman * second time while it is already waiting to run.
552*3a82bea4SAndy Fiddaman */
553*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
554*3a82bea4SAndy Fiddaman if (!rxq->vrq_collect_queued) {
555*3a82bea4SAndy Fiddaman rxq->vrq_collect_queued = true;
556*3a82bea4SAndy Fiddaman taskq_dispatch_ent(rxq->vrq_vif->vif_taskq,
557*3a82bea4SAndy Fiddaman vioif_rx_collect_task, rxq, 0, &rxq->vrq_collect_tqent);
558*3a82bea4SAndy Fiddaman }
559*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
560*3a82bea4SAndy Fiddaman }
561*3a82bea4SAndy Fiddaman
562*3a82bea4SAndy Fiddaman uint_t
vioif_rx_handler(caddr_t arg0,caddr_t arg1 __unused)563*3a82bea4SAndy Fiddaman vioif_rx_handler(caddr_t arg0, caddr_t arg1 __unused)
564*3a82bea4SAndy Fiddaman {
565*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = (vioif_rxq_t *)arg0;
566*3a82bea4SAndy Fiddaman
567*3a82bea4SAndy Fiddaman vioif_rx_collect(rxq);
568*3a82bea4SAndy Fiddaman
569*3a82bea4SAndy Fiddaman return (DDI_INTR_CLAIMED);
570*3a82bea4SAndy Fiddaman }
571*3a82bea4SAndy Fiddaman
572*3a82bea4SAndy Fiddaman static int
vioif_rx_ring_start(mac_ring_driver_t rh,uint64_t gen)573*3a82bea4SAndy Fiddaman vioif_rx_ring_start(mac_ring_driver_t rh, uint64_t gen)
574*3a82bea4SAndy Fiddaman {
575*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = (vioif_rxq_t *)rh;
576*3a82bea4SAndy Fiddaman mblk_t *mp;
577*3a82bea4SAndy Fiddaman
578*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
579*3a82bea4SAndy Fiddaman rxq->vrq_gen = gen;
580*3a82bea4SAndy Fiddaman rxq->vrq_polling = false;
581*3a82bea4SAndy Fiddaman
582*3a82bea4SAndy Fiddaman /*
583*3a82bea4SAndy Fiddaman * The device may have continued to fill buffers that were left in
584*3a82bea4SAndy Fiddaman * the queue while the ring was stopped, without raising an
585*3a82bea4SAndy Fiddaman * interrupt. Those frames belong to an earlier incarnation of the
586*3a82bea4SAndy Fiddaman * ring, so discard them. Discarding also returns their buffers for
587*3a82bea4SAndy Fiddaman * reuse. With `vrq_running` still clear, `vioif_ring_rx()` frees
588*3a82bea4SAndy Fiddaman * each frame as it is collected and never allocates a message, so
589*3a82bea4SAndy Fiddaman * it cannot return a chain here.
590*3a82bea4SAndy Fiddaman */
591*3a82bea4SAndy Fiddaman mp = vioif_ring_rx(rxq, VIOIF_RX_ALL_BYTES);
592*3a82bea4SAndy Fiddaman VERIFY3P(mp, ==, NULL);
593*3a82bea4SAndy Fiddaman
594*3a82bea4SAndy Fiddaman rxq->vrq_running = true;
595*3a82bea4SAndy Fiddaman
596*3a82bea4SAndy Fiddaman /*
597*3a82bea4SAndy Fiddaman * Add as many receive buffers as we can to the receive queue. If we
598*3a82bea4SAndy Fiddaman * cannot add any, it may be because we have stopped and started again
599*3a82bea4SAndy Fiddaman * and the descriptors are all in the queue already.
600*3a82bea4SAndy Fiddaman */
601*3a82bea4SAndy Fiddaman (void) vioif_add_rx(rxq);
602*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
603*3a82bea4SAndy Fiddaman
604*3a82bea4SAndy Fiddaman virtio_queue_no_interrupt(rxq->vrq_vq, B_FALSE);
605*3a82bea4SAndy Fiddaman
606*3a82bea4SAndy Fiddaman /*
607*3a82bea4SAndy Fiddaman * A frame that the device returned after the drain above, but before
608*3a82bea4SAndy Fiddaman * the queue interrupt was re-enabled, will never raise one. Collect
609*3a82bea4SAndy Fiddaman * it now. It arrived around the time the ring started, so it is
610*3a82bea4SAndy Fiddaman * delivered under the new generation.
611*3a82bea4SAndy Fiddaman */
612*3a82bea4SAndy Fiddaman vioif_rx_dispatch_collect(rxq);
613*3a82bea4SAndy Fiddaman
614*3a82bea4SAndy Fiddaman return (0);
615*3a82bea4SAndy Fiddaman }
616*3a82bea4SAndy Fiddaman
617*3a82bea4SAndy Fiddaman static void
vioif_rx_ring_stop(mac_ring_driver_t rh)618*3a82bea4SAndy Fiddaman vioif_rx_ring_stop(mac_ring_driver_t rh)
619*3a82bea4SAndy Fiddaman {
620*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = (vioif_rxq_t *)rh;
621*3a82bea4SAndy Fiddaman
622*3a82bea4SAndy Fiddaman virtio_queue_no_interrupt(rxq->vrq_vq, B_TRUE);
623*3a82bea4SAndy Fiddaman
624*3a82bea4SAndy Fiddaman /*
625*3a82bea4SAndy Fiddaman * There is no way to instruct the device to stop using a ring, so
626*3a82bea4SAndy Fiddaman * all we can do here is stop collecting from it. The buffers the
627*3a82bea4SAndy Fiddaman * device holds cannot be recovered without a full device reset, and
628*3a82bea4SAndy Fiddaman * it will continue to fill them with received frames while the ring
629*3a82bea4SAndy Fiddaman * is stopped. Those frames are discarded when the ring is next
630*3a82bea4SAndy Fiddaman * started, and the buffers are finally recovered in detach.
631*3a82bea4SAndy Fiddaman */
632*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
633*3a82bea4SAndy Fiddaman rxq->vrq_running = false;
634*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
635*3a82bea4SAndy Fiddaman }
636*3a82bea4SAndy Fiddaman
637*3a82bea4SAndy Fiddaman static mblk_t *
vioif_rx_ring_poll(void * arg,int nbytes)638*3a82bea4SAndy Fiddaman vioif_rx_ring_poll(void *arg, int nbytes)
639*3a82bea4SAndy Fiddaman {
640*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = arg;
641*3a82bea4SAndy Fiddaman mblk_t *mp;
642*3a82bea4SAndy Fiddaman
643*3a82bea4SAndy Fiddaman if (nbytes <= 0)
644*3a82bea4SAndy Fiddaman return (NULL);
645*3a82bea4SAndy Fiddaman
646*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
647*3a82bea4SAndy Fiddaman mp = vioif_ring_rx(rxq, nbytes);
648*3a82bea4SAndy Fiddaman
649*3a82bea4SAndy Fiddaman /*
650*3a82bea4SAndy Fiddaman * Attempt to replenish the receive queue, as the interrupt path
651*3a82bea4SAndy Fiddaman * does. If we cannot add any descriptors here, it may be because
652*3a82bea4SAndy Fiddaman * all of the recently received packets were loaned up to the
653*3a82bea4SAndy Fiddaman * networking stack.
654*3a82bea4SAndy Fiddaman */
655*3a82bea4SAndy Fiddaman (void) vioif_add_rx(rxq);
656*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
657*3a82bea4SAndy Fiddaman
658*3a82bea4SAndy Fiddaman return (mp);
659*3a82bea4SAndy Fiddaman }
660*3a82bea4SAndy Fiddaman
661*3a82bea4SAndy Fiddaman static int
vioif_rx_ring_intr_enable(mac_intr_handle_t ih)662*3a82bea4SAndy Fiddaman vioif_rx_ring_intr_enable(mac_intr_handle_t ih)
663*3a82bea4SAndy Fiddaman {
664*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = (vioif_rxq_t *)ih;
665*3a82bea4SAndy Fiddaman
666*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
667*3a82bea4SAndy Fiddaman rxq->vrq_polling = false;
668*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
669*3a82bea4SAndy Fiddaman
670*3a82bea4SAndy Fiddaman virtio_queue_no_interrupt(rxq->vrq_vq, B_FALSE);
671*3a82bea4SAndy Fiddaman
672*3a82bea4SAndy Fiddaman /*
673*3a82bea4SAndy Fiddaman * A frame that the device returned after the final poll, but before
674*3a82bea4SAndy Fiddaman * interrupts were re-enabled above, will never raise one. Collect it
675*3a82bea4SAndy Fiddaman * now.
676*3a82bea4SAndy Fiddaman */
677*3a82bea4SAndy Fiddaman vioif_rx_dispatch_collect(rxq);
678*3a82bea4SAndy Fiddaman
679*3a82bea4SAndy Fiddaman return (0);
680*3a82bea4SAndy Fiddaman }
681*3a82bea4SAndy Fiddaman
682*3a82bea4SAndy Fiddaman static int
vioif_rx_ring_intr_disable(mac_intr_handle_t ih)683*3a82bea4SAndy Fiddaman vioif_rx_ring_intr_disable(mac_intr_handle_t ih)
684*3a82bea4SAndy Fiddaman {
685*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = (vioif_rxq_t *)ih;
686*3a82bea4SAndy Fiddaman
687*3a82bea4SAndy Fiddaman /*
688*3a82bea4SAndy Fiddaman * Virtqueue interrupt suppression is only advisory, so the device may
689*3a82bea4SAndy Fiddaman * still deliver an interrupt while MAC is polling the ring. The
690*3a82bea4SAndy Fiddaman * "vrq_polling" flag stops the interrupt handler from collecting
691*3a82bea4SAndy Fiddaman * frames while the flag is set.
692*3a82bea4SAndy Fiddaman */
693*3a82bea4SAndy Fiddaman mutex_enter(&rxq->vrq_mutex);
694*3a82bea4SAndy Fiddaman rxq->vrq_polling = true;
695*3a82bea4SAndy Fiddaman mutex_exit(&rxq->vrq_mutex);
696*3a82bea4SAndy Fiddaman
697*3a82bea4SAndy Fiddaman virtio_queue_no_interrupt(rxq->vrq_vq, B_TRUE);
698*3a82bea4SAndy Fiddaman
699*3a82bea4SAndy Fiddaman return (0);
700*3a82bea4SAndy Fiddaman }
701*3a82bea4SAndy Fiddaman
702*3a82bea4SAndy Fiddaman static int
vioif_rx_ring_stat(mac_ring_driver_t rh,uint_t stat,uint64_t * val)703*3a82bea4SAndy Fiddaman vioif_rx_ring_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val)
704*3a82bea4SAndy Fiddaman {
705*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq = (vioif_rxq_t *)rh;
706*3a82bea4SAndy Fiddaman
707*3a82bea4SAndy Fiddaman switch (stat) {
708*3a82bea4SAndy Fiddaman case MAC_STAT_RBYTES:
709*3a82bea4SAndy Fiddaman *val = rxq->vrq_rbytes;
710*3a82bea4SAndy Fiddaman break;
711*3a82bea4SAndy Fiddaman case MAC_STAT_IPACKETS:
712*3a82bea4SAndy Fiddaman *val = rxq->vrq_ipackets;
713*3a82bea4SAndy Fiddaman break;
714*3a82bea4SAndy Fiddaman default:
715*3a82bea4SAndy Fiddaman *val = 0;
716*3a82bea4SAndy Fiddaman return (ENOTSUP);
717*3a82bea4SAndy Fiddaman }
718*3a82bea4SAndy Fiddaman
719*3a82bea4SAndy Fiddaman return (0);
720*3a82bea4SAndy Fiddaman }
721*3a82bea4SAndy Fiddaman
722*3a82bea4SAndy Fiddaman void
vioif_fill_rx_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)723*3a82bea4SAndy Fiddaman vioif_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
724*3a82bea4SAndy Fiddaman const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
725*3a82bea4SAndy Fiddaman {
726*3a82bea4SAndy Fiddaman vioif_t *vif = arg;
727*3a82bea4SAndy Fiddaman vioif_rxq_t *rxq;
728*3a82bea4SAndy Fiddaman mac_intr_t *mintr = &infop->mri_intr;
729*3a82bea4SAndy Fiddaman
730*3a82bea4SAndy Fiddaman VERIFY3S(rtype, ==, MAC_RING_TYPE_RX);
731*3a82bea4SAndy Fiddaman VERIFY3S(group_index, ==, 0);
732*3a82bea4SAndy Fiddaman VERIFY3S(ring_index, >=, 0);
733*3a82bea4SAndy Fiddaman VERIFY3U(ring_index, <, vif->vif_nqpairs);
734*3a82bea4SAndy Fiddaman
735*3a82bea4SAndy Fiddaman rxq = &vif->vif_rxqs[ring_index];
736*3a82bea4SAndy Fiddaman rxq->vrq_ringh = rh;
737*3a82bea4SAndy Fiddaman
738*3a82bea4SAndy Fiddaman infop->mri_driver = (mac_ring_driver_t)rxq;
739*3a82bea4SAndy Fiddaman infop->mri_start = vioif_rx_ring_start;
740*3a82bea4SAndy Fiddaman infop->mri_stop = vioif_rx_ring_stop;
741*3a82bea4SAndy Fiddaman infop->mri_poll = vioif_rx_ring_poll;
742*3a82bea4SAndy Fiddaman infop->mri_stat = vioif_rx_ring_stat;
743*3a82bea4SAndy Fiddaman
744*3a82bea4SAndy Fiddaman mintr->mi_handle = (mac_intr_handle_t)rxq;
745*3a82bea4SAndy Fiddaman mintr->mi_enable = vioif_rx_ring_intr_enable;
746*3a82bea4SAndy Fiddaman mintr->mi_disable = vioif_rx_ring_intr_disable;
747*3a82bea4SAndy Fiddaman
748*3a82bea4SAndy Fiddaman /*
749*3a82bea4SAndy Fiddaman * Provide the interrupt handle for the MSI-X vector servicing this
750*3a82bea4SAndy Fiddaman * queue, if it has one, so that MAC can retarget the interrupt to
751*3a82bea4SAndy Fiddaman * the CPU associated with the ring.
752*3a82bea4SAndy Fiddaman */
753*3a82bea4SAndy Fiddaman mintr->mi_ddi_handle = virtio_queue_intr_handle(rxq->vrq_vq);
754*3a82bea4SAndy Fiddaman }
755