xref: /illumos-gate/usr/src/uts/intel/io/viona/viona_rx.c (revision d4221574)
1b22a70abSPatrick Mooney /*
2b22a70abSPatrick Mooney  * Copyright (c) 2013  Chris Torek <torek @ torek net>
3b22a70abSPatrick Mooney  * All rights reserved.
4b22a70abSPatrick Mooney  *
5b22a70abSPatrick Mooney  * Redistribution and use in source and binary forms, with or without
6b22a70abSPatrick Mooney  * modification, are permitted provided that the following conditions
7b22a70abSPatrick Mooney  * are met:
8b22a70abSPatrick Mooney  * 1. Redistributions of source code must retain the above copyright
9b22a70abSPatrick Mooney  *    notice, this list of conditions and the following disclaimer.
10b22a70abSPatrick Mooney  * 2. Redistributions in binary form must reproduce the above copyright
11b22a70abSPatrick Mooney  *    notice, this list of conditions and the following disclaimer in the
12b22a70abSPatrick Mooney  *    documentation and/or other materials provided with the distribution.
13b22a70abSPatrick Mooney  *
14b22a70abSPatrick Mooney  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15b22a70abSPatrick Mooney  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16b22a70abSPatrick Mooney  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17b22a70abSPatrick Mooney  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18b22a70abSPatrick Mooney  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19b22a70abSPatrick Mooney  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20b22a70abSPatrick Mooney  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21b22a70abSPatrick Mooney  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22b22a70abSPatrick Mooney  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23b22a70abSPatrick Mooney  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24b22a70abSPatrick Mooney  * SUCH DAMAGE.
25b22a70abSPatrick Mooney  */
26b22a70abSPatrick Mooney /*
27b22a70abSPatrick Mooney  * This file and its contents are supplied under the terms of the
28b22a70abSPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
29b22a70abSPatrick Mooney  * You may only use this file in accordance with the terms of version
30b22a70abSPatrick Mooney  * 1.0 of the CDDL.
31b22a70abSPatrick Mooney  *
32b22a70abSPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
33b22a70abSPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
34b22a70abSPatrick Mooney  * http://www.illumos.org/license/CDDL.
35b22a70abSPatrick Mooney  *
36b22a70abSPatrick Mooney  * Copyright 2015 Pluribus Networks Inc.
37b22a70abSPatrick Mooney  * Copyright 2019 Joyent, Inc.
38a26f9c14SPatrick Mooney  * Copyright 2022 Oxide Computer Company
392c972cd3SMike Zeller  * Copyright 2022 Michael Zeller
40*d4221574SAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
41b22a70abSPatrick Mooney  */
42b22a70abSPatrick Mooney 
43b22a70abSPatrick Mooney #include <sys/types.h>
44b22a70abSPatrick Mooney #include <sys/strsubr.h>
45b22a70abSPatrick Mooney 
46b22a70abSPatrick Mooney #include <sys/dlpi.h>
47b22a70abSPatrick Mooney #include <sys/pattr.h>
48b22a70abSPatrick Mooney #include <sys/vlan.h>
49b22a70abSPatrick Mooney 
50b22a70abSPatrick Mooney #include "viona_impl.h"
51b22a70abSPatrick Mooney 
52b22a70abSPatrick Mooney 
53b22a70abSPatrick Mooney 
54b22a70abSPatrick Mooney #define	VTNET_MAXSEGS		32
55b22a70abSPatrick Mooney 
56b22a70abSPatrick Mooney /* Min. octets in an ethernet frame minus FCS */
57b22a70abSPatrick Mooney #define	MIN_BUF_SIZE		60
58b22a70abSPatrick Mooney #define	NEED_VLAN_PAD_SIZE	(MIN_BUF_SIZE - VLAN_TAGSZ)
59b22a70abSPatrick Mooney 
60b22a70abSPatrick Mooney static mblk_t *viona_vlan_pad_mp;
61b22a70abSPatrick Mooney 
62b22a70abSPatrick Mooney void
viona_rx_init(void)63b22a70abSPatrick Mooney viona_rx_init(void)
64b22a70abSPatrick Mooney {
65b22a70abSPatrick Mooney 	mblk_t *mp;
66b22a70abSPatrick Mooney 
67b22a70abSPatrick Mooney 	ASSERT(viona_vlan_pad_mp == NULL);
68b22a70abSPatrick Mooney 
69b22a70abSPatrick Mooney 	/* Create mblk for padding when VLAN tags are stripped */
70b22a70abSPatrick Mooney 	mp = allocb_wait(VLAN_TAGSZ, BPRI_HI, STR_NOSIG, NULL);
71b22a70abSPatrick Mooney 	bzero(mp->b_rptr, VLAN_TAGSZ);
72b22a70abSPatrick Mooney 	mp->b_wptr += VLAN_TAGSZ;
73b22a70abSPatrick Mooney 	viona_vlan_pad_mp = mp;
74b22a70abSPatrick Mooney }
75b22a70abSPatrick Mooney 
76b22a70abSPatrick Mooney void
viona_rx_fini(void)77b22a70abSPatrick Mooney viona_rx_fini(void)
78b22a70abSPatrick Mooney {
79b22a70abSPatrick Mooney 	mblk_t *mp;
80b22a70abSPatrick Mooney 
81b22a70abSPatrick Mooney 	/* Clean up the VLAN padding mblk */
82b22a70abSPatrick Mooney 	mp = viona_vlan_pad_mp;
83b22a70abSPatrick Mooney 	viona_vlan_pad_mp = NULL;
84b22a70abSPatrick Mooney 	VERIFY(mp != NULL && mp->b_cont == NULL);
85b22a70abSPatrick Mooney 	freemsg(mp);
86b22a70abSPatrick Mooney }
87b22a70abSPatrick Mooney 
88b22a70abSPatrick Mooney void
viona_worker_rx(viona_vring_t * ring,viona_link_t * link)89b22a70abSPatrick Mooney viona_worker_rx(viona_vring_t *ring, viona_link_t *link)
90b22a70abSPatrick Mooney {
91b22a70abSPatrick Mooney 	(void) thread_vsetname(curthread, "viona_rx_%p", ring);
92b22a70abSPatrick Mooney 
93b22a70abSPatrick Mooney 	ASSERT(MUTEX_HELD(&ring->vr_lock));
94b22a70abSPatrick Mooney 	ASSERT3U(ring->vr_state, ==, VRS_RUN);
95b22a70abSPatrick Mooney 
96427f9b9aSPatrick Mooney 	viona_ring_disable_notify(ring);
97b22a70abSPatrick Mooney 
98b22a70abSPatrick Mooney 	do {
99b22a70abSPatrick Mooney 		if (vmm_drv_lease_expired(ring->vr_lease)) {
100b22a70abSPatrick Mooney 			/*
101b22a70abSPatrick Mooney 			 * Set the renewal flag, causing incoming traffic to be
102b22a70abSPatrick Mooney 			 * dropped, and issue an RX barrier to ensure any
103b22a70abSPatrick Mooney 			 * threads in the RX callbacks will have finished.
104b22a70abSPatrick Mooney 			 * The vr_lock cannot be held across the barrier as it
105b22a70abSPatrick Mooney 			 * poses a deadlock risk.
106b22a70abSPatrick Mooney 			 */
107b22a70abSPatrick Mooney 			ring->vr_state_flags |= VRSF_RENEW;
108b22a70abSPatrick Mooney 			mutex_exit(&ring->vr_lock);
109b22a70abSPatrick Mooney 			mac_rx_barrier(link->l_mch);
110b22a70abSPatrick Mooney 			mutex_enter(&ring->vr_lock);
111b22a70abSPatrick Mooney 
112b22a70abSPatrick Mooney 			if (!viona_ring_lease_renew(ring)) {
113b22a70abSPatrick Mooney 				break;
114b22a70abSPatrick Mooney 			}
115b22a70abSPatrick Mooney 			ring->vr_state_flags &= ~VRSF_RENEW;
116b22a70abSPatrick Mooney 		}
117b22a70abSPatrick Mooney 
118b22a70abSPatrick Mooney 		/*
119b22a70abSPatrick Mooney 		 * For now, there is little to do in the RX worker as inbound
120b22a70abSPatrick Mooney 		 * data is delivered by MAC via the RX callbacks.  If tap-like
121b22a70abSPatrick Mooney 		 * functionality is added later, this would be a convenient
122b22a70abSPatrick Mooney 		 * place to inject frames into the guest.
123b22a70abSPatrick Mooney 		 */
124b22a70abSPatrick Mooney 		(void) cv_wait_sig(&ring->vr_cv, &ring->vr_lock);
125a26f9c14SPatrick Mooney 	} while (!vring_need_bail(ring));
126b22a70abSPatrick Mooney 
127b22a70abSPatrick Mooney 	ring->vr_state = VRS_STOP;
128b22a70abSPatrick Mooney 
129b22a70abSPatrick Mooney 	/*
130b22a70abSPatrick Mooney 	 * The RX ring is stopping, before we start tearing it down it
131b22a70abSPatrick Mooney 	 * is imperative that we perform an RX barrier so that
132b22a70abSPatrick Mooney 	 * incoming packets are dropped at viona_rx_classified().
133b22a70abSPatrick Mooney 	 */
134b22a70abSPatrick Mooney 	mutex_exit(&ring->vr_lock);
135b22a70abSPatrick Mooney 	mac_rx_barrier(link->l_mch);
136b22a70abSPatrick Mooney 	mutex_enter(&ring->vr_lock);
137b22a70abSPatrick Mooney 
1382c972cd3SMike Zeller 	/*
1392c972cd3SMike Zeller 	 * If we bailed while renewing the ring lease, we cannot reset
1402c972cd3SMike Zeller 	 * USED_NO_NOTIFY, since we lack a valid mapping to do so.
1412c972cd3SMike Zeller 	 */
1422c972cd3SMike Zeller 	if (ring->vr_lease != NULL) {
1432c972cd3SMike Zeller 		viona_ring_enable_notify(ring);
1442c972cd3SMike Zeller 	}
145b22a70abSPatrick Mooney }
146b22a70abSPatrick Mooney 
147b22a70abSPatrick Mooney static size_t
viona_copy_mblk(const mblk_t * mp,size_t seek,caddr_t buf,size_t len,boolean_t * end)148b22a70abSPatrick Mooney viona_copy_mblk(const mblk_t *mp, size_t seek, caddr_t buf, size_t len,
149b22a70abSPatrick Mooney     boolean_t *end)
150b22a70abSPatrick Mooney {
151b22a70abSPatrick Mooney 	size_t copied = 0;
152b22a70abSPatrick Mooney 	size_t off = 0;
153b22a70abSPatrick Mooney 
154b22a70abSPatrick Mooney 	/* Seek past already-consumed data */
155b22a70abSPatrick Mooney 	while (seek > 0 && mp != NULL) {
156b22a70abSPatrick Mooney 		const size_t chunk = MBLKL(mp);
157b22a70abSPatrick Mooney 
158b22a70abSPatrick Mooney 		if (chunk > seek) {
159b22a70abSPatrick Mooney 			off = seek;
160b22a70abSPatrick Mooney 			break;
161b22a70abSPatrick Mooney 		}
162b22a70abSPatrick Mooney 		mp = mp->b_cont;
163b22a70abSPatrick Mooney 		seek -= chunk;
164b22a70abSPatrick Mooney 	}
165b22a70abSPatrick Mooney 
166b22a70abSPatrick Mooney 	while (mp != NULL) {
167b22a70abSPatrick Mooney 		const size_t chunk = MBLKL(mp) - off;
168b22a70abSPatrick Mooney 		const size_t to_copy = MIN(chunk, len);
169b22a70abSPatrick Mooney 
170b22a70abSPatrick Mooney 		bcopy(mp->b_rptr + off, buf, to_copy);
171b22a70abSPatrick Mooney 		copied += to_copy;
172b22a70abSPatrick Mooney 		buf += to_copy;
173b22a70abSPatrick Mooney 		len -= to_copy;
174b22a70abSPatrick Mooney 
175b22a70abSPatrick Mooney 		/*
176b22a70abSPatrick Mooney 		 * If all the remaining data in the mblk_t was copied, move on
177b22a70abSPatrick Mooney 		 * to the next one in the chain.  Any seek offset applied to
178b22a70abSPatrick Mooney 		 * the first mblk copy is zeroed out for subsequent operations.
179b22a70abSPatrick Mooney 		 */
180b22a70abSPatrick Mooney 		if (chunk == to_copy) {
181b22a70abSPatrick Mooney 			mp = mp->b_cont;
182b22a70abSPatrick Mooney 			off = 0;
183b22a70abSPatrick Mooney 		}
184b22a70abSPatrick Mooney #ifdef DEBUG
185b22a70abSPatrick Mooney 		else {
186b22a70abSPatrick Mooney 			/*
187b22a70abSPatrick Mooney 			 * The only valid reason for the copy to consume less
188b22a70abSPatrick Mooney 			 * than the entire contents of the mblk_t is because
189b22a70abSPatrick Mooney 			 * the output buffer has been filled.
190b22a70abSPatrick Mooney 			 */
191b22a70abSPatrick Mooney 			ASSERT0(len);
192b22a70abSPatrick Mooney 		}
193b22a70abSPatrick Mooney #endif
194b22a70abSPatrick Mooney 
195b22a70abSPatrick Mooney 		/* Go no further if the buffer has been filled */
196b22a70abSPatrick Mooney 		if (len == 0) {
197b22a70abSPatrick Mooney 			break;
198b22a70abSPatrick Mooney 		}
199b22a70abSPatrick Mooney 
200b22a70abSPatrick Mooney 	}
201b22a70abSPatrick Mooney 	*end = (mp == NULL);
202b22a70abSPatrick Mooney 	return (copied);
203b22a70abSPatrick Mooney }
204b22a70abSPatrick Mooney 
205b22a70abSPatrick Mooney static int
viona_recv_plain(viona_vring_t * ring,const mblk_t * mp,size_t msz)206b22a70abSPatrick Mooney viona_recv_plain(viona_vring_t *ring, const mblk_t *mp, size_t msz)
207b22a70abSPatrick Mooney {
208b22a70abSPatrick Mooney 	struct iovec iov[VTNET_MAXSEGS];
209b22a70abSPatrick Mooney 	uint16_t cookie;
210b22a70abSPatrick Mooney 	int n;
211b22a70abSPatrick Mooney 	const size_t hdr_sz = sizeof (struct virtio_net_hdr);
212b22a70abSPatrick Mooney 	struct virtio_net_hdr *hdr;
213b22a70abSPatrick Mooney 	size_t len, copied = 0;
214b22a70abSPatrick Mooney 	caddr_t buf = NULL;
215b22a70abSPatrick Mooney 	boolean_t end = B_FALSE;
216b22a70abSPatrick Mooney 	const uint32_t features = ring->vr_link->l_features;
217db9aa506SPatrick Mooney 	vmm_page_t *pages = NULL;
218b22a70abSPatrick Mooney 
219b22a70abSPatrick Mooney 	ASSERT(msz >= MIN_BUF_SIZE);
220b22a70abSPatrick Mooney 
221db9aa506SPatrick Mooney 	n = vq_popchain(ring, iov, VTNET_MAXSEGS, &cookie, &pages);
222b22a70abSPatrick Mooney 	if (n <= 0) {
223b22a70abSPatrick Mooney 		/* Without available buffers, the frame must be dropped. */
224b22a70abSPatrick Mooney 		return (ENOSPC);
225b22a70abSPatrick Mooney 	}
226b22a70abSPatrick Mooney 	if (iov[0].iov_len < hdr_sz) {
227b22a70abSPatrick Mooney 		/*
228b22a70abSPatrick Mooney 		 * There is little to do if there is not even space available
229b22a70abSPatrick Mooney 		 * for the sole header.  Zero the buffer and bail out as a last
230b22a70abSPatrick Mooney 		 * act of desperation.
231b22a70abSPatrick Mooney 		 */
232b22a70abSPatrick Mooney 		bzero(iov[0].iov_base, iov[0].iov_len);
233b22a70abSPatrick Mooney 		goto bad_frame;
234b22a70abSPatrick Mooney 	}
235b22a70abSPatrick Mooney 
236b22a70abSPatrick Mooney 	/* Grab the address of the header before anything else */
237b22a70abSPatrick Mooney 	hdr = (struct virtio_net_hdr *)iov[0].iov_base;
238b22a70abSPatrick Mooney 
239b22a70abSPatrick Mooney 	/*
240b22a70abSPatrick Mooney 	 * If there is any space remaining in the first buffer after writing
241b22a70abSPatrick Mooney 	 * the header, fill it with frame data.
242b22a70abSPatrick Mooney 	 */
243b22a70abSPatrick Mooney 	if (iov[0].iov_len > hdr_sz) {
244b22a70abSPatrick Mooney 		buf = (caddr_t)iov[0].iov_base + hdr_sz;
245b22a70abSPatrick Mooney 		len = iov[0].iov_len - hdr_sz;
246b22a70abSPatrick Mooney 
247b22a70abSPatrick Mooney 		copied += viona_copy_mblk(mp, copied, buf, len, &end);
248b22a70abSPatrick Mooney 	}
249b22a70abSPatrick Mooney 
250b22a70abSPatrick Mooney 	/* Copy any remaining data into subsequent buffers, if present */
251b22a70abSPatrick Mooney 	for (int i = 1; i < n && !end; i++) {
252b22a70abSPatrick Mooney 		buf = (caddr_t)iov[i].iov_base;
253b22a70abSPatrick Mooney 		len = iov[i].iov_len;
254b22a70abSPatrick Mooney 
255b22a70abSPatrick Mooney 		copied += viona_copy_mblk(mp, copied, buf, len, &end);
256b22a70abSPatrick Mooney 	}
257b22a70abSPatrick Mooney 
258b22a70abSPatrick Mooney 	/* Was the expected amount of data copied? */
259b22a70abSPatrick Mooney 	if (copied != msz) {
260b22a70abSPatrick Mooney 		VIONA_PROBE5(too_short, viona_vring_t *, ring,
261b22a70abSPatrick Mooney 		    uint16_t, cookie, mblk_t *, mp, size_t, copied,
262b22a70abSPatrick Mooney 		    size_t, msz);
263b22a70abSPatrick Mooney 		VIONA_RING_STAT_INCR(ring, too_short);
264b22a70abSPatrick Mooney 		goto bad_frame;
265b22a70abSPatrick Mooney 	}
266b22a70abSPatrick Mooney 
267b22a70abSPatrick Mooney 	/* Populate (read: zero) the header and account for it in the size */
268b22a70abSPatrick Mooney 	bzero(hdr, hdr_sz);
269b22a70abSPatrick Mooney 	copied += hdr_sz;
270b22a70abSPatrick Mooney 
271b22a70abSPatrick Mooney 	/* Add chksum bits, if needed */
272b22a70abSPatrick Mooney 	if ((features & VIRTIO_NET_F_GUEST_CSUM) != 0) {
273b22a70abSPatrick Mooney 		uint32_t cksum_flags;
274b22a70abSPatrick Mooney 
275b22a70abSPatrick Mooney 		if (((features & VIRTIO_NET_F_GUEST_TSO4) != 0) &&
276b22a70abSPatrick Mooney 		    ((DB_CKSUMFLAGS(mp) & HW_LSO) != 0)) {
277b22a70abSPatrick Mooney 			hdr->vrh_gso_type |= VIRTIO_NET_HDR_GSO_TCPV4;
278b22a70abSPatrick Mooney 			hdr->vrh_gso_size = DB_LSOMSS(mp);
279b22a70abSPatrick Mooney 		}
280b22a70abSPatrick Mooney 
281b22a70abSPatrick Mooney 		mac_hcksum_get((mblk_t *)mp, NULL, NULL, NULL, NULL,
282b22a70abSPatrick Mooney 		    &cksum_flags);
283b22a70abSPatrick Mooney 		if ((cksum_flags & HCK_FULLCKSUM_OK) != 0) {
284b22a70abSPatrick Mooney 			hdr->vrh_flags |= VIRTIO_NET_HDR_F_DATA_VALID;
285b22a70abSPatrick Mooney 		}
286b22a70abSPatrick Mooney 	}
287b22a70abSPatrick Mooney 
288b22a70abSPatrick Mooney 	/* Release this chain */
289db9aa506SPatrick Mooney 	vmm_drv_page_release_chain(pages);
290b22a70abSPatrick Mooney 	vq_pushchain(ring, copied, cookie);
291b22a70abSPatrick Mooney 	return (0);
292b22a70abSPatrick Mooney 
293b22a70abSPatrick Mooney bad_frame:
294b22a70abSPatrick Mooney 	VIONA_PROBE3(bad_rx_frame, viona_vring_t *, ring, uint16_t, cookie,
295b22a70abSPatrick Mooney 	    mblk_t *, mp);
296b22a70abSPatrick Mooney 	VIONA_RING_STAT_INCR(ring, bad_rx_frame);
297b22a70abSPatrick Mooney 
298db9aa506SPatrick Mooney 	vmm_drv_page_release_chain(pages);
299b22a70abSPatrick Mooney 	vq_pushchain(ring, MAX(copied, MIN_BUF_SIZE + hdr_sz), cookie);
300b22a70abSPatrick Mooney 	return (EINVAL);
301b22a70abSPatrick Mooney }
302b22a70abSPatrick Mooney 
303b22a70abSPatrick Mooney static int
viona_recv_merged(viona_vring_t * ring,const mblk_t * mp,size_t msz)304b22a70abSPatrick Mooney viona_recv_merged(viona_vring_t *ring, const mblk_t *mp, size_t msz)
305b22a70abSPatrick Mooney {
306b22a70abSPatrick Mooney 	struct iovec iov[VTNET_MAXSEGS];
307b22a70abSPatrick Mooney 	used_elem_t uelem[VTNET_MAXSEGS];
308db9aa506SPatrick Mooney 	vmm_page_t *pages = NULL, *hdr_pages = NULL;
309b22a70abSPatrick Mooney 	int n, i = 0, buf_idx = 0, err = 0;
310b22a70abSPatrick Mooney 	uint16_t cookie;
311b22a70abSPatrick Mooney 	caddr_t buf;
312b22a70abSPatrick Mooney 	size_t len, copied = 0, chunk = 0;
313b22a70abSPatrick Mooney 	struct virtio_net_mrgrxhdr *hdr = NULL;
314b22a70abSPatrick Mooney 	const size_t hdr_sz = sizeof (struct virtio_net_mrgrxhdr);
315b22a70abSPatrick Mooney 	boolean_t end = B_FALSE;
316b22a70abSPatrick Mooney 	const uint32_t features = ring->vr_link->l_features;
317b22a70abSPatrick Mooney 
318b22a70abSPatrick Mooney 	ASSERT(msz >= MIN_BUF_SIZE);
319b22a70abSPatrick Mooney 
320db9aa506SPatrick Mooney 	n = vq_popchain(ring, iov, VTNET_MAXSEGS, &cookie, &hdr_pages);
321b22a70abSPatrick Mooney 	if (n <= 0) {
322b22a70abSPatrick Mooney 		/* Without available buffers, the frame must be dropped. */
323b22a70abSPatrick Mooney 		VIONA_PROBE2(no_space, viona_vring_t *, ring, mblk_t *, mp);
324b22a70abSPatrick Mooney 		VIONA_RING_STAT_INCR(ring, no_space);
325b22a70abSPatrick Mooney 		return (ENOSPC);
326b22a70abSPatrick Mooney 	}
327b22a70abSPatrick Mooney 	if (iov[0].iov_len < hdr_sz) {
328b22a70abSPatrick Mooney 		/*
329b22a70abSPatrick Mooney 		 * There is little to do if there is not even space available
330b22a70abSPatrick Mooney 		 * for the sole header.  Zero the buffer and bail out as a last
331b22a70abSPatrick Mooney 		 * act of desperation.
332b22a70abSPatrick Mooney 		 */
333b22a70abSPatrick Mooney 		bzero(iov[0].iov_base, iov[0].iov_len);
334b22a70abSPatrick Mooney 		uelem[0].id = cookie;
335b22a70abSPatrick Mooney 		uelem[0].len = iov[0].iov_len;
336b22a70abSPatrick Mooney 		err = EINVAL;
337b22a70abSPatrick Mooney 		goto done;
338b22a70abSPatrick Mooney 	}
339b22a70abSPatrick Mooney 
340b22a70abSPatrick Mooney 	/* Grab the address of the header and do initial population */
341b22a70abSPatrick Mooney 	hdr = (struct virtio_net_mrgrxhdr *)iov[0].iov_base;
342b22a70abSPatrick Mooney 	bzero(hdr, hdr_sz);
343b22a70abSPatrick Mooney 	hdr->vrh_bufs = 1;
344b22a70abSPatrick Mooney 
345b22a70abSPatrick Mooney 	/*
346b22a70abSPatrick Mooney 	 * If there is any space remaining in the first buffer after writing
34749e06792SPatrick Mooney 	 * the header, fill it with frame data.  The size of the header itself
34849e06792SPatrick Mooney 	 * is accounted for later.
349b22a70abSPatrick Mooney 	 */
350b22a70abSPatrick Mooney 	if (iov[0].iov_len > hdr_sz) {
351b22a70abSPatrick Mooney 		buf = iov[0].iov_base + hdr_sz;
352b22a70abSPatrick Mooney 		len = iov[0].iov_len - hdr_sz;
353b22a70abSPatrick Mooney 
35449e06792SPatrick Mooney 		size_t copy_len;
35549e06792SPatrick Mooney 		copy_len = viona_copy_mblk(mp, copied, buf, len, &end);
35649e06792SPatrick Mooney 		chunk += copy_len;
35749e06792SPatrick Mooney 		copied += copy_len;
358b22a70abSPatrick Mooney 	}
359b22a70abSPatrick Mooney 	i = 1;
360b22a70abSPatrick Mooney 
361b22a70abSPatrick Mooney 	do {
362b22a70abSPatrick Mooney 		while (i < n && !end) {
363b22a70abSPatrick Mooney 			buf = iov[i].iov_base;
364b22a70abSPatrick Mooney 			len = iov[i].iov_len;
365b22a70abSPatrick Mooney 
36649e06792SPatrick Mooney 			size_t copy_len;
36749e06792SPatrick Mooney 			copy_len = viona_copy_mblk(mp, copied, buf, len, &end);
36849e06792SPatrick Mooney 			chunk += copy_len;
36949e06792SPatrick Mooney 			copied += copy_len;
370b22a70abSPatrick Mooney 			i++;
371b22a70abSPatrick Mooney 		}
372b22a70abSPatrick Mooney 
373b22a70abSPatrick Mooney 		uelem[buf_idx].id = cookie;
374b22a70abSPatrick Mooney 		uelem[buf_idx].len = chunk;
375b22a70abSPatrick Mooney 
376b22a70abSPatrick Mooney 		/*
377b22a70abSPatrick Mooney 		 * Try to grab another buffer from the ring if the mblk has not
378b22a70abSPatrick Mooney 		 * yet been entirely copied out.
379b22a70abSPatrick Mooney 		 */
380b22a70abSPatrick Mooney 		if (!end) {
381b22a70abSPatrick Mooney 			if (buf_idx == (VTNET_MAXSEGS - 1)) {
382b22a70abSPatrick Mooney 				/*
383b22a70abSPatrick Mooney 				 * Our arbitrary limit on the number of buffers
384b22a70abSPatrick Mooney 				 * to offer for merge has already been reached.
385b22a70abSPatrick Mooney 				 */
386b22a70abSPatrick Mooney 				err = EOVERFLOW;
387b22a70abSPatrick Mooney 				break;
388b22a70abSPatrick Mooney 			}
389db9aa506SPatrick Mooney 			if (pages != NULL) {
390db9aa506SPatrick Mooney 				vmm_drv_page_release_chain(pages);
391db9aa506SPatrick Mooney 				pages = NULL;
392db9aa506SPatrick Mooney 			}
393db9aa506SPatrick Mooney 			n = vq_popchain(ring, iov, VTNET_MAXSEGS, &cookie,
394db9aa506SPatrick Mooney 			    &pages);
395b22a70abSPatrick Mooney 			if (n <= 0) {
396b22a70abSPatrick Mooney 				/*
397b22a70abSPatrick Mooney 				 * Without more immediate space to perform the
398b22a70abSPatrick Mooney 				 * copying, there is little choice left but to
399b22a70abSPatrick Mooney 				 * drop the packet.
400b22a70abSPatrick Mooney 				 */
401b22a70abSPatrick Mooney 				err = EMSGSIZE;
402b22a70abSPatrick Mooney 				break;
403b22a70abSPatrick Mooney 			}
404b22a70abSPatrick Mooney 			chunk = 0;
405b22a70abSPatrick Mooney 			i = 0;
406b22a70abSPatrick Mooney 			buf_idx++;
407b22a70abSPatrick Mooney 			/*
408b22a70abSPatrick Mooney 			 * Keep the header up-to-date with the number of
409b22a70abSPatrick Mooney 			 * buffers, but never reference its value since the
410b22a70abSPatrick Mooney 			 * guest could meddle with it.
411b22a70abSPatrick Mooney 			 */
412b22a70abSPatrick Mooney 			hdr->vrh_bufs++;
413b22a70abSPatrick Mooney 		}
414b22a70abSPatrick Mooney 	} while (!end && copied < msz);
415b22a70abSPatrick Mooney 
416b22a70abSPatrick Mooney 	/* Account for the header size in the first buffer */
417b22a70abSPatrick Mooney 	uelem[0].len += hdr_sz;
418b22a70abSPatrick Mooney 
419b22a70abSPatrick Mooney 	/*
420b22a70abSPatrick Mooney 	 * If no other errors were encounted during the copy, was the expected
421*d4221574SAndy Fiddaman 	 * amount of data transferred?
422b22a70abSPatrick Mooney 	 */
423b22a70abSPatrick Mooney 	if (err == 0 && copied != msz) {
424b22a70abSPatrick Mooney 		VIONA_PROBE5(too_short, viona_vring_t *, ring,
425b22a70abSPatrick Mooney 		    uint16_t, cookie, mblk_t *, mp, size_t, copied,
426b22a70abSPatrick Mooney 		    size_t, msz);
427b22a70abSPatrick Mooney 		VIONA_RING_STAT_INCR(ring, too_short);
428b22a70abSPatrick Mooney 		err = EINVAL;
429b22a70abSPatrick Mooney 	}
430b22a70abSPatrick Mooney 
431b22a70abSPatrick Mooney 	/* Add chksum bits, if needed */
432b22a70abSPatrick Mooney 	if ((features & VIRTIO_NET_F_GUEST_CSUM) != 0) {
433b22a70abSPatrick Mooney 		uint32_t cksum_flags;
434b22a70abSPatrick Mooney 
435b22a70abSPatrick Mooney 		if (((features & VIRTIO_NET_F_GUEST_TSO4) != 0) &&
436b22a70abSPatrick Mooney 		    ((DB_CKSUMFLAGS(mp) & HW_LSO) != 0)) {
437b22a70abSPatrick Mooney 			hdr->vrh_gso_type |= VIRTIO_NET_HDR_GSO_TCPV4;
438b22a70abSPatrick Mooney 			hdr->vrh_gso_size = DB_LSOMSS(mp);
439b22a70abSPatrick Mooney 		}
440b22a70abSPatrick Mooney 
441b22a70abSPatrick Mooney 		mac_hcksum_get((mblk_t *)mp, NULL, NULL, NULL, NULL,
442b22a70abSPatrick Mooney 		    &cksum_flags);
443b22a70abSPatrick Mooney 		if ((cksum_flags & HCK_FULLCKSUM_OK) != 0) {
444b22a70abSPatrick Mooney 			hdr->vrh_flags |= VIRTIO_NET_HDR_F_DATA_VALID;
445b22a70abSPatrick Mooney 		}
446b22a70abSPatrick Mooney 	}
447b22a70abSPatrick Mooney 
448b22a70abSPatrick Mooney done:
449b22a70abSPatrick Mooney 	switch (err) {
450b22a70abSPatrick Mooney 	case 0:
451b22a70abSPatrick Mooney 		/* Success can fall right through to ring delivery */
452b22a70abSPatrick Mooney 		break;
453b22a70abSPatrick Mooney 
454b22a70abSPatrick Mooney 	case EMSGSIZE:
455b22a70abSPatrick Mooney 		VIONA_PROBE3(rx_merge_underrun, viona_vring_t *, ring,
456b22a70abSPatrick Mooney 		    uint16_t, cookie, mblk_t *, mp);
457b22a70abSPatrick Mooney 		VIONA_RING_STAT_INCR(ring, rx_merge_underrun);
458b22a70abSPatrick Mooney 		break;
459b22a70abSPatrick Mooney 
460b22a70abSPatrick Mooney 	case EOVERFLOW:
461b22a70abSPatrick Mooney 		VIONA_PROBE3(rx_merge_overrun, viona_vring_t *, ring,
462b22a70abSPatrick Mooney 		    uint16_t, cookie, mblk_t *, mp);
463b22a70abSPatrick Mooney 		VIONA_RING_STAT_INCR(ring, rx_merge_overrun);
464b22a70abSPatrick Mooney 		break;
465b22a70abSPatrick Mooney 
466b22a70abSPatrick Mooney 	default:
467b22a70abSPatrick Mooney 		VIONA_PROBE3(bad_rx_frame, viona_vring_t *, ring,
468b22a70abSPatrick Mooney 		    uint16_t, cookie, mblk_t *, mp);
469b22a70abSPatrick Mooney 		VIONA_RING_STAT_INCR(ring, bad_rx_frame);
470b22a70abSPatrick Mooney 	}
471db9aa506SPatrick Mooney 
472db9aa506SPatrick Mooney 	if (hdr_pages != NULL) {
473db9aa506SPatrick Mooney 		vmm_drv_page_release_chain(hdr_pages);
474db9aa506SPatrick Mooney 	}
475db9aa506SPatrick Mooney 	if (pages != NULL) {
476db9aa506SPatrick Mooney 		vmm_drv_page_release_chain(pages);
477db9aa506SPatrick Mooney 	}
478b22a70abSPatrick Mooney 	vq_pushchain_many(ring, buf_idx + 1, uelem);
479b22a70abSPatrick Mooney 	return (err);
480b22a70abSPatrick Mooney }
481b22a70abSPatrick Mooney 
482b22a70abSPatrick Mooney static void
viona_rx_common(viona_vring_t * ring,mblk_t * mp,boolean_t is_loopback)483b22a70abSPatrick Mooney viona_rx_common(viona_vring_t *ring, mblk_t *mp, boolean_t is_loopback)
484b22a70abSPatrick Mooney {
485b22a70abSPatrick Mooney 	viona_link_t *link = ring->vr_link;
486b22a70abSPatrick Mooney 	mblk_t *mprx = NULL, **mprx_prevp = &mprx;
487b22a70abSPatrick Mooney 	mblk_t *mpdrop = NULL, **mpdrop_prevp = &mpdrop;
488b22a70abSPatrick Mooney 	const boolean_t do_merge =
489b22a70abSPatrick Mooney 	    ((link->l_features & VIRTIO_NET_F_MRG_RXBUF) != 0);
490b22a70abSPatrick Mooney 
491b22a70abSPatrick Mooney 	size_t nrx = 0, ndrop = 0;
492b22a70abSPatrick Mooney 
493b22a70abSPatrick Mooney 	while (mp != NULL) {
494b22a70abSPatrick Mooney 		mblk_t *next = mp->b_next;
495b22a70abSPatrick Mooney 		mblk_t *pad = NULL;
496b22a70abSPatrick Mooney 		size_t size = msgsize(mp);
497b22a70abSPatrick Mooney 		int err = 0;
498b22a70abSPatrick Mooney 
499b22a70abSPatrick Mooney 		mp->b_next = NULL;
500b22a70abSPatrick Mooney 
501b22a70abSPatrick Mooney 		/*
502b22a70abSPatrick Mooney 		 * We treat both a 'drop' response and errors the same here
503b22a70abSPatrick Mooney 		 * and put the packet on the drop chain.  As packets may be
504b22a70abSPatrick Mooney 		 * subject to different actions in ipf (which do not all
505b22a70abSPatrick Mooney 		 * return the same set of error values), an error processing
506b22a70abSPatrick Mooney 		 * one packet doesn't mean the next packet will also generate
507b22a70abSPatrick Mooney 		 * an error.
508b22a70abSPatrick Mooney 		 */
509b22a70abSPatrick Mooney 		if (VNETHOOK_INTERESTED_IN(link->l_neti) &&
510b22a70abSPatrick Mooney 		    viona_hook(link, ring, &mp, B_FALSE) != 0) {
511b22a70abSPatrick Mooney 			if (mp != NULL) {
512b22a70abSPatrick Mooney 				*mpdrop_prevp = mp;
513b22a70abSPatrick Mooney 				mpdrop_prevp = &mp->b_next;
514b22a70abSPatrick Mooney 			} else {
515b22a70abSPatrick Mooney 				/*
516b22a70abSPatrick Mooney 				 * If the hook consumer (e.g. ipf) already
517b22a70abSPatrick Mooney 				 * freed the mblk_t, update the drop count now.
518b22a70abSPatrick Mooney 				 */
519b22a70abSPatrick Mooney 				ndrop++;
520b22a70abSPatrick Mooney 			}
521b22a70abSPatrick Mooney 			mp = next;
522b22a70abSPatrick Mooney 			continue;
523b22a70abSPatrick Mooney 		}
524b22a70abSPatrick Mooney 
525b22a70abSPatrick Mooney 		/*
526b22a70abSPatrick Mooney 		 * Ethernet frames are expected to be padded out in order to
527b22a70abSPatrick Mooney 		 * meet the minimum size.
528b22a70abSPatrick Mooney 		 *
529b22a70abSPatrick Mooney 		 * A special case is made for frames which are short by
530b22a70abSPatrick Mooney 		 * VLAN_TAGSZ, having been stripped of their VLAN tag while
531b22a70abSPatrick Mooney 		 * traversing MAC.  A preallocated (and recycled) mblk is used
532b22a70abSPatrick Mooney 		 * for that specific condition.
533b22a70abSPatrick Mooney 		 *
534b22a70abSPatrick Mooney 		 * All other frames that fall short on length will have custom
535b22a70abSPatrick Mooney 		 * zero-padding allocated appended to them.
536b22a70abSPatrick Mooney 		 */
537b22a70abSPatrick Mooney 		if (size == NEED_VLAN_PAD_SIZE) {
538b22a70abSPatrick Mooney 			ASSERT(MBLKL(viona_vlan_pad_mp) == VLAN_TAGSZ);
539b22a70abSPatrick Mooney 			ASSERT(viona_vlan_pad_mp->b_cont == NULL);
540b22a70abSPatrick Mooney 
541b22a70abSPatrick Mooney 			for (pad = mp; pad->b_cont != NULL; pad = pad->b_cont)
542b22a70abSPatrick Mooney 				;
543b22a70abSPatrick Mooney 
544b22a70abSPatrick Mooney 			pad->b_cont = viona_vlan_pad_mp;
545b22a70abSPatrick Mooney 			size += VLAN_TAGSZ;
546b22a70abSPatrick Mooney 		} else if (size < MIN_BUF_SIZE) {
547b22a70abSPatrick Mooney 			const size_t pad_size = MIN_BUF_SIZE - size;
548b22a70abSPatrick Mooney 			mblk_t *zero_mp;
549b22a70abSPatrick Mooney 
550b22a70abSPatrick Mooney 			zero_mp = allocb(pad_size, BPRI_MED);
551b22a70abSPatrick Mooney 			if (zero_mp == NULL) {
552b22a70abSPatrick Mooney 				err = ENOMEM;
553b22a70abSPatrick Mooney 				goto pad_drop;
554b22a70abSPatrick Mooney 			}
555b22a70abSPatrick Mooney 
556b22a70abSPatrick Mooney 			VIONA_PROBE3(rx_pad_short, viona_vring_t *, ring,
557b22a70abSPatrick Mooney 			    mblk_t *, mp, size_t, pad_size);
558b22a70abSPatrick Mooney 			VIONA_RING_STAT_INCR(ring, rx_pad_short);
559b22a70abSPatrick Mooney 			zero_mp->b_wptr += pad_size;
560b22a70abSPatrick Mooney 			bzero(zero_mp->b_rptr, pad_size);
561b22a70abSPatrick Mooney 			linkb(mp, zero_mp);
562b22a70abSPatrick Mooney 			size += pad_size;
563b22a70abSPatrick Mooney 		}
564b22a70abSPatrick Mooney 
565b22a70abSPatrick Mooney 		if (do_merge) {
566b22a70abSPatrick Mooney 			err = viona_recv_merged(ring, mp, size);
567b22a70abSPatrick Mooney 		} else {
568b22a70abSPatrick Mooney 			err = viona_recv_plain(ring, mp, size);
569b22a70abSPatrick Mooney 		}
570b22a70abSPatrick Mooney 
571b22a70abSPatrick Mooney 		/*
572b22a70abSPatrick Mooney 		 * The VLAN padding mblk is meant for continual reuse, so
573b22a70abSPatrick Mooney 		 * remove it from the chain to prevent it from being freed.
574b22a70abSPatrick Mooney 		 *
575b22a70abSPatrick Mooney 		 * Custom allocated padding does not require this treatment and
576b22a70abSPatrick Mooney 		 * is freed normally.
577b22a70abSPatrick Mooney 		 */
578b22a70abSPatrick Mooney 		if (pad != NULL) {
579b22a70abSPatrick Mooney 			pad->b_cont = NULL;
580b22a70abSPatrick Mooney 		}
581b22a70abSPatrick Mooney 
582b22a70abSPatrick Mooney pad_drop:
583b22a70abSPatrick Mooney 		/*
584b22a70abSPatrick Mooney 		 * While an error during rx processing
585b22a70abSPatrick Mooney 		 * (viona_recv_{merged,plain}) does not free mp on error,
586b22a70abSPatrick Mooney 		 * hook processing might or might not free mp.  Handle either
587b22a70abSPatrick Mooney 		 * scenario -- if mp is not yet free, it is queued up and
588b22a70abSPatrick Mooney 		 * freed after the guest has been notified.  If mp is
589b22a70abSPatrick Mooney 		 * already NULL, just proceed on.
590b22a70abSPatrick Mooney 		 */
591b22a70abSPatrick Mooney 		if (err != 0) {
592b22a70abSPatrick Mooney 			*mpdrop_prevp = mp;
593b22a70abSPatrick Mooney 			mpdrop_prevp = &mp->b_next;
594b22a70abSPatrick Mooney 
595b22a70abSPatrick Mooney 			/*
596b22a70abSPatrick Mooney 			 * If the available ring is empty, do not bother
597b22a70abSPatrick Mooney 			 * attempting to deliver any more frames.  Count the
598b22a70abSPatrick Mooney 			 * rest as dropped too.
599b22a70abSPatrick Mooney 			 */
600b22a70abSPatrick Mooney 			if (err == ENOSPC) {
601b22a70abSPatrick Mooney 				mp->b_next = next;
602b22a70abSPatrick Mooney 				break;
603b22a70abSPatrick Mooney 			}
604b22a70abSPatrick Mooney 		} else {
605b22a70abSPatrick Mooney 			/* Chain successful mblks to be freed later */
606b22a70abSPatrick Mooney 			*mprx_prevp = mp;
607b22a70abSPatrick Mooney 			mprx_prevp = &mp->b_next;
608b22a70abSPatrick Mooney 			nrx++;
609b22a70abSPatrick Mooney 		}
610b22a70abSPatrick Mooney 		mp = next;
611b22a70abSPatrick Mooney 	}
612b22a70abSPatrick Mooney 
613b22a70abSPatrick Mooney 	membar_enter();
614427f9b9aSPatrick Mooney 	viona_intr_ring(ring, B_FALSE);
615b22a70abSPatrick Mooney 
616b22a70abSPatrick Mooney 	/* Free successfully received frames */
617b22a70abSPatrick Mooney 	if (mprx != NULL) {
618b22a70abSPatrick Mooney 		freemsgchain(mprx);
619b22a70abSPatrick Mooney 	}
620b22a70abSPatrick Mooney 
621b22a70abSPatrick Mooney 	/* Free dropped frames, also tallying them */
622b22a70abSPatrick Mooney 	mp = mpdrop;
623b22a70abSPatrick Mooney 	while (mp != NULL) {
624b22a70abSPatrick Mooney 		mblk_t *next = mp->b_next;
625b22a70abSPatrick Mooney 
626b22a70abSPatrick Mooney 		mp->b_next = NULL;
627b22a70abSPatrick Mooney 		freemsg(mp);
628b22a70abSPatrick Mooney 		mp = next;
629b22a70abSPatrick Mooney 		ndrop++;
630b22a70abSPatrick Mooney 	}
631b22a70abSPatrick Mooney 	VIONA_PROBE3(rx, viona_link_t *, link, size_t, nrx, size_t, ndrop);
632b22a70abSPatrick Mooney }
633b22a70abSPatrick Mooney 
634b22a70abSPatrick Mooney static void
viona_rx_classified(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t is_loopback)635b22a70abSPatrick Mooney viona_rx_classified(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
636b22a70abSPatrick Mooney     boolean_t is_loopback)
637b22a70abSPatrick Mooney {
638b22a70abSPatrick Mooney 	viona_vring_t *ring = (viona_vring_t *)arg;
639b22a70abSPatrick Mooney 
640b22a70abSPatrick Mooney 	/* Drop traffic if ring is inactive or renewing its lease */
641b22a70abSPatrick Mooney 	if (ring->vr_state != VRS_RUN ||
642b22a70abSPatrick Mooney 	    (ring->vr_state_flags & VRSF_RENEW) != 0) {
643b22a70abSPatrick Mooney 		freemsgchain(mp);
644b22a70abSPatrick Mooney 		return;
645b22a70abSPatrick Mooney 	}
646b22a70abSPatrick Mooney 
647b22a70abSPatrick Mooney 	viona_rx_common(ring, mp, is_loopback);
648b22a70abSPatrick Mooney }
649b22a70abSPatrick Mooney 
650b22a70abSPatrick Mooney static void
viona_rx_mcast(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t is_loopback)651b22a70abSPatrick Mooney viona_rx_mcast(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
652b22a70abSPatrick Mooney     boolean_t is_loopback)
653b22a70abSPatrick Mooney {
654b22a70abSPatrick Mooney 	viona_vring_t *ring = (viona_vring_t *)arg;
655b22a70abSPatrick Mooney 	mac_handle_t mh = ring->vr_link->l_mh;
656b22a70abSPatrick Mooney 	mblk_t *mp_mcast_only = NULL;
657b22a70abSPatrick Mooney 	mblk_t **mpp = &mp_mcast_only;
658b22a70abSPatrick Mooney 
659b22a70abSPatrick Mooney 	/* Drop traffic if ring is inactive or renewing its lease */
660b22a70abSPatrick Mooney 	if (ring->vr_state != VRS_RUN ||
661b22a70abSPatrick Mooney 	    (ring->vr_state_flags & VRSF_RENEW) != 0) {
662b22a70abSPatrick Mooney 		freemsgchain(mp);
663b22a70abSPatrick Mooney 		return;
664b22a70abSPatrick Mooney 	}
665b22a70abSPatrick Mooney 
666b22a70abSPatrick Mooney 	/*
667b22a70abSPatrick Mooney 	 * In addition to multicast traffic, broadcast packets will also arrive
668b22a70abSPatrick Mooney 	 * via the MAC_CLIENT_PROMISC_MULTI handler. The mac_rx_set() callback
669b22a70abSPatrick Mooney 	 * for fully-classified traffic has already delivered that broadcast
670b22a70abSPatrick Mooney 	 * traffic, so it should be suppressed here, rather than duplicating it
671b22a70abSPatrick Mooney 	 * to the guest.
672b22a70abSPatrick Mooney 	 */
673b22a70abSPatrick Mooney 	while (mp != NULL) {
674b22a70abSPatrick Mooney 		mblk_t *mp_next;
675b22a70abSPatrick Mooney 		mac_header_info_t mhi;
676b22a70abSPatrick Mooney 		int err;
677b22a70abSPatrick Mooney 
678b22a70abSPatrick Mooney 		mp_next = mp->b_next;
679b22a70abSPatrick Mooney 		mp->b_next = NULL;
680b22a70abSPatrick Mooney 
681b22a70abSPatrick Mooney 		/* Determine the packet type */
682b22a70abSPatrick Mooney 		err = mac_vlan_header_info(mh, mp, &mhi);
683b22a70abSPatrick Mooney 		if (err != 0) {
684b22a70abSPatrick Mooney 			mblk_t *pull;
685b22a70abSPatrick Mooney 
686b22a70abSPatrick Mooney 			/*
687b22a70abSPatrick Mooney 			 * It is possible that gathering of the header
688b22a70abSPatrick Mooney 			 * information was impeded by a leading mblk_t which
689b22a70abSPatrick Mooney 			 * was of inadequate length to reference the needed
690b22a70abSPatrick Mooney 			 * fields.  Try again, in case that could be solved
691b22a70abSPatrick Mooney 			 * with a pull-up.
692b22a70abSPatrick Mooney 			 */
693b22a70abSPatrick Mooney 			pull = msgpullup(mp, sizeof (struct ether_vlan_header));
694b22a70abSPatrick Mooney 			if (pull == NULL) {
695b22a70abSPatrick Mooney 				err = ENOMEM;
696b22a70abSPatrick Mooney 			} else {
697b22a70abSPatrick Mooney 				err = mac_vlan_header_info(mh, pull, &mhi);
698b22a70abSPatrick Mooney 				freemsg(pull);
699b22a70abSPatrick Mooney 			}
700b22a70abSPatrick Mooney 
701b22a70abSPatrick Mooney 			if (err != 0) {
702b22a70abSPatrick Mooney 				VIONA_RING_STAT_INCR(ring, rx_mcast_check);
703b22a70abSPatrick Mooney 			}
704b22a70abSPatrick Mooney 		}
705b22a70abSPatrick Mooney 
706b22a70abSPatrick Mooney 		/* Chain up matching packets while discarding others */
707b22a70abSPatrick Mooney 		if (err == 0 && mhi.mhi_dsttype == MAC_ADDRTYPE_MULTICAST) {
708b22a70abSPatrick Mooney 			*mpp = mp;
709b22a70abSPatrick Mooney 			mpp = &mp->b_next;
710b22a70abSPatrick Mooney 		} else {
711b22a70abSPatrick Mooney 			freemsg(mp);
712b22a70abSPatrick Mooney 		}
713b22a70abSPatrick Mooney 
714b22a70abSPatrick Mooney 		mp = mp_next;
715b22a70abSPatrick Mooney 	}
716b22a70abSPatrick Mooney 
717b22a70abSPatrick Mooney 	if (mp_mcast_only != NULL) {
718b22a70abSPatrick Mooney 		viona_rx_common(ring, mp_mcast_only, is_loopback);
719b22a70abSPatrick Mooney 	}
720b22a70abSPatrick Mooney }
721b22a70abSPatrick Mooney 
722b22a70abSPatrick Mooney int
viona_rx_set(viona_link_t * link,viona_promisc_t mode)723*d4221574SAndy Fiddaman viona_rx_set(viona_link_t *link, viona_promisc_t mode)
724b22a70abSPatrick Mooney {
725b22a70abSPatrick Mooney 	viona_vring_t *ring = &link->l_vrings[VIONA_VQ_RX];
726*d4221574SAndy Fiddaman 	int err = 0;
727b22a70abSPatrick Mooney 
728*d4221574SAndy Fiddaman 	if (link->l_mph != NULL) {
729*d4221574SAndy Fiddaman 		mac_promisc_remove(link->l_mph);
730*d4221574SAndy Fiddaman 		link->l_mph = NULL;
731*d4221574SAndy Fiddaman 	}
732*d4221574SAndy Fiddaman 
733*d4221574SAndy Fiddaman 	switch (mode) {
734*d4221574SAndy Fiddaman 	case VIONA_PROMISC_MULTI:
735*d4221574SAndy Fiddaman 		mac_rx_set(link->l_mch, viona_rx_classified, ring);
736*d4221574SAndy Fiddaman 		err = mac_promisc_add(link->l_mch, MAC_CLIENT_PROMISC_MULTI,
737*d4221574SAndy Fiddaman 		    viona_rx_mcast, ring, &link->l_mph,
738*d4221574SAndy Fiddaman 		    MAC_PROMISC_FLAGS_NO_TX_LOOP |
739*d4221574SAndy Fiddaman 		    MAC_PROMISC_FLAGS_VLAN_TAG_STRIP);
740*d4221574SAndy Fiddaman 		break;
741*d4221574SAndy Fiddaman 	case VIONA_PROMISC_ALL:
742b22a70abSPatrick Mooney 		mac_rx_clear(link->l_mch);
743*d4221574SAndy Fiddaman 		err = mac_promisc_add(link->l_mch, MAC_CLIENT_PROMISC_ALL,
744*d4221574SAndy Fiddaman 		    viona_rx_classified, ring, &link->l_mph,
745*d4221574SAndy Fiddaman 		    MAC_PROMISC_FLAGS_NO_TX_LOOP |
746*d4221574SAndy Fiddaman 		    MAC_PROMISC_FLAGS_VLAN_TAG_STRIP);
747*d4221574SAndy Fiddaman 		/*
748*d4221574SAndy Fiddaman 		 * In case adding the promisc handler failed, restore the
749*d4221574SAndy Fiddaman 		 * generic classified callback so that packets continue to
750*d4221574SAndy Fiddaman 		 * flow to the guest.
751*d4221574SAndy Fiddaman 		 */
752*d4221574SAndy Fiddaman 		if (err != 0) {
753*d4221574SAndy Fiddaman 			mac_rx_set(link->l_mch, viona_rx_classified, ring);
754*d4221574SAndy Fiddaman 		}
755*d4221574SAndy Fiddaman 		break;
756*d4221574SAndy Fiddaman 	case VIONA_PROMISC_NONE:
757*d4221574SAndy Fiddaman 	default:
758*d4221574SAndy Fiddaman 		mac_rx_set(link->l_mch, viona_rx_classified, ring);
759*d4221574SAndy Fiddaman 		break;
760b22a70abSPatrick Mooney 	}
761b22a70abSPatrick Mooney 
762b22a70abSPatrick Mooney 	return (err);
763b22a70abSPatrick Mooney }
764b22a70abSPatrick Mooney 
765b22a70abSPatrick Mooney void
viona_rx_clear(viona_link_t * link)766b22a70abSPatrick Mooney viona_rx_clear(viona_link_t *link)
767b22a70abSPatrick Mooney {
768*d4221574SAndy Fiddaman 	if (link->l_mph != NULL) {
769*d4221574SAndy Fiddaman 		mac_promisc_remove(link->l_mph);
770*d4221574SAndy Fiddaman 		link->l_mph = NULL;
771*d4221574SAndy Fiddaman 	}
772b22a70abSPatrick Mooney 	mac_rx_clear(link->l_mch);
773b22a70abSPatrick Mooney }
774