xref: /titanic_50/usr/src/uts/common/io/vioif/vioif.c (revision 8a324c92216752a3ac2de7c31f554588932de707)
1*8a324c92SDan McDonald /*
2*8a324c92SDan McDonald  * This file and its contents are supplied under the terms of the
3*8a324c92SDan McDonald  * Common Development and Distribution License ("CDDL"), version 1.0.
4*8a324c92SDan McDonald  * You may only use this file in accordance with the terms of version
5*8a324c92SDan McDonald  * 1.0 of the CDDL.
6*8a324c92SDan McDonald  *
7*8a324c92SDan McDonald  * A full copy of the text of the CDDL should have accompanied this
8*8a324c92SDan McDonald  * source.  A copy of the CDDL is also available via the Internet at
9*8a324c92SDan McDonald  * http://www.illumos.org/license/CDDL.
10*8a324c92SDan McDonald  */
11*8a324c92SDan McDonald 
12*8a324c92SDan McDonald /*
13*8a324c92SDan McDonald  * Copyright 2013 Nexenta Inc.  All rights reserved.
14*8a324c92SDan McDonald  * Copyright (c) 2014, 2015 by Delphix. All rights reserved.
15*8a324c92SDan McDonald  */
16*8a324c92SDan McDonald 
17*8a324c92SDan McDonald /* Based on the NetBSD virtio driver by Minoura Makoto. */
18*8a324c92SDan McDonald /*
19*8a324c92SDan McDonald  * Copyright (c) 2010 Minoura Makoto.
20*8a324c92SDan McDonald  * All rights reserved.
21*8a324c92SDan McDonald  *
22*8a324c92SDan McDonald  * Redistribution and use in source and binary forms, with or without
23*8a324c92SDan McDonald  * modification, are permitted provided that the following conditions
24*8a324c92SDan McDonald  * are met:
25*8a324c92SDan McDonald  * 1. Redistributions of source code must retain the above copyright
26*8a324c92SDan McDonald  *    notice, this list of conditions and the following disclaimer.
27*8a324c92SDan McDonald  * 2. Redistributions in binary form must reproduce the above copyright
28*8a324c92SDan McDonald  *    notice, this list of conditions and the following disclaimer in the
29*8a324c92SDan McDonald  *    documentation and/or other materials provided with the distribution.
30*8a324c92SDan McDonald  *
31*8a324c92SDan McDonald  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
32*8a324c92SDan McDonald  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33*8a324c92SDan McDonald  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
34*8a324c92SDan McDonald  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
35*8a324c92SDan McDonald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36*8a324c92SDan McDonald  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37*8a324c92SDan McDonald  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38*8a324c92SDan McDonald  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39*8a324c92SDan McDonald  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
40*8a324c92SDan McDonald  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41*8a324c92SDan McDonald  */
42*8a324c92SDan McDonald 
43*8a324c92SDan McDonald #include <sys/types.h>
44*8a324c92SDan McDonald #include <sys/errno.h>
45*8a324c92SDan McDonald #include <sys/param.h>
46*8a324c92SDan McDonald #include <sys/stropts.h>
47*8a324c92SDan McDonald #include <sys/stream.h>
48*8a324c92SDan McDonald #include <sys/strsubr.h>
49*8a324c92SDan McDonald #include <sys/kmem.h>
50*8a324c92SDan McDonald #include <sys/conf.h>
51*8a324c92SDan McDonald #include <sys/devops.h>
52*8a324c92SDan McDonald #include <sys/ksynch.h>
53*8a324c92SDan McDonald #include <sys/stat.h>
54*8a324c92SDan McDonald #include <sys/modctl.h>
55*8a324c92SDan McDonald #include <sys/debug.h>
56*8a324c92SDan McDonald #include <sys/pci.h>
57*8a324c92SDan McDonald #include <sys/ethernet.h>
58*8a324c92SDan McDonald #include <sys/vlan.h>
59*8a324c92SDan McDonald 
60*8a324c92SDan McDonald #include <sys/dlpi.h>
61*8a324c92SDan McDonald #include <sys/taskq.h>
62*8a324c92SDan McDonald #include <sys/cyclic.h>
63*8a324c92SDan McDonald 
64*8a324c92SDan McDonald #include <sys/pattr.h>
65*8a324c92SDan McDonald #include <sys/strsun.h>
66*8a324c92SDan McDonald 
67*8a324c92SDan McDonald #include <sys/random.h>
68*8a324c92SDan McDonald #include <sys/sysmacros.h>
69*8a324c92SDan McDonald #include <sys/stream.h>
70*8a324c92SDan McDonald 
71*8a324c92SDan McDonald #include <sys/mac.h>
72*8a324c92SDan McDonald #include <sys/mac_provider.h>
73*8a324c92SDan McDonald #include <sys/mac_ether.h>
74*8a324c92SDan McDonald 
75*8a324c92SDan McDonald #include "virtiovar.h"
76*8a324c92SDan McDonald #include "virtioreg.h"
77*8a324c92SDan McDonald 
78*8a324c92SDan McDonald /* Configuration registers */
79*8a324c92SDan McDonald #define	VIRTIO_NET_CONFIG_MAC		0 /* 8bit x 6byte */
80*8a324c92SDan McDonald #define	VIRTIO_NET_CONFIG_STATUS	6 /* 16bit */
81*8a324c92SDan McDonald 
82*8a324c92SDan McDonald /* Feature bits */
83*8a324c92SDan McDonald #define	VIRTIO_NET_F_CSUM	(1 << 0) /* Host handles pkts w/ partial csum */
84*8a324c92SDan McDonald #define	VIRTIO_NET_F_GUEST_CSUM	(1 << 1) /* Guest handles pkts w/ part csum */
85*8a324c92SDan McDonald #define	VIRTIO_NET_F_MAC	(1 << 5) /* Host has given MAC address. */
86*8a324c92SDan McDonald #define	VIRTIO_NET_F_GSO	(1 << 6) /* Host handles pkts w/ any GSO type */
87*8a324c92SDan McDonald #define	VIRTIO_NET_F_GUEST_TSO4	(1 << 7) /* Guest can handle TSOv4 in. */
88*8a324c92SDan McDonald #define	VIRTIO_NET_F_GUEST_TSO6	(1 << 8) /* Guest can handle TSOv6 in. */
89*8a324c92SDan McDonald #define	VIRTIO_NET_F_GUEST_ECN	(1 << 9) /* Guest can handle TSO[6] w/ ECN in */
90*8a324c92SDan McDonald #define	VIRTIO_NET_F_GUEST_UFO	(1 << 10) /* Guest can handle UFO in. */
91*8a324c92SDan McDonald #define	VIRTIO_NET_F_HOST_TSO4	(1 << 11) /* Host can handle TSOv4 in. */
92*8a324c92SDan McDonald #define	VIRTIO_NET_F_HOST_TSO6	(1 << 12) /* Host can handle TSOv6 in. */
93*8a324c92SDan McDonald #define	VIRTIO_NET_F_HOST_ECN	(1 << 13) /* Host can handle TSO[6] w/ ECN in */
94*8a324c92SDan McDonald #define	VIRTIO_NET_F_HOST_UFO	(1 << 14) /* Host can handle UFO in. */
95*8a324c92SDan McDonald #define	VIRTIO_NET_F_MRG_RXBUF	(1 << 15) /* Host can merge receive buffers. */
96*8a324c92SDan McDonald #define	VIRTIO_NET_F_STATUS	(1 << 16) /* Config.status available */
97*8a324c92SDan McDonald #define	VIRTIO_NET_F_CTRL_VQ	(1 << 17) /* Control channel available */
98*8a324c92SDan McDonald #define	VIRTIO_NET_F_CTRL_RX	(1 << 18) /* Control channel RX mode support */
99*8a324c92SDan McDonald #define	VIRTIO_NET_F_CTRL_VLAN	(1 << 19) /* Control channel VLAN filtering */
100*8a324c92SDan McDonald #define	VIRTIO_NET_F_CTRL_RX_EXTRA (1 << 20) /* Extra RX mode control support */
101*8a324c92SDan McDonald 
102*8a324c92SDan McDonald #define	VIRTIO_NET_FEATURE_BITS \
103*8a324c92SDan McDonald 	"\020" \
104*8a324c92SDan McDonald 	"\1CSUM" \
105*8a324c92SDan McDonald 	"\2GUEST_CSUM" \
106*8a324c92SDan McDonald 	"\6MAC" \
107*8a324c92SDan McDonald 	"\7GSO" \
108*8a324c92SDan McDonald 	"\10GUEST_TSO4" \
109*8a324c92SDan McDonald 	"\11GUEST_TSO6" \
110*8a324c92SDan McDonald 	"\12GUEST_ECN" \
111*8a324c92SDan McDonald 	"\13GUEST_UFO" \
112*8a324c92SDan McDonald 	"\14HOST_TSO4" \
113*8a324c92SDan McDonald 	"\15HOST_TSO6" \
114*8a324c92SDan McDonald 	"\16HOST_ECN" \
115*8a324c92SDan McDonald 	"\17HOST_UFO" \
116*8a324c92SDan McDonald 	"\20MRG_RXBUF" \
117*8a324c92SDan McDonald 	"\21STATUS" \
118*8a324c92SDan McDonald 	"\22CTRL_VQ" \
119*8a324c92SDan McDonald 	"\23CTRL_RX" \
120*8a324c92SDan McDonald 	"\24CTRL_VLAN" \
121*8a324c92SDan McDonald 	"\25CTRL_RX_EXTRA"
122*8a324c92SDan McDonald 
123*8a324c92SDan McDonald /* Status */
124*8a324c92SDan McDonald #define	VIRTIO_NET_S_LINK_UP	1
125*8a324c92SDan McDonald 
126*8a324c92SDan McDonald #pragma pack(1)
127*8a324c92SDan McDonald /* Packet header structure */
128*8a324c92SDan McDonald struct virtio_net_hdr {
129*8a324c92SDan McDonald 	uint8_t		flags;
130*8a324c92SDan McDonald 	uint8_t		gso_type;
131*8a324c92SDan McDonald 	uint16_t	hdr_len;
132*8a324c92SDan McDonald 	uint16_t	gso_size;
133*8a324c92SDan McDonald 	uint16_t	csum_start;
134*8a324c92SDan McDonald 	uint16_t	csum_offset;
135*8a324c92SDan McDonald };
136*8a324c92SDan McDonald #pragma pack()
137*8a324c92SDan McDonald 
138*8a324c92SDan McDonald #define	VIRTIO_NET_HDR_F_NEEDS_CSUM	1 /* flags */
139*8a324c92SDan McDonald #define	VIRTIO_NET_HDR_GSO_NONE		0 /* gso_type */
140*8a324c92SDan McDonald #define	VIRTIO_NET_HDR_GSO_TCPV4	1 /* gso_type */
141*8a324c92SDan McDonald #define	VIRTIO_NET_HDR_GSO_UDP		3 /* gso_type */
142*8a324c92SDan McDonald #define	VIRTIO_NET_HDR_GSO_TCPV6	4 /* gso_type */
143*8a324c92SDan McDonald #define	VIRTIO_NET_HDR_GSO_ECN		0x80 /* gso_type, |'ed */
144*8a324c92SDan McDonald 
145*8a324c92SDan McDonald 
146*8a324c92SDan McDonald /* Control virtqueue */
147*8a324c92SDan McDonald #pragma pack(1)
148*8a324c92SDan McDonald struct virtio_net_ctrl_cmd {
149*8a324c92SDan McDonald 	uint8_t	class;
150*8a324c92SDan McDonald 	uint8_t	command;
151*8a324c92SDan McDonald };
152*8a324c92SDan McDonald #pragma pack()
153*8a324c92SDan McDonald 
154*8a324c92SDan McDonald #define	VIRTIO_NET_CTRL_RX		0
155*8a324c92SDan McDonald #define	VIRTIO_NET_CTRL_RX_PROMISC	0
156*8a324c92SDan McDonald #define	VIRTIO_NET_CTRL_RX_ALLMULTI	1
157*8a324c92SDan McDonald 
158*8a324c92SDan McDonald #define	VIRTIO_NET_CTRL_MAC		1
159*8a324c92SDan McDonald #define	VIRTIO_NET_CTRL_MAC_TABLE_SET	0
160*8a324c92SDan McDonald 
161*8a324c92SDan McDonald #define	VIRTIO_NET_CTRL_VLAN		2
162*8a324c92SDan McDonald #define	VIRTIO_NET_CTRL_VLAN_ADD	0
163*8a324c92SDan McDonald #define	VIRTIO_NET_CTRL_VLAN_DEL	1
164*8a324c92SDan McDonald 
165*8a324c92SDan McDonald #pragma pack(1)
166*8a324c92SDan McDonald struct virtio_net_ctrl_status {
167*8a324c92SDan McDonald 	uint8_t	ack;
168*8a324c92SDan McDonald };
169*8a324c92SDan McDonald 
170*8a324c92SDan McDonald struct virtio_net_ctrl_rx {
171*8a324c92SDan McDonald 	uint8_t	onoff;
172*8a324c92SDan McDonald };
173*8a324c92SDan McDonald 
174*8a324c92SDan McDonald struct virtio_net_ctrl_mac_tbl {
175*8a324c92SDan McDonald 	uint32_t nentries;
176*8a324c92SDan McDonald 	uint8_t macs[][ETHERADDRL];
177*8a324c92SDan McDonald };
178*8a324c92SDan McDonald 
179*8a324c92SDan McDonald struct virtio_net_ctrl_vlan {
180*8a324c92SDan McDonald 	uint16_t id;
181*8a324c92SDan McDonald };
182*8a324c92SDan McDonald #pragma pack()
183*8a324c92SDan McDonald 
184*8a324c92SDan McDonald static int vioif_quiesce(dev_info_t *);
185*8a324c92SDan McDonald static int vioif_attach(dev_info_t *, ddi_attach_cmd_t);
186*8a324c92SDan McDonald static int vioif_detach(dev_info_t *, ddi_detach_cmd_t);
187*8a324c92SDan McDonald 
188*8a324c92SDan McDonald DDI_DEFINE_STREAM_OPS(vioif_ops,
189*8a324c92SDan McDonald 	nulldev,		/* identify */
190*8a324c92SDan McDonald 	nulldev,		/* probe */
191*8a324c92SDan McDonald 	vioif_attach,		/* attach */
192*8a324c92SDan McDonald 	vioif_detach,		/* detach */
193*8a324c92SDan McDonald 	nodev,			/* reset */
194*8a324c92SDan McDonald 	NULL,			/* cb_ops */
195*8a324c92SDan McDonald 	D_MP,			/* bus_ops */
196*8a324c92SDan McDonald 	NULL,			/* power */
197*8a324c92SDan McDonald 	vioif_quiesce		/* quiesce */
198*8a324c92SDan McDonald );
199*8a324c92SDan McDonald 
200*8a324c92SDan McDonald static char vioif_ident[] = "VirtIO ethernet driver";
201*8a324c92SDan McDonald 
202*8a324c92SDan McDonald /* Standard Module linkage initialization for a Streams driver */
203*8a324c92SDan McDonald extern struct mod_ops mod_driverops;
204*8a324c92SDan McDonald 
205*8a324c92SDan McDonald static struct modldrv modldrv = {
206*8a324c92SDan McDonald 	&mod_driverops,		/* Type of module.  This one is a driver */
207*8a324c92SDan McDonald 	vioif_ident,		/* short description */
208*8a324c92SDan McDonald 	&vioif_ops		/* driver specific ops */
209*8a324c92SDan McDonald };
210*8a324c92SDan McDonald 
211*8a324c92SDan McDonald static struct modlinkage modlinkage = {
212*8a324c92SDan McDonald 	MODREV_1,
213*8a324c92SDan McDonald 	{
214*8a324c92SDan McDonald 		(void *)&modldrv,
215*8a324c92SDan McDonald 		NULL,
216*8a324c92SDan McDonald 	},
217*8a324c92SDan McDonald };
218*8a324c92SDan McDonald 
219*8a324c92SDan McDonald ddi_device_acc_attr_t vioif_attr = {
220*8a324c92SDan McDonald 	DDI_DEVICE_ATTR_V0,
221*8a324c92SDan McDonald 	DDI_NEVERSWAP_ACC,	/* virtio is always native byte order */
222*8a324c92SDan McDonald 	DDI_STORECACHING_OK_ACC,
223*8a324c92SDan McDonald 	DDI_DEFAULT_ACC
224*8a324c92SDan McDonald };
225*8a324c92SDan McDonald 
226*8a324c92SDan McDonald /*
227*8a324c92SDan McDonald  * A mapping represents a binding for a single buffer that is contiguous in the
228*8a324c92SDan McDonald  * virtual address space.
229*8a324c92SDan McDonald  */
230*8a324c92SDan McDonald struct vioif_buf_mapping {
231*8a324c92SDan McDonald 	caddr_t			vbm_buf;
232*8a324c92SDan McDonald 	ddi_dma_handle_t	vbm_dmah;
233*8a324c92SDan McDonald 	ddi_acc_handle_t	vbm_acch;
234*8a324c92SDan McDonald 	ddi_dma_cookie_t	vbm_dmac;
235*8a324c92SDan McDonald 	unsigned int		vbm_ncookies;
236*8a324c92SDan McDonald };
237*8a324c92SDan McDonald 
238*8a324c92SDan McDonald /*
239*8a324c92SDan McDonald  * Rx buffers can be loaned upstream, so the code has
240*8a324c92SDan McDonald  * to allocate them dynamically.
241*8a324c92SDan McDonald  */
242*8a324c92SDan McDonald struct vioif_rx_buf {
243*8a324c92SDan McDonald 	struct vioif_softc	*rb_sc;
244*8a324c92SDan McDonald 	frtn_t			rb_frtn;
245*8a324c92SDan McDonald 
246*8a324c92SDan McDonald 	struct vioif_buf_mapping rb_mapping;
247*8a324c92SDan McDonald };
248*8a324c92SDan McDonald 
249*8a324c92SDan McDonald /*
250*8a324c92SDan McDonald  * Tx buffers have two mapping types. One, "inline", is pre-allocated and is
251*8a324c92SDan McDonald  * used to hold the virtio_net_header. Small packets also get copied there, as
252*8a324c92SDan McDonald  * it's faster then mapping them. Bigger packets get mapped using the "external"
253*8a324c92SDan McDonald  * mapping array. An array is used, because a packet may consist of muptiple
254*8a324c92SDan McDonald  * fragments, so each fragment gets bound to an entry. According to my
255*8a324c92SDan McDonald  * observations, the number of fragments does not exceed 2, but just in case,
256*8a324c92SDan McDonald  * a bigger, up to VIOIF_INDIRECT_MAX - 1 array is allocated. To save resources,
257*8a324c92SDan McDonald  * the dma handles are allocated lazily in the tx path.
258*8a324c92SDan McDonald  */
259*8a324c92SDan McDonald struct vioif_tx_buf {
260*8a324c92SDan McDonald 	mblk_t			*tb_mp;
261*8a324c92SDan McDonald 
262*8a324c92SDan McDonald 	/* inline buffer */
263*8a324c92SDan McDonald 	struct vioif_buf_mapping tb_inline_mapping;
264*8a324c92SDan McDonald 
265*8a324c92SDan McDonald 	/* External buffers */
266*8a324c92SDan McDonald 	struct vioif_buf_mapping *tb_external_mapping;
267*8a324c92SDan McDonald 	unsigned int		tb_external_num;
268*8a324c92SDan McDonald };
269*8a324c92SDan McDonald 
270*8a324c92SDan McDonald struct vioif_softc {
271*8a324c92SDan McDonald 	dev_info_t		*sc_dev; /* mirrors virtio_softc->sc_dev */
272*8a324c92SDan McDonald 	struct virtio_softc	sc_virtio;
273*8a324c92SDan McDonald 
274*8a324c92SDan McDonald 	mac_handle_t sc_mac_handle;
275*8a324c92SDan McDonald 	mac_register_t *sc_macp;
276*8a324c92SDan McDonald 
277*8a324c92SDan McDonald 	struct virtqueue	*sc_rx_vq;
278*8a324c92SDan McDonald 	struct virtqueue	*sc_tx_vq;
279*8a324c92SDan McDonald 	struct virtqueue	*sc_ctrl_vq;
280*8a324c92SDan McDonald 
281*8a324c92SDan McDonald 	unsigned int		sc_tx_stopped:1;
282*8a324c92SDan McDonald 
283*8a324c92SDan McDonald 	/* Feature bits. */
284*8a324c92SDan McDonald 	unsigned int		sc_rx_csum:1;
285*8a324c92SDan McDonald 	unsigned int		sc_tx_csum:1;
286*8a324c92SDan McDonald 	unsigned int		sc_tx_tso4:1;
287*8a324c92SDan McDonald 
288*8a324c92SDan McDonald 	int			sc_mtu;
289*8a324c92SDan McDonald 	uint8_t			sc_mac[ETHERADDRL];
290*8a324c92SDan McDonald 	/*
291*8a324c92SDan McDonald 	 * For rx buffers, we keep a pointer array, because the buffers
292*8a324c92SDan McDonald 	 * can be loaned upstream, and we have to repopulate the array with
293*8a324c92SDan McDonald 	 * new members.
294*8a324c92SDan McDonald 	 */
295*8a324c92SDan McDonald 	struct vioif_rx_buf	**sc_rxbufs;
296*8a324c92SDan McDonald 
297*8a324c92SDan McDonald 	/*
298*8a324c92SDan McDonald 	 * For tx, we just allocate an array of buffers. The packet can
299*8a324c92SDan McDonald 	 * either be copied into the inline buffer, or the external mapping
300*8a324c92SDan McDonald 	 * could be used to map the packet
301*8a324c92SDan McDonald 	 */
302*8a324c92SDan McDonald 	struct vioif_tx_buf	*sc_txbufs;
303*8a324c92SDan McDonald 
304*8a324c92SDan McDonald 	kstat_t			*sc_intrstat;
305*8a324c92SDan McDonald 	/*
306*8a324c92SDan McDonald 	 * We "loan" rx buffers upstream and reuse them after they are
307*8a324c92SDan McDonald 	 * freed. This lets us avoid allocations in the hot path.
308*8a324c92SDan McDonald 	 */
309*8a324c92SDan McDonald 	kmem_cache_t		*sc_rxbuf_cache;
310*8a324c92SDan McDonald 	ulong_t			sc_rxloan;
311*8a324c92SDan McDonald 
312*8a324c92SDan McDonald 	/* Copying small packets turns out to be faster then mapping them. */
313*8a324c92SDan McDonald 	unsigned long		sc_rxcopy_thresh;
314*8a324c92SDan McDonald 	unsigned long		sc_txcopy_thresh;
315*8a324c92SDan McDonald 	/* Some statistic coming here */
316*8a324c92SDan McDonald 	uint64_t		sc_ipackets;
317*8a324c92SDan McDonald 	uint64_t		sc_opackets;
318*8a324c92SDan McDonald 	uint64_t		sc_rbytes;
319*8a324c92SDan McDonald 	uint64_t		sc_obytes;
320*8a324c92SDan McDonald 	uint64_t		sc_brdcstxmt;
321*8a324c92SDan McDonald 	uint64_t		sc_brdcstrcv;
322*8a324c92SDan McDonald 	uint64_t		sc_multixmt;
323*8a324c92SDan McDonald 	uint64_t		sc_multircv;
324*8a324c92SDan McDonald 	uint64_t		sc_norecvbuf;
325*8a324c92SDan McDonald 	uint64_t		sc_notxbuf;
326*8a324c92SDan McDonald 	uint64_t		sc_ierrors;
327*8a324c92SDan McDonald 	uint64_t		sc_oerrors;
328*8a324c92SDan McDonald };
329*8a324c92SDan McDonald 
330*8a324c92SDan McDonald #define	ETHER_HEADER_LEN		sizeof (struct ether_header)
331*8a324c92SDan McDonald 
332*8a324c92SDan McDonald /* MTU + the ethernet header. */
333*8a324c92SDan McDonald #define	MAX_PAYLOAD	65535
334*8a324c92SDan McDonald #define	MAX_MTU		(MAX_PAYLOAD - ETHER_HEADER_LEN)
335*8a324c92SDan McDonald #define	DEFAULT_MTU	ETHERMTU
336*8a324c92SDan McDonald 
337*8a324c92SDan McDonald /*
338*8a324c92SDan McDonald  * Yeah, we spend 8M per device. Turns out, there is no point
339*8a324c92SDan McDonald  * being smart and using merged rx buffers (VIRTIO_NET_F_MRG_RXBUF),
340*8a324c92SDan McDonald  * because vhost does not support them, and we expect to be used with
341*8a324c92SDan McDonald  * vhost in production environment.
342*8a324c92SDan McDonald  */
343*8a324c92SDan McDonald /* The buffer keeps both the packet data and the virtio_net_header. */
344*8a324c92SDan McDonald #define	VIOIF_RX_SIZE (MAX_PAYLOAD + sizeof (struct virtio_net_hdr))
345*8a324c92SDan McDonald 
346*8a324c92SDan McDonald /*
347*8a324c92SDan McDonald  * We win a bit on header alignment, but the host wins a lot
348*8a324c92SDan McDonald  * more on moving aligned buffers. Might need more thought.
349*8a324c92SDan McDonald  */
350*8a324c92SDan McDonald #define	VIOIF_IP_ALIGN 0
351*8a324c92SDan McDonald 
352*8a324c92SDan McDonald /* Maximum number of indirect descriptors, somewhat arbitrary. */
353*8a324c92SDan McDonald #define	VIOIF_INDIRECT_MAX 128
354*8a324c92SDan McDonald 
355*8a324c92SDan McDonald /*
356*8a324c92SDan McDonald  * We pre-allocate a reasonably large buffer to copy small packets
357*8a324c92SDan McDonald  * there. Bigger packets are mapped, packets with multiple
358*8a324c92SDan McDonald  * cookies are mapped as indirect buffers.
359*8a324c92SDan McDonald  */
360*8a324c92SDan McDonald #define	VIOIF_TX_INLINE_SIZE 2048
361*8a324c92SDan McDonald 
362*8a324c92SDan McDonald /* Native queue size for all queues */
363*8a324c92SDan McDonald #define	VIOIF_RX_QLEN 0
364*8a324c92SDan McDonald #define	VIOIF_TX_QLEN 0
365*8a324c92SDan McDonald #define	VIOIF_CTRL_QLEN 0
366*8a324c92SDan McDonald 
367*8a324c92SDan McDonald static uchar_t vioif_broadcast[ETHERADDRL] = {
368*8a324c92SDan McDonald 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
369*8a324c92SDan McDonald };
370*8a324c92SDan McDonald 
371*8a324c92SDan McDonald #define	VIOIF_TX_THRESH_MAX	640
372*8a324c92SDan McDonald #define	VIOIF_RX_THRESH_MAX	640
373*8a324c92SDan McDonald 
374*8a324c92SDan McDonald #define	CACHE_NAME_SIZE	32
375*8a324c92SDan McDonald 
376*8a324c92SDan McDonald static char vioif_txcopy_thresh[] =
377*8a324c92SDan McDonald 	"vioif_txcopy_thresh";
378*8a324c92SDan McDonald static char vioif_rxcopy_thresh[] =
379*8a324c92SDan McDonald 	"vioif_rxcopy_thresh";
380*8a324c92SDan McDonald 
381*8a324c92SDan McDonald static char *vioif_priv_props[] = {
382*8a324c92SDan McDonald 	vioif_txcopy_thresh,
383*8a324c92SDan McDonald 	vioif_rxcopy_thresh,
384*8a324c92SDan McDonald 	NULL
385*8a324c92SDan McDonald };
386*8a324c92SDan McDonald 
387*8a324c92SDan McDonald /* Add up to ddi? */
388*8a324c92SDan McDonald static ddi_dma_cookie_t *
vioif_dma_curr_cookie(ddi_dma_handle_t dmah)389*8a324c92SDan McDonald vioif_dma_curr_cookie(ddi_dma_handle_t dmah)
390*8a324c92SDan McDonald {
391*8a324c92SDan McDonald 	ddi_dma_impl_t *dmah_impl = (void *) dmah;
392*8a324c92SDan McDonald 	ASSERT(dmah_impl->dmai_cookie);
393*8a324c92SDan McDonald 	return (dmah_impl->dmai_cookie);
394*8a324c92SDan McDonald }
395*8a324c92SDan McDonald 
396*8a324c92SDan McDonald static void
vioif_dma_reset_cookie(ddi_dma_handle_t dmah,ddi_dma_cookie_t * dmac)397*8a324c92SDan McDonald vioif_dma_reset_cookie(ddi_dma_handle_t dmah, ddi_dma_cookie_t *dmac)
398*8a324c92SDan McDonald {
399*8a324c92SDan McDonald 	ddi_dma_impl_t *dmah_impl = (void *) dmah;
400*8a324c92SDan McDonald 	dmah_impl->dmai_cookie = dmac;
401*8a324c92SDan McDonald }
402*8a324c92SDan McDonald 
403*8a324c92SDan McDonald static link_state_t
vioif_link_state(struct vioif_softc * sc)404*8a324c92SDan McDonald vioif_link_state(struct vioif_softc *sc)
405*8a324c92SDan McDonald {
406*8a324c92SDan McDonald 	if (sc->sc_virtio.sc_features & VIRTIO_NET_F_STATUS) {
407*8a324c92SDan McDonald 		if (virtio_read_device_config_2(&sc->sc_virtio,
408*8a324c92SDan McDonald 		    VIRTIO_NET_CONFIG_STATUS) & VIRTIO_NET_S_LINK_UP) {
409*8a324c92SDan McDonald 			return (LINK_STATE_UP);
410*8a324c92SDan McDonald 		} else {
411*8a324c92SDan McDonald 			return (LINK_STATE_DOWN);
412*8a324c92SDan McDonald 		}
413*8a324c92SDan McDonald 	}
414*8a324c92SDan McDonald 
415*8a324c92SDan McDonald 	return (LINK_STATE_UP);
416*8a324c92SDan McDonald }
417*8a324c92SDan McDonald 
418*8a324c92SDan McDonald static ddi_dma_attr_t vioif_inline_buf_dma_attr = {
419*8a324c92SDan McDonald 	DMA_ATTR_V0,		/* Version number */
420*8a324c92SDan McDonald 	0,			/* low address */
421*8a324c92SDan McDonald 	0xFFFFFFFFFFFFFFFF,	/* high address */
422*8a324c92SDan McDonald 	0xFFFFFFFF,		/* counter register max */
423*8a324c92SDan McDonald 	1,			/* page alignment */
424*8a324c92SDan McDonald 	1,			/* burst sizes: 1 - 32 */
425*8a324c92SDan McDonald 	1,			/* minimum transfer size */
426*8a324c92SDan McDonald 	0xFFFFFFFF,		/* max transfer size */
427*8a324c92SDan McDonald 	0xFFFFFFFFFFFFFFF,	/* address register max */
428*8a324c92SDan McDonald 	1,			/* scatter-gather capacity */
429*8a324c92SDan McDonald 	1,			/* device operates on bytes */
430*8a324c92SDan McDonald 	0,			/* attr flag: set to 0 */
431*8a324c92SDan McDonald };
432*8a324c92SDan McDonald 
433*8a324c92SDan McDonald static ddi_dma_attr_t vioif_mapped_buf_dma_attr = {
434*8a324c92SDan McDonald 	DMA_ATTR_V0,		/* Version number */
435*8a324c92SDan McDonald 	0,			/* low address */
436*8a324c92SDan McDonald 	0xFFFFFFFFFFFFFFFF,	/* high address */
437*8a324c92SDan McDonald 	0xFFFFFFFF,		/* counter register max */
438*8a324c92SDan McDonald 	1,			/* page alignment */
439*8a324c92SDan McDonald 	1,			/* burst sizes: 1 - 32 */
440*8a324c92SDan McDonald 	1,			/* minimum transfer size */
441*8a324c92SDan McDonald 	0xFFFFFFFF,		/* max transfer size */
442*8a324c92SDan McDonald 	0xFFFFFFFFFFFFFFF,	/* address register max */
443*8a324c92SDan McDonald 
444*8a324c92SDan McDonald 	/* One entry is used for the virtio_net_hdr on the tx path */
445*8a324c92SDan McDonald 	VIOIF_INDIRECT_MAX - 1,	/* scatter-gather capacity */
446*8a324c92SDan McDonald 	1,			/* device operates on bytes */
447*8a324c92SDan McDonald 	0,			/* attr flag: set to 0 */
448*8a324c92SDan McDonald };
449*8a324c92SDan McDonald 
450*8a324c92SDan McDonald static ddi_device_acc_attr_t vioif_bufattr = {
451*8a324c92SDan McDonald 	DDI_DEVICE_ATTR_V0,
452*8a324c92SDan McDonald 	DDI_NEVERSWAP_ACC,
453*8a324c92SDan McDonald 	DDI_STORECACHING_OK_ACC,
454*8a324c92SDan McDonald 	DDI_DEFAULT_ACC
455*8a324c92SDan McDonald };
456*8a324c92SDan McDonald 
457*8a324c92SDan McDonald static void
vioif_rx_free(caddr_t free_arg)458*8a324c92SDan McDonald vioif_rx_free(caddr_t free_arg)
459*8a324c92SDan McDonald {
460*8a324c92SDan McDonald 	struct vioif_rx_buf *buf = (void *) free_arg;
461*8a324c92SDan McDonald 	struct vioif_softc *sc = buf->rb_sc;
462*8a324c92SDan McDonald 
463*8a324c92SDan McDonald 	kmem_cache_free(sc->sc_rxbuf_cache, buf);
464*8a324c92SDan McDonald 	atomic_dec_ulong(&sc->sc_rxloan);
465*8a324c92SDan McDonald }
466*8a324c92SDan McDonald 
467*8a324c92SDan McDonald static int
vioif_rx_construct(void * buffer,void * user_arg,int kmflags)468*8a324c92SDan McDonald vioif_rx_construct(void *buffer, void *user_arg, int kmflags)
469*8a324c92SDan McDonald {
470*8a324c92SDan McDonald 	_NOTE(ARGUNUSED(kmflags));
471*8a324c92SDan McDonald 	struct vioif_softc *sc = user_arg;
472*8a324c92SDan McDonald 	struct vioif_rx_buf *buf = buffer;
473*8a324c92SDan McDonald 	size_t len;
474*8a324c92SDan McDonald 
475*8a324c92SDan McDonald 	if (ddi_dma_alloc_handle(sc->sc_dev, &vioif_mapped_buf_dma_attr,
476*8a324c92SDan McDonald 	    DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmah)) {
477*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_WARN,
478*8a324c92SDan McDonald 		    "Can't allocate dma handle for rx buffer");
479*8a324c92SDan McDonald 		goto exit_handle;
480*8a324c92SDan McDonald 	}
481*8a324c92SDan McDonald 
482*8a324c92SDan McDonald 	if (ddi_dma_mem_alloc(buf->rb_mapping.vbm_dmah,
483*8a324c92SDan McDonald 	    VIOIF_RX_SIZE + sizeof (struct virtio_net_hdr),
484*8a324c92SDan McDonald 	    &vioif_bufattr, DDI_DMA_STREAMING, DDI_DMA_SLEEP,
485*8a324c92SDan McDonald 	    NULL, &buf->rb_mapping.vbm_buf, &len, &buf->rb_mapping.vbm_acch)) {
486*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_WARN,
487*8a324c92SDan McDonald 		    "Can't allocate rx buffer");
488*8a324c92SDan McDonald 		goto exit_alloc;
489*8a324c92SDan McDonald 	}
490*8a324c92SDan McDonald 	ASSERT(len >= VIOIF_RX_SIZE);
491*8a324c92SDan McDonald 
492*8a324c92SDan McDonald 	if (ddi_dma_addr_bind_handle(buf->rb_mapping.vbm_dmah, NULL,
493*8a324c92SDan McDonald 	    buf->rb_mapping.vbm_buf, len, DDI_DMA_READ | DDI_DMA_STREAMING,
494*8a324c92SDan McDonald 	    DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmac,
495*8a324c92SDan McDonald 	    &buf->rb_mapping.vbm_ncookies)) {
496*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_WARN, "Can't bind tx buffer");
497*8a324c92SDan McDonald 
498*8a324c92SDan McDonald 		goto exit_bind;
499*8a324c92SDan McDonald 	}
500*8a324c92SDan McDonald 
501*8a324c92SDan McDonald 	ASSERT(buf->rb_mapping.vbm_ncookies <= VIOIF_INDIRECT_MAX);
502*8a324c92SDan McDonald 
503*8a324c92SDan McDonald 	buf->rb_sc = sc;
504*8a324c92SDan McDonald 	buf->rb_frtn.free_arg = (void *) buf;
505*8a324c92SDan McDonald 	buf->rb_frtn.free_func = vioif_rx_free;
506*8a324c92SDan McDonald 
507*8a324c92SDan McDonald 	return (0);
508*8a324c92SDan McDonald exit_bind:
509*8a324c92SDan McDonald 	ddi_dma_mem_free(&buf->rb_mapping.vbm_acch);
510*8a324c92SDan McDonald exit_alloc:
511*8a324c92SDan McDonald 	ddi_dma_free_handle(&buf->rb_mapping.vbm_dmah);
512*8a324c92SDan McDonald exit_handle:
513*8a324c92SDan McDonald 
514*8a324c92SDan McDonald 	return (ENOMEM);
515*8a324c92SDan McDonald }
516*8a324c92SDan McDonald 
517*8a324c92SDan McDonald static void
vioif_rx_destruct(void * buffer,void * user_arg)518*8a324c92SDan McDonald vioif_rx_destruct(void *buffer, void *user_arg)
519*8a324c92SDan McDonald {
520*8a324c92SDan McDonald 	_NOTE(ARGUNUSED(user_arg));
521*8a324c92SDan McDonald 	struct vioif_rx_buf *buf = buffer;
522*8a324c92SDan McDonald 
523*8a324c92SDan McDonald 	ASSERT(buf->rb_mapping.vbm_acch);
524*8a324c92SDan McDonald 	ASSERT(buf->rb_mapping.vbm_acch);
525*8a324c92SDan McDonald 
526*8a324c92SDan McDonald 	(void) ddi_dma_unbind_handle(buf->rb_mapping.vbm_dmah);
527*8a324c92SDan McDonald 	ddi_dma_mem_free(&buf->rb_mapping.vbm_acch);
528*8a324c92SDan McDonald 	ddi_dma_free_handle(&buf->rb_mapping.vbm_dmah);
529*8a324c92SDan McDonald }
530*8a324c92SDan McDonald 
531*8a324c92SDan McDonald static void
vioif_free_mems(struct vioif_softc * sc)532*8a324c92SDan McDonald vioif_free_mems(struct vioif_softc *sc)
533*8a324c92SDan McDonald {
534*8a324c92SDan McDonald 	int i;
535*8a324c92SDan McDonald 
536*8a324c92SDan McDonald 	for (i = 0; i < sc->sc_tx_vq->vq_num; i++) {
537*8a324c92SDan McDonald 		struct vioif_tx_buf *buf = &sc->sc_txbufs[i];
538*8a324c92SDan McDonald 		int j;
539*8a324c92SDan McDonald 
540*8a324c92SDan McDonald 		/* Tear down the internal mapping. */
541*8a324c92SDan McDonald 
542*8a324c92SDan McDonald 		ASSERT(buf->tb_inline_mapping.vbm_acch);
543*8a324c92SDan McDonald 		ASSERT(buf->tb_inline_mapping.vbm_dmah);
544*8a324c92SDan McDonald 
545*8a324c92SDan McDonald 		(void) ddi_dma_unbind_handle(buf->tb_inline_mapping.vbm_dmah);
546*8a324c92SDan McDonald 		ddi_dma_mem_free(&buf->tb_inline_mapping.vbm_acch);
547*8a324c92SDan McDonald 		ddi_dma_free_handle(&buf->tb_inline_mapping.vbm_dmah);
548*8a324c92SDan McDonald 
549*8a324c92SDan McDonald 		/* We should not see any in-flight buffers at this point. */
550*8a324c92SDan McDonald 		ASSERT(!buf->tb_mp);
551*8a324c92SDan McDonald 
552*8a324c92SDan McDonald 		/* Free all the dma hdnales we allocated lazily. */
553*8a324c92SDan McDonald 		for (j = 0; buf->tb_external_mapping[j].vbm_dmah; j++)
554*8a324c92SDan McDonald 			ddi_dma_free_handle(
555*8a324c92SDan McDonald 			    &buf->tb_external_mapping[j].vbm_dmah);
556*8a324c92SDan McDonald 		/* Free the external mapping array. */
557*8a324c92SDan McDonald 		kmem_free(buf->tb_external_mapping,
558*8a324c92SDan McDonald 		    sizeof (struct vioif_tx_buf) * VIOIF_INDIRECT_MAX - 1);
559*8a324c92SDan McDonald 	}
560*8a324c92SDan McDonald 
561*8a324c92SDan McDonald 	kmem_free(sc->sc_txbufs, sizeof (struct vioif_tx_buf) *
562*8a324c92SDan McDonald 	    sc->sc_tx_vq->vq_num);
563*8a324c92SDan McDonald 
564*8a324c92SDan McDonald 	for (i = 0; i < sc->sc_rx_vq->vq_num; i++) {
565*8a324c92SDan McDonald 		struct vioif_rx_buf *buf = sc->sc_rxbufs[i];
566*8a324c92SDan McDonald 
567*8a324c92SDan McDonald 		if (buf)
568*8a324c92SDan McDonald 			kmem_cache_free(sc->sc_rxbuf_cache, buf);
569*8a324c92SDan McDonald 	}
570*8a324c92SDan McDonald 	kmem_free(sc->sc_rxbufs, sizeof (struct vioif_rx_buf *) *
571*8a324c92SDan McDonald 	    sc->sc_rx_vq->vq_num);
572*8a324c92SDan McDonald }
573*8a324c92SDan McDonald 
574*8a324c92SDan McDonald static int
vioif_alloc_mems(struct vioif_softc * sc)575*8a324c92SDan McDonald vioif_alloc_mems(struct vioif_softc *sc)
576*8a324c92SDan McDonald {
577*8a324c92SDan McDonald 	int i, txqsize, rxqsize;
578*8a324c92SDan McDonald 	size_t len;
579*8a324c92SDan McDonald 	unsigned int nsegments;
580*8a324c92SDan McDonald 
581*8a324c92SDan McDonald 	txqsize = sc->sc_tx_vq->vq_num;
582*8a324c92SDan McDonald 	rxqsize = sc->sc_rx_vq->vq_num;
583*8a324c92SDan McDonald 
584*8a324c92SDan McDonald 	sc->sc_txbufs = kmem_zalloc(sizeof (struct vioif_tx_buf) * txqsize,
585*8a324c92SDan McDonald 	    KM_SLEEP);
586*8a324c92SDan McDonald 	if (sc->sc_txbufs == NULL) {
587*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_WARN,
588*8a324c92SDan McDonald 		    "Failed to allocate the tx buffers array");
589*8a324c92SDan McDonald 		goto exit_txalloc;
590*8a324c92SDan McDonald 	}
591*8a324c92SDan McDonald 
592*8a324c92SDan McDonald 	/*
593*8a324c92SDan McDonald 	 * We don't allocate the rx vioif_bufs, just the pointers, as
594*8a324c92SDan McDonald 	 * rx vioif_bufs can be loaned upstream, and we don't know the
595*8a324c92SDan McDonald 	 * total number we need.
596*8a324c92SDan McDonald 	 */
597*8a324c92SDan McDonald 	sc->sc_rxbufs = kmem_zalloc(sizeof (struct vioif_rx_buf *) * rxqsize,
598*8a324c92SDan McDonald 	    KM_SLEEP);
599*8a324c92SDan McDonald 	if (sc->sc_rxbufs == NULL) {
600*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_WARN,
601*8a324c92SDan McDonald 		    "Failed to allocate the rx buffers pointer array");
602*8a324c92SDan McDonald 		goto exit_rxalloc;
603*8a324c92SDan McDonald 	}
604*8a324c92SDan McDonald 
605*8a324c92SDan McDonald 	for (i = 0; i < txqsize; i++) {
606*8a324c92SDan McDonald 		struct vioif_tx_buf *buf = &sc->sc_txbufs[i];
607*8a324c92SDan McDonald 
608*8a324c92SDan McDonald 		/* Allocate and bind an inline mapping. */
609*8a324c92SDan McDonald 
610*8a324c92SDan McDonald 		if (ddi_dma_alloc_handle(sc->sc_dev,
611*8a324c92SDan McDonald 		    &vioif_inline_buf_dma_attr,
612*8a324c92SDan McDonald 		    DDI_DMA_SLEEP, NULL, &buf->tb_inline_mapping.vbm_dmah)) {
613*8a324c92SDan McDonald 
614*8a324c92SDan McDonald 			dev_err(sc->sc_dev, CE_WARN,
615*8a324c92SDan McDonald 			    "Can't allocate dma handle for tx buffer %d", i);
616*8a324c92SDan McDonald 			goto exit_tx;
617*8a324c92SDan McDonald 		}
618*8a324c92SDan McDonald 
619*8a324c92SDan McDonald 		if (ddi_dma_mem_alloc(buf->tb_inline_mapping.vbm_dmah,
620*8a324c92SDan McDonald 		    VIOIF_TX_INLINE_SIZE, &vioif_bufattr, DDI_DMA_STREAMING,
621*8a324c92SDan McDonald 		    DDI_DMA_SLEEP, NULL, &buf->tb_inline_mapping.vbm_buf,
622*8a324c92SDan McDonald 		    &len, &buf->tb_inline_mapping.vbm_acch)) {
623*8a324c92SDan McDonald 
624*8a324c92SDan McDonald 			dev_err(sc->sc_dev, CE_WARN,
625*8a324c92SDan McDonald 			    "Can't allocate tx buffer %d", i);
626*8a324c92SDan McDonald 			goto exit_tx;
627*8a324c92SDan McDonald 		}
628*8a324c92SDan McDonald 		ASSERT(len >= VIOIF_TX_INLINE_SIZE);
629*8a324c92SDan McDonald 
630*8a324c92SDan McDonald 		if (ddi_dma_addr_bind_handle(buf->tb_inline_mapping.vbm_dmah,
631*8a324c92SDan McDonald 		    NULL, buf->tb_inline_mapping.vbm_buf, len,
632*8a324c92SDan McDonald 		    DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL,
633*8a324c92SDan McDonald 		    &buf->tb_inline_mapping.vbm_dmac, &nsegments)) {
634*8a324c92SDan McDonald 
635*8a324c92SDan McDonald 			dev_err(sc->sc_dev, CE_WARN,
636*8a324c92SDan McDonald 			    "Can't bind tx buffer %d", i);
637*8a324c92SDan McDonald 			goto exit_tx;
638*8a324c92SDan McDonald 		}
639*8a324c92SDan McDonald 
640*8a324c92SDan McDonald 		/* We asked for a single segment */
641*8a324c92SDan McDonald 		ASSERT(nsegments == 1);
642*8a324c92SDan McDonald 
643*8a324c92SDan McDonald 		/*
644*8a324c92SDan McDonald 		 * We allow up to VIOIF_INDIRECT_MAX - 1 external mappings.
645*8a324c92SDan McDonald 		 * In reality, I don't expect more then 2-3 used, but who
646*8a324c92SDan McDonald 		 * knows.
647*8a324c92SDan McDonald 		 */
648*8a324c92SDan McDonald 		buf->tb_external_mapping = kmem_zalloc(
649*8a324c92SDan McDonald 		    sizeof (struct vioif_tx_buf) * VIOIF_INDIRECT_MAX - 1,
650*8a324c92SDan McDonald 		    KM_SLEEP);
651*8a324c92SDan McDonald 
652*8a324c92SDan McDonald 		/*
653*8a324c92SDan McDonald 		 * The external mapping's dma handles are allocate lazily,
654*8a324c92SDan McDonald 		 * as we don't expect most of them to be used..
655*8a324c92SDan McDonald 		 */
656*8a324c92SDan McDonald 	}
657*8a324c92SDan McDonald 
658*8a324c92SDan McDonald 	return (0);
659*8a324c92SDan McDonald 
660*8a324c92SDan McDonald exit_tx:
661*8a324c92SDan McDonald 	for (i = 0; i < txqsize; i++) {
662*8a324c92SDan McDonald 		struct vioif_tx_buf *buf = &sc->sc_txbufs[i];
663*8a324c92SDan McDonald 
664*8a324c92SDan McDonald 		if (buf->tb_inline_mapping.vbm_dmah)
665*8a324c92SDan McDonald 			(void) ddi_dma_unbind_handle(
666*8a324c92SDan McDonald 			    buf->tb_inline_mapping.vbm_dmah);
667*8a324c92SDan McDonald 
668*8a324c92SDan McDonald 		if (buf->tb_inline_mapping.vbm_acch)
669*8a324c92SDan McDonald 			ddi_dma_mem_free(
670*8a324c92SDan McDonald 			    &buf->tb_inline_mapping.vbm_acch);
671*8a324c92SDan McDonald 
672*8a324c92SDan McDonald 		if (buf->tb_inline_mapping.vbm_dmah)
673*8a324c92SDan McDonald 			ddi_dma_free_handle(
674*8a324c92SDan McDonald 			    &buf->tb_inline_mapping.vbm_dmah);
675*8a324c92SDan McDonald 
676*8a324c92SDan McDonald 		if (buf->tb_external_mapping)
677*8a324c92SDan McDonald 			kmem_free(buf->tb_external_mapping,
678*8a324c92SDan McDonald 			    sizeof (struct vioif_tx_buf) *
679*8a324c92SDan McDonald 			    VIOIF_INDIRECT_MAX - 1);
680*8a324c92SDan McDonald 	}
681*8a324c92SDan McDonald 
682*8a324c92SDan McDonald 	kmem_free(sc->sc_rxbufs, sizeof (struct vioif_rx_buf) * rxqsize);
683*8a324c92SDan McDonald 
684*8a324c92SDan McDonald exit_rxalloc:
685*8a324c92SDan McDonald 	kmem_free(sc->sc_txbufs, sizeof (struct vioif_tx_buf) * txqsize);
686*8a324c92SDan McDonald exit_txalloc:
687*8a324c92SDan McDonald 	return (ENOMEM);
688*8a324c92SDan McDonald }
689*8a324c92SDan McDonald 
690*8a324c92SDan McDonald /* ARGSUSED */
691*8a324c92SDan McDonald int
vioif_multicst(void * arg,boolean_t add,const uint8_t * macaddr)692*8a324c92SDan McDonald vioif_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
693*8a324c92SDan McDonald {
694*8a324c92SDan McDonald 	return (DDI_SUCCESS);
695*8a324c92SDan McDonald }
696*8a324c92SDan McDonald 
697*8a324c92SDan McDonald /* ARGSUSED */
698*8a324c92SDan McDonald int
vioif_promisc(void * arg,boolean_t on)699*8a324c92SDan McDonald vioif_promisc(void *arg, boolean_t on)
700*8a324c92SDan McDonald {
701*8a324c92SDan McDonald 	return (DDI_SUCCESS);
702*8a324c92SDan McDonald }
703*8a324c92SDan McDonald 
704*8a324c92SDan McDonald /* ARGSUSED */
705*8a324c92SDan McDonald int
vioif_unicst(void * arg,const uint8_t * macaddr)706*8a324c92SDan McDonald vioif_unicst(void *arg, const uint8_t *macaddr)
707*8a324c92SDan McDonald {
708*8a324c92SDan McDonald 	return (DDI_FAILURE);
709*8a324c92SDan McDonald }
710*8a324c92SDan McDonald 
711*8a324c92SDan McDonald 
712*8a324c92SDan McDonald static int
vioif_add_rx(struct vioif_softc * sc,int kmflag)713*8a324c92SDan McDonald vioif_add_rx(struct vioif_softc *sc, int kmflag)
714*8a324c92SDan McDonald {
715*8a324c92SDan McDonald 	struct vq_entry *ve;
716*8a324c92SDan McDonald 	struct vioif_rx_buf *buf;
717*8a324c92SDan McDonald 
718*8a324c92SDan McDonald 	ve = vq_alloc_entry(sc->sc_rx_vq);
719*8a324c92SDan McDonald 	if (!ve) {
720*8a324c92SDan McDonald 		/*
721*8a324c92SDan McDonald 		 * Out of free descriptors - ring already full.
722*8a324c92SDan McDonald 		 * It would be better to update sc_norxdescavail
723*8a324c92SDan McDonald 		 * but MAC does not ask for this info, hence we
724*8a324c92SDan McDonald 		 * update sc_norecvbuf.
725*8a324c92SDan McDonald 		 */
726*8a324c92SDan McDonald 		sc->sc_norecvbuf++;
727*8a324c92SDan McDonald 		goto exit_vq;
728*8a324c92SDan McDonald 	}
729*8a324c92SDan McDonald 	buf = sc->sc_rxbufs[ve->qe_index];
730*8a324c92SDan McDonald 
731*8a324c92SDan McDonald 	if (!buf) {
732*8a324c92SDan McDonald 		/* First run, allocate the buffer. */
733*8a324c92SDan McDonald 		buf = kmem_cache_alloc(sc->sc_rxbuf_cache, kmflag);
734*8a324c92SDan McDonald 		sc->sc_rxbufs[ve->qe_index] = buf;
735*8a324c92SDan McDonald 	}
736*8a324c92SDan McDonald 
737*8a324c92SDan McDonald 	/* Still nothing? Bye. */
738*8a324c92SDan McDonald 	if (!buf) {
739*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_WARN, "Can't allocate rx buffer");
740*8a324c92SDan McDonald 		sc->sc_norecvbuf++;
741*8a324c92SDan McDonald 		goto exit_buf;
742*8a324c92SDan McDonald 	}
743*8a324c92SDan McDonald 
744*8a324c92SDan McDonald 	ASSERT(buf->rb_mapping.vbm_ncookies >= 1);
745*8a324c92SDan McDonald 
746*8a324c92SDan McDonald 	/*
747*8a324c92SDan McDonald 	 * For an unknown reason, the virtio_net_hdr must be placed
748*8a324c92SDan McDonald 	 * as a separate virtio queue entry.
749*8a324c92SDan McDonald 	 */
750*8a324c92SDan McDonald 	virtio_ve_add_indirect_buf(ve, buf->rb_mapping.vbm_dmac.dmac_laddress,
751*8a324c92SDan McDonald 	    sizeof (struct virtio_net_hdr), B_FALSE);
752*8a324c92SDan McDonald 
753*8a324c92SDan McDonald 	/* Add the rest of the first cookie. */
754*8a324c92SDan McDonald 	virtio_ve_add_indirect_buf(ve,
755*8a324c92SDan McDonald 	    buf->rb_mapping.vbm_dmac.dmac_laddress +
756*8a324c92SDan McDonald 	    sizeof (struct virtio_net_hdr),
757*8a324c92SDan McDonald 	    buf->rb_mapping.vbm_dmac.dmac_size -
758*8a324c92SDan McDonald 	    sizeof (struct virtio_net_hdr), B_FALSE);
759*8a324c92SDan McDonald 
760*8a324c92SDan McDonald 	/*
761*8a324c92SDan McDonald 	 * If the buffer consists of a single cookie (unlikely for a
762*8a324c92SDan McDonald 	 * 64-k buffer), we are done. Otherwise, add the rest of the cookies
763*8a324c92SDan McDonald 	 * using indirect entries.
764*8a324c92SDan McDonald 	 */
765*8a324c92SDan McDonald 	if (buf->rb_mapping.vbm_ncookies > 1) {
766*8a324c92SDan McDonald 		ddi_dma_cookie_t *first_extra_dmac;
767*8a324c92SDan McDonald 		ddi_dma_cookie_t dmac;
768*8a324c92SDan McDonald 		first_extra_dmac =
769*8a324c92SDan McDonald 		    vioif_dma_curr_cookie(buf->rb_mapping.vbm_dmah);
770*8a324c92SDan McDonald 
771*8a324c92SDan McDonald 		ddi_dma_nextcookie(buf->rb_mapping.vbm_dmah, &dmac);
772*8a324c92SDan McDonald 		virtio_ve_add_cookie(ve, buf->rb_mapping.vbm_dmah,
773*8a324c92SDan McDonald 		    dmac, buf->rb_mapping.vbm_ncookies - 1, B_FALSE);
774*8a324c92SDan McDonald 		vioif_dma_reset_cookie(buf->rb_mapping.vbm_dmah,
775*8a324c92SDan McDonald 		    first_extra_dmac);
776*8a324c92SDan McDonald 	}
777*8a324c92SDan McDonald 
778*8a324c92SDan McDonald 	virtio_push_chain(ve, B_FALSE);
779*8a324c92SDan McDonald 
780*8a324c92SDan McDonald 	return (DDI_SUCCESS);
781*8a324c92SDan McDonald 
782*8a324c92SDan McDonald exit_buf:
783*8a324c92SDan McDonald 	vq_free_entry(sc->sc_rx_vq, ve);
784*8a324c92SDan McDonald exit_vq:
785*8a324c92SDan McDonald 	return (DDI_FAILURE);
786*8a324c92SDan McDonald }
787*8a324c92SDan McDonald 
788*8a324c92SDan McDonald static int
vioif_populate_rx(struct vioif_softc * sc,int kmflag)789*8a324c92SDan McDonald vioif_populate_rx(struct vioif_softc *sc, int kmflag)
790*8a324c92SDan McDonald {
791*8a324c92SDan McDonald 	int i = 0;
792*8a324c92SDan McDonald 	int ret;
793*8a324c92SDan McDonald 
794*8a324c92SDan McDonald 	for (;;) {
795*8a324c92SDan McDonald 		ret = vioif_add_rx(sc, kmflag);
796*8a324c92SDan McDonald 		if (ret)
797*8a324c92SDan McDonald 			/*
798*8a324c92SDan McDonald 			 * We could not allocate some memory. Try to work with
799*8a324c92SDan McDonald 			 * what we've got.
800*8a324c92SDan McDonald 			 */
801*8a324c92SDan McDonald 			break;
802*8a324c92SDan McDonald 		i++;
803*8a324c92SDan McDonald 	}
804*8a324c92SDan McDonald 
805*8a324c92SDan McDonald 	if (i)
806*8a324c92SDan McDonald 		virtio_sync_vq(sc->sc_rx_vq);
807*8a324c92SDan McDonald 
808*8a324c92SDan McDonald 	return (i);
809*8a324c92SDan McDonald }
810*8a324c92SDan McDonald 
811*8a324c92SDan McDonald static int
vioif_process_rx(struct vioif_softc * sc)812*8a324c92SDan McDonald vioif_process_rx(struct vioif_softc *sc)
813*8a324c92SDan McDonald {
814*8a324c92SDan McDonald 	struct vq_entry *ve;
815*8a324c92SDan McDonald 	struct vioif_rx_buf *buf;
816*8a324c92SDan McDonald 	mblk_t *mp;
817*8a324c92SDan McDonald 	uint32_t len;
818*8a324c92SDan McDonald 	int i = 0;
819*8a324c92SDan McDonald 
820*8a324c92SDan McDonald 	while ((ve = virtio_pull_chain(sc->sc_rx_vq, &len))) {
821*8a324c92SDan McDonald 
822*8a324c92SDan McDonald 		buf = sc->sc_rxbufs[ve->qe_index];
823*8a324c92SDan McDonald 		ASSERT(buf);
824*8a324c92SDan McDonald 
825*8a324c92SDan McDonald 		if (len < sizeof (struct virtio_net_hdr)) {
826*8a324c92SDan McDonald 			dev_err(sc->sc_dev, CE_WARN, "RX: Cnain too small: %u",
827*8a324c92SDan McDonald 			    len - (uint32_t)sizeof (struct virtio_net_hdr));
828*8a324c92SDan McDonald 			sc->sc_ierrors++;
829*8a324c92SDan McDonald 			virtio_free_chain(ve);
830*8a324c92SDan McDonald 			continue;
831*8a324c92SDan McDonald 		}
832*8a324c92SDan McDonald 
833*8a324c92SDan McDonald 		len -= sizeof (struct virtio_net_hdr);
834*8a324c92SDan McDonald 		/*
835*8a324c92SDan McDonald 		 * We copy small packets that happenned to fit into a single
836*8a324c92SDan McDonald 		 * cookie and reuse the buffers. For bigger ones, we loan
837*8a324c92SDan McDonald 		 * the buffers upstream.
838*8a324c92SDan McDonald 		 */
839*8a324c92SDan McDonald 		if (len < sc->sc_rxcopy_thresh) {
840*8a324c92SDan McDonald 			mp = allocb(len, 0);
841*8a324c92SDan McDonald 			if (!mp) {
842*8a324c92SDan McDonald 				sc->sc_norecvbuf++;
843*8a324c92SDan McDonald 				sc->sc_ierrors++;
844*8a324c92SDan McDonald 
845*8a324c92SDan McDonald 				virtio_free_chain(ve);
846*8a324c92SDan McDonald 				break;
847*8a324c92SDan McDonald 			}
848*8a324c92SDan McDonald 
849*8a324c92SDan McDonald 			bcopy((char *)buf->rb_mapping.vbm_buf +
850*8a324c92SDan McDonald 			    sizeof (struct virtio_net_hdr), mp->b_rptr, len);
851*8a324c92SDan McDonald 			mp->b_wptr = mp->b_rptr + len;
852*8a324c92SDan McDonald 
853*8a324c92SDan McDonald 		} else {
854*8a324c92SDan McDonald 			mp = desballoc((unsigned char *)
855*8a324c92SDan McDonald 			    buf->rb_mapping.vbm_buf +
856*8a324c92SDan McDonald 			    sizeof (struct virtio_net_hdr) +
857*8a324c92SDan McDonald 			    VIOIF_IP_ALIGN, len, 0, &buf->rb_frtn);
858*8a324c92SDan McDonald 			if (!mp) {
859*8a324c92SDan McDonald 				sc->sc_norecvbuf++;
860*8a324c92SDan McDonald 				sc->sc_ierrors++;
861*8a324c92SDan McDonald 
862*8a324c92SDan McDonald 				virtio_free_chain(ve);
863*8a324c92SDan McDonald 				break;
864*8a324c92SDan McDonald 			}
865*8a324c92SDan McDonald 			mp->b_wptr = mp->b_rptr + len;
866*8a324c92SDan McDonald 
867*8a324c92SDan McDonald 			atomic_inc_ulong(&sc->sc_rxloan);
868*8a324c92SDan McDonald 			/*
869*8a324c92SDan McDonald 			 * Buffer loaned, we will have to allocate a new one
870*8a324c92SDan McDonald 			 * for this slot.
871*8a324c92SDan McDonald 			 */
872*8a324c92SDan McDonald 			sc->sc_rxbufs[ve->qe_index] = NULL;
873*8a324c92SDan McDonald 		}
874*8a324c92SDan McDonald 
875*8a324c92SDan McDonald 		/*
876*8a324c92SDan McDonald 		 * virtio-net does not tell us if this packet is multicast
877*8a324c92SDan McDonald 		 * or broadcast, so we have to check it.
878*8a324c92SDan McDonald 		 */
879*8a324c92SDan McDonald 		if (mp->b_rptr[0] & 0x1) {
880*8a324c92SDan McDonald 			if (bcmp(mp->b_rptr, vioif_broadcast, ETHERADDRL) != 0)
881*8a324c92SDan McDonald 				sc->sc_multircv++;
882*8a324c92SDan McDonald 			else
883*8a324c92SDan McDonald 				sc->sc_brdcstrcv++;
884*8a324c92SDan McDonald 		}
885*8a324c92SDan McDonald 
886*8a324c92SDan McDonald 		sc->sc_rbytes += len;
887*8a324c92SDan McDonald 		sc->sc_ipackets++;
888*8a324c92SDan McDonald 
889*8a324c92SDan McDonald 		virtio_free_chain(ve);
890*8a324c92SDan McDonald 		mac_rx(sc->sc_mac_handle, NULL, mp);
891*8a324c92SDan McDonald 		i++;
892*8a324c92SDan McDonald 	}
893*8a324c92SDan McDonald 
894*8a324c92SDan McDonald 	return (i);
895*8a324c92SDan McDonald }
896*8a324c92SDan McDonald 
897*8a324c92SDan McDonald static void
vioif_reclaim_used_tx(struct vioif_softc * sc)898*8a324c92SDan McDonald vioif_reclaim_used_tx(struct vioif_softc *sc)
899*8a324c92SDan McDonald {
900*8a324c92SDan McDonald 	struct vq_entry *ve;
901*8a324c92SDan McDonald 	struct vioif_tx_buf *buf;
902*8a324c92SDan McDonald 	uint32_t len;
903*8a324c92SDan McDonald 	mblk_t *mp;
904*8a324c92SDan McDonald 	int i = 0;
905*8a324c92SDan McDonald 
906*8a324c92SDan McDonald 	while ((ve = virtio_pull_chain(sc->sc_tx_vq, &len))) {
907*8a324c92SDan McDonald 		/* We don't chain descriptors for tx, so don't expect any. */
908*8a324c92SDan McDonald 		ASSERT(!ve->qe_next);
909*8a324c92SDan McDonald 
910*8a324c92SDan McDonald 		buf = &sc->sc_txbufs[ve->qe_index];
911*8a324c92SDan McDonald 		mp = buf->tb_mp;
912*8a324c92SDan McDonald 		buf->tb_mp = NULL;
913*8a324c92SDan McDonald 
914*8a324c92SDan McDonald 		if (mp) {
915*8a324c92SDan McDonald 			for (i = 0; i < buf->tb_external_num; i++)
916*8a324c92SDan McDonald 				(void) ddi_dma_unbind_handle(
917*8a324c92SDan McDonald 				    buf->tb_external_mapping[i].vbm_dmah);
918*8a324c92SDan McDonald 		}
919*8a324c92SDan McDonald 
920*8a324c92SDan McDonald 		virtio_free_chain(ve);
921*8a324c92SDan McDonald 
922*8a324c92SDan McDonald 		/* External mapping used, mp was not freed in vioif_send() */
923*8a324c92SDan McDonald 		if (mp)
924*8a324c92SDan McDonald 			freemsg(mp);
925*8a324c92SDan McDonald 		i++;
926*8a324c92SDan McDonald 	}
927*8a324c92SDan McDonald 
928*8a324c92SDan McDonald 	if (sc->sc_tx_stopped && i) {
929*8a324c92SDan McDonald 		sc->sc_tx_stopped = 0;
930*8a324c92SDan McDonald 		mac_tx_update(sc->sc_mac_handle);
931*8a324c92SDan McDonald 	}
932*8a324c92SDan McDonald }
933*8a324c92SDan McDonald 
934*8a324c92SDan McDonald /* sc will be used to update stat counters. */
935*8a324c92SDan McDonald /* ARGSUSED */
936*8a324c92SDan McDonald static inline void
vioif_tx_inline(struct vioif_softc * sc,struct vq_entry * ve,mblk_t * mp,size_t msg_size)937*8a324c92SDan McDonald vioif_tx_inline(struct vioif_softc *sc, struct vq_entry *ve, mblk_t *mp,
938*8a324c92SDan McDonald     size_t msg_size)
939*8a324c92SDan McDonald {
940*8a324c92SDan McDonald 	struct vioif_tx_buf *buf;
941*8a324c92SDan McDonald 	buf = &sc->sc_txbufs[ve->qe_index];
942*8a324c92SDan McDonald 
943*8a324c92SDan McDonald 	ASSERT(buf);
944*8a324c92SDan McDonald 
945*8a324c92SDan McDonald 	/* Frees mp */
946*8a324c92SDan McDonald 	mcopymsg(mp, buf->tb_inline_mapping.vbm_buf +
947*8a324c92SDan McDonald 	    sizeof (struct virtio_net_hdr));
948*8a324c92SDan McDonald 
949*8a324c92SDan McDonald 	virtio_ve_add_indirect_buf(ve,
950*8a324c92SDan McDonald 	    buf->tb_inline_mapping.vbm_dmac.dmac_laddress +
951*8a324c92SDan McDonald 	    sizeof (struct virtio_net_hdr), msg_size, B_TRUE);
952*8a324c92SDan McDonald }
953*8a324c92SDan McDonald 
954*8a324c92SDan McDonald static inline int
vioif_tx_lazy_handle_alloc(struct vioif_softc * sc,struct vioif_tx_buf * buf,int i)955*8a324c92SDan McDonald vioif_tx_lazy_handle_alloc(struct vioif_softc *sc, struct vioif_tx_buf *buf,
956*8a324c92SDan McDonald     int i)
957*8a324c92SDan McDonald {
958*8a324c92SDan McDonald 	int ret = DDI_SUCCESS;
959*8a324c92SDan McDonald 
960*8a324c92SDan McDonald 	if (!buf->tb_external_mapping[i].vbm_dmah) {
961*8a324c92SDan McDonald 		ret = ddi_dma_alloc_handle(sc->sc_dev,
962*8a324c92SDan McDonald 		    &vioif_mapped_buf_dma_attr, DDI_DMA_SLEEP, NULL,
963*8a324c92SDan McDonald 		    &buf->tb_external_mapping[i].vbm_dmah);
964*8a324c92SDan McDonald 		if (ret != DDI_SUCCESS) {
965*8a324c92SDan McDonald 			dev_err(sc->sc_dev, CE_WARN,
966*8a324c92SDan McDonald 			    "Can't allocate dma handle for external tx buffer");
967*8a324c92SDan McDonald 		}
968*8a324c92SDan McDonald 	}
969*8a324c92SDan McDonald 
970*8a324c92SDan McDonald 	return (ret);
971*8a324c92SDan McDonald }
972*8a324c92SDan McDonald 
973*8a324c92SDan McDonald static inline int
vioif_tx_external(struct vioif_softc * sc,struct vq_entry * ve,mblk_t * mp,size_t msg_size)974*8a324c92SDan McDonald vioif_tx_external(struct vioif_softc *sc, struct vq_entry *ve, mblk_t *mp,
975*8a324c92SDan McDonald     size_t msg_size)
976*8a324c92SDan McDonald {
977*8a324c92SDan McDonald 	_NOTE(ARGUNUSED(msg_size));
978*8a324c92SDan McDonald 
979*8a324c92SDan McDonald 	struct vioif_tx_buf *buf;
980*8a324c92SDan McDonald 	mblk_t *nmp;
981*8a324c92SDan McDonald 	int i, j;
982*8a324c92SDan McDonald 	int ret = DDI_SUCCESS;
983*8a324c92SDan McDonald 
984*8a324c92SDan McDonald 	buf = &sc->sc_txbufs[ve->qe_index];
985*8a324c92SDan McDonald 
986*8a324c92SDan McDonald 	ASSERT(buf);
987*8a324c92SDan McDonald 
988*8a324c92SDan McDonald 	buf->tb_external_num = 0;
989*8a324c92SDan McDonald 	i = 0;
990*8a324c92SDan McDonald 	nmp = mp;
991*8a324c92SDan McDonald 
992*8a324c92SDan McDonald 	while (nmp) {
993*8a324c92SDan McDonald 		size_t len;
994*8a324c92SDan McDonald 		ddi_dma_cookie_t dmac;
995*8a324c92SDan McDonald 		unsigned int ncookies;
996*8a324c92SDan McDonald 
997*8a324c92SDan McDonald 		len = MBLKL(nmp);
998*8a324c92SDan McDonald 		/*
999*8a324c92SDan McDonald 		 * For some reason, the network stack can
1000*8a324c92SDan McDonald 		 * actually send us zero-length fragments.
1001*8a324c92SDan McDonald 		 */
1002*8a324c92SDan McDonald 		if (len == 0) {
1003*8a324c92SDan McDonald 			nmp = nmp->b_cont;
1004*8a324c92SDan McDonald 			continue;
1005*8a324c92SDan McDonald 		}
1006*8a324c92SDan McDonald 
1007*8a324c92SDan McDonald 		ret = vioif_tx_lazy_handle_alloc(sc, buf, i);
1008*8a324c92SDan McDonald 		if (ret != DDI_SUCCESS) {
1009*8a324c92SDan McDonald 			sc->sc_notxbuf++;
1010*8a324c92SDan McDonald 			sc->sc_oerrors++;
1011*8a324c92SDan McDonald 			goto exit_lazy_alloc;
1012*8a324c92SDan McDonald 		}
1013*8a324c92SDan McDonald 		ret = ddi_dma_addr_bind_handle(
1014*8a324c92SDan McDonald 		    buf->tb_external_mapping[i].vbm_dmah, NULL,
1015*8a324c92SDan McDonald 		    (caddr_t)nmp->b_rptr, len,
1016*8a324c92SDan McDonald 		    DDI_DMA_WRITE | DDI_DMA_STREAMING,
1017*8a324c92SDan McDonald 		    DDI_DMA_SLEEP, NULL, &dmac, &ncookies);
1018*8a324c92SDan McDonald 
1019*8a324c92SDan McDonald 		if (ret != DDI_SUCCESS) {
1020*8a324c92SDan McDonald 			sc->sc_oerrors++;
1021*8a324c92SDan McDonald 			dev_err(sc->sc_dev, CE_NOTE,
1022*8a324c92SDan McDonald 			    "TX: Failed to bind external handle");
1023*8a324c92SDan McDonald 			goto exit_bind;
1024*8a324c92SDan McDonald 		}
1025*8a324c92SDan McDonald 
1026*8a324c92SDan McDonald 		/* Check if we still fit into the indirect table. */
1027*8a324c92SDan McDonald 		if (virtio_ve_indirect_available(ve) < ncookies) {
1028*8a324c92SDan McDonald 			dev_err(sc->sc_dev, CE_NOTE,
1029*8a324c92SDan McDonald 			    "TX: Indirect descriptor table limit reached."
1030*8a324c92SDan McDonald 			    " It took %d fragments.", i);
1031*8a324c92SDan McDonald 			sc->sc_notxbuf++;
1032*8a324c92SDan McDonald 			sc->sc_oerrors++;
1033*8a324c92SDan McDonald 
1034*8a324c92SDan McDonald 			ret = DDI_FAILURE;
1035*8a324c92SDan McDonald 			goto exit_limit;
1036*8a324c92SDan McDonald 		}
1037*8a324c92SDan McDonald 
1038*8a324c92SDan McDonald 		virtio_ve_add_cookie(ve, buf->tb_external_mapping[i].vbm_dmah,
1039*8a324c92SDan McDonald 		    dmac, ncookies, B_TRUE);
1040*8a324c92SDan McDonald 
1041*8a324c92SDan McDonald 		nmp = nmp->b_cont;
1042*8a324c92SDan McDonald 		i++;
1043*8a324c92SDan McDonald 	}
1044*8a324c92SDan McDonald 
1045*8a324c92SDan McDonald 	buf->tb_external_num = i;
1046*8a324c92SDan McDonald 	/* Save the mp to free it when the packet is sent. */
1047*8a324c92SDan McDonald 	buf->tb_mp = mp;
1048*8a324c92SDan McDonald 
1049*8a324c92SDan McDonald 	return (DDI_SUCCESS);
1050*8a324c92SDan McDonald 
1051*8a324c92SDan McDonald exit_limit:
1052*8a324c92SDan McDonald exit_bind:
1053*8a324c92SDan McDonald exit_lazy_alloc:
1054*8a324c92SDan McDonald 
1055*8a324c92SDan McDonald 	for (j = 0; j < i; j++) {
1056*8a324c92SDan McDonald 		(void) ddi_dma_unbind_handle(
1057*8a324c92SDan McDonald 		    buf->tb_external_mapping[j].vbm_dmah);
1058*8a324c92SDan McDonald 	}
1059*8a324c92SDan McDonald 
1060*8a324c92SDan McDonald 	return (ret);
1061*8a324c92SDan McDonald }
1062*8a324c92SDan McDonald 
1063*8a324c92SDan McDonald static boolean_t
vioif_send(struct vioif_softc * sc,mblk_t * mp)1064*8a324c92SDan McDonald vioif_send(struct vioif_softc *sc, mblk_t *mp)
1065*8a324c92SDan McDonald {
1066*8a324c92SDan McDonald 	struct vq_entry *ve;
1067*8a324c92SDan McDonald 	struct vioif_tx_buf *buf;
1068*8a324c92SDan McDonald 	struct virtio_net_hdr *net_header = NULL;
1069*8a324c92SDan McDonald 	size_t msg_size = 0;
1070*8a324c92SDan McDonald 	uint32_t csum_start;
1071*8a324c92SDan McDonald 	uint32_t csum_stuff;
1072*8a324c92SDan McDonald 	uint32_t csum_flags;
1073*8a324c92SDan McDonald 	uint32_t lso_flags;
1074*8a324c92SDan McDonald 	uint32_t lso_mss;
1075*8a324c92SDan McDonald 	mblk_t *nmp;
1076*8a324c92SDan McDonald 	int ret;
1077*8a324c92SDan McDonald 	boolean_t lso_required = B_FALSE;
1078*8a324c92SDan McDonald 
1079*8a324c92SDan McDonald 	for (nmp = mp; nmp; nmp = nmp->b_cont)
1080*8a324c92SDan McDonald 		msg_size += MBLKL(nmp);
1081*8a324c92SDan McDonald 
1082*8a324c92SDan McDonald 	if (sc->sc_tx_tso4) {
1083*8a324c92SDan McDonald 		mac_lso_get(mp, &lso_mss, &lso_flags);
1084*8a324c92SDan McDonald 		lso_required = (lso_flags & HW_LSO);
1085*8a324c92SDan McDonald 	}
1086*8a324c92SDan McDonald 
1087*8a324c92SDan McDonald 	ve = vq_alloc_entry(sc->sc_tx_vq);
1088*8a324c92SDan McDonald 
1089*8a324c92SDan McDonald 	if (!ve) {
1090*8a324c92SDan McDonald 		sc->sc_notxbuf++;
1091*8a324c92SDan McDonald 		/* Out of free descriptors - try later. */
1092*8a324c92SDan McDonald 		return (B_FALSE);
1093*8a324c92SDan McDonald 	}
1094*8a324c92SDan McDonald 	buf = &sc->sc_txbufs[ve->qe_index];
1095*8a324c92SDan McDonald 
1096*8a324c92SDan McDonald 	/* Use the inline buffer of the first entry for the virtio_net_hdr. */
1097*8a324c92SDan McDonald 	(void) memset(buf->tb_inline_mapping.vbm_buf, 0,
1098*8a324c92SDan McDonald 	    sizeof (struct virtio_net_hdr));
1099*8a324c92SDan McDonald 
1100*8a324c92SDan McDonald 	net_header = (struct virtio_net_hdr *)buf->tb_inline_mapping.vbm_buf;
1101*8a324c92SDan McDonald 
1102*8a324c92SDan McDonald 	mac_hcksum_get(mp, &csum_start, &csum_stuff, NULL,
1103*8a324c92SDan McDonald 	    NULL, &csum_flags);
1104*8a324c92SDan McDonald 
1105*8a324c92SDan McDonald 	/* They want us to do the TCP/UDP csum calculation. */
1106*8a324c92SDan McDonald 	if (csum_flags & HCK_PARTIALCKSUM) {
1107*8a324c92SDan McDonald 		struct ether_header *eth_header;
1108*8a324c92SDan McDonald 		int eth_hsize;
1109*8a324c92SDan McDonald 
1110*8a324c92SDan McDonald 		/* Did we ask for it? */
1111*8a324c92SDan McDonald 		ASSERT(sc->sc_tx_csum);
1112*8a324c92SDan McDonald 
1113*8a324c92SDan McDonald 		/* We only asked for partial csum packets. */
1114*8a324c92SDan McDonald 		ASSERT(!(csum_flags & HCK_IPV4_HDRCKSUM));
1115*8a324c92SDan McDonald 		ASSERT(!(csum_flags & HCK_FULLCKSUM));
1116*8a324c92SDan McDonald 
1117*8a324c92SDan McDonald 		eth_header = (void *) mp->b_rptr;
1118*8a324c92SDan McDonald 		if (eth_header->ether_type == htons(ETHERTYPE_VLAN)) {
1119*8a324c92SDan McDonald 			eth_hsize = sizeof (struct ether_vlan_header);
1120*8a324c92SDan McDonald 		} else {
1121*8a324c92SDan McDonald 			eth_hsize = sizeof (struct ether_header);
1122*8a324c92SDan McDonald 		}
1123*8a324c92SDan McDonald 		net_header->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
1124*8a324c92SDan McDonald 		net_header->csum_start = eth_hsize + csum_start;
1125*8a324c92SDan McDonald 		net_header->csum_offset = csum_stuff - csum_start;
1126*8a324c92SDan McDonald 	}
1127*8a324c92SDan McDonald 
1128*8a324c92SDan McDonald 	/* setup LSO fields if required */
1129*8a324c92SDan McDonald 	if (lso_required) {
1130*8a324c92SDan McDonald 		net_header->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
1131*8a324c92SDan McDonald 		net_header->gso_size = (uint16_t)lso_mss;
1132*8a324c92SDan McDonald 	}
1133*8a324c92SDan McDonald 
1134*8a324c92SDan McDonald 	virtio_ve_add_indirect_buf(ve,
1135*8a324c92SDan McDonald 	    buf->tb_inline_mapping.vbm_dmac.dmac_laddress,
1136*8a324c92SDan McDonald 	    sizeof (struct virtio_net_hdr), B_TRUE);
1137*8a324c92SDan McDonald 
1138*8a324c92SDan McDonald 	/* meanwhile update the statistic */
1139*8a324c92SDan McDonald 	if (mp->b_rptr[0] & 0x1) {
1140*8a324c92SDan McDonald 		if (bcmp(mp->b_rptr, vioif_broadcast, ETHERADDRL) != 0)
1141*8a324c92SDan McDonald 				sc->sc_multixmt++;
1142*8a324c92SDan McDonald 			else
1143*8a324c92SDan McDonald 				sc->sc_brdcstxmt++;
1144*8a324c92SDan McDonald 	}
1145*8a324c92SDan McDonald 
1146*8a324c92SDan McDonald 	/*
1147*8a324c92SDan McDonald 	 * We copy small packets into the inline buffer. The bigger ones
1148*8a324c92SDan McDonald 	 * get mapped using the mapped buffer.
1149*8a324c92SDan McDonald 	 */
1150*8a324c92SDan McDonald 	if (msg_size < sc->sc_txcopy_thresh) {
1151*8a324c92SDan McDonald 		vioif_tx_inline(sc, ve, mp, msg_size);
1152*8a324c92SDan McDonald 	} else {
1153*8a324c92SDan McDonald 		/* statistic gets updated by vioif_tx_external when fail */
1154*8a324c92SDan McDonald 		ret = vioif_tx_external(sc, ve, mp, msg_size);
1155*8a324c92SDan McDonald 		if (ret != DDI_SUCCESS)
1156*8a324c92SDan McDonald 			goto exit_tx_external;
1157*8a324c92SDan McDonald 	}
1158*8a324c92SDan McDonald 
1159*8a324c92SDan McDonald 	virtio_push_chain(ve, B_TRUE);
1160*8a324c92SDan McDonald 
1161*8a324c92SDan McDonald 	sc->sc_opackets++;
1162*8a324c92SDan McDonald 	sc->sc_obytes += msg_size;
1163*8a324c92SDan McDonald 
1164*8a324c92SDan McDonald 	return (B_TRUE);
1165*8a324c92SDan McDonald 
1166*8a324c92SDan McDonald exit_tx_external:
1167*8a324c92SDan McDonald 
1168*8a324c92SDan McDonald 	vq_free_entry(sc->sc_tx_vq, ve);
1169*8a324c92SDan McDonald 	/*
1170*8a324c92SDan McDonald 	 * vioif_tx_external can fail when the buffer does not fit into the
1171*8a324c92SDan McDonald 	 * indirect descriptor table. Free the mp. I don't expect this ever
1172*8a324c92SDan McDonald 	 * to happen.
1173*8a324c92SDan McDonald 	 */
1174*8a324c92SDan McDonald 	freemsg(mp);
1175*8a324c92SDan McDonald 
1176*8a324c92SDan McDonald 	return (B_TRUE);
1177*8a324c92SDan McDonald }
1178*8a324c92SDan McDonald 
1179*8a324c92SDan McDonald mblk_t *
vioif_tx(void * arg,mblk_t * mp)1180*8a324c92SDan McDonald vioif_tx(void *arg, mblk_t *mp)
1181*8a324c92SDan McDonald {
1182*8a324c92SDan McDonald 	struct vioif_softc *sc = arg;
1183*8a324c92SDan McDonald 	mblk_t	*nmp;
1184*8a324c92SDan McDonald 
1185*8a324c92SDan McDonald 	while (mp != NULL) {
1186*8a324c92SDan McDonald 		nmp = mp->b_next;
1187*8a324c92SDan McDonald 		mp->b_next = NULL;
1188*8a324c92SDan McDonald 
1189*8a324c92SDan McDonald 		if (!vioif_send(sc, mp)) {
1190*8a324c92SDan McDonald 			sc->sc_tx_stopped = 1;
1191*8a324c92SDan McDonald 			mp->b_next = nmp;
1192*8a324c92SDan McDonald 			break;
1193*8a324c92SDan McDonald 		}
1194*8a324c92SDan McDonald 		mp = nmp;
1195*8a324c92SDan McDonald 	}
1196*8a324c92SDan McDonald 
1197*8a324c92SDan McDonald 	return (mp);
1198*8a324c92SDan McDonald }
1199*8a324c92SDan McDonald 
1200*8a324c92SDan McDonald int
vioif_start(void * arg)1201*8a324c92SDan McDonald vioif_start(void *arg)
1202*8a324c92SDan McDonald {
1203*8a324c92SDan McDonald 	struct vioif_softc *sc = arg;
1204*8a324c92SDan McDonald 
1205*8a324c92SDan McDonald 	mac_link_update(sc->sc_mac_handle,
1206*8a324c92SDan McDonald 	    vioif_link_state(sc));
1207*8a324c92SDan McDonald 
1208*8a324c92SDan McDonald 	virtio_start_vq_intr(sc->sc_rx_vq);
1209*8a324c92SDan McDonald 
1210*8a324c92SDan McDonald 	return (DDI_SUCCESS);
1211*8a324c92SDan McDonald }
1212*8a324c92SDan McDonald 
1213*8a324c92SDan McDonald void
vioif_stop(void * arg)1214*8a324c92SDan McDonald vioif_stop(void *arg)
1215*8a324c92SDan McDonald {
1216*8a324c92SDan McDonald 	struct vioif_softc *sc = arg;
1217*8a324c92SDan McDonald 
1218*8a324c92SDan McDonald 	virtio_stop_vq_intr(sc->sc_rx_vq);
1219*8a324c92SDan McDonald }
1220*8a324c92SDan McDonald 
1221*8a324c92SDan McDonald /* ARGSUSED */
1222*8a324c92SDan McDonald static int
vioif_stat(void * arg,uint_t stat,uint64_t * val)1223*8a324c92SDan McDonald vioif_stat(void *arg, uint_t stat, uint64_t *val)
1224*8a324c92SDan McDonald {
1225*8a324c92SDan McDonald 	struct vioif_softc *sc = arg;
1226*8a324c92SDan McDonald 
1227*8a324c92SDan McDonald 	switch (stat) {
1228*8a324c92SDan McDonald 	case MAC_STAT_IERRORS:
1229*8a324c92SDan McDonald 		*val = sc->sc_ierrors;
1230*8a324c92SDan McDonald 		break;
1231*8a324c92SDan McDonald 	case MAC_STAT_OERRORS:
1232*8a324c92SDan McDonald 		*val = sc->sc_oerrors;
1233*8a324c92SDan McDonald 		break;
1234*8a324c92SDan McDonald 	case MAC_STAT_MULTIRCV:
1235*8a324c92SDan McDonald 		*val = sc->sc_multircv;
1236*8a324c92SDan McDonald 		break;
1237*8a324c92SDan McDonald 	case MAC_STAT_BRDCSTRCV:
1238*8a324c92SDan McDonald 		*val = sc->sc_brdcstrcv;
1239*8a324c92SDan McDonald 		break;
1240*8a324c92SDan McDonald 	case MAC_STAT_MULTIXMT:
1241*8a324c92SDan McDonald 		*val = sc->sc_multixmt;
1242*8a324c92SDan McDonald 		break;
1243*8a324c92SDan McDonald 	case MAC_STAT_BRDCSTXMT:
1244*8a324c92SDan McDonald 		*val = sc->sc_brdcstxmt;
1245*8a324c92SDan McDonald 		break;
1246*8a324c92SDan McDonald 	case MAC_STAT_IPACKETS:
1247*8a324c92SDan McDonald 		*val = sc->sc_ipackets;
1248*8a324c92SDan McDonald 		break;
1249*8a324c92SDan McDonald 	case MAC_STAT_RBYTES:
1250*8a324c92SDan McDonald 		*val = sc->sc_rbytes;
1251*8a324c92SDan McDonald 		break;
1252*8a324c92SDan McDonald 	case MAC_STAT_OPACKETS:
1253*8a324c92SDan McDonald 		*val = sc->sc_opackets;
1254*8a324c92SDan McDonald 		break;
1255*8a324c92SDan McDonald 	case MAC_STAT_OBYTES:
1256*8a324c92SDan McDonald 		*val = sc->sc_obytes;
1257*8a324c92SDan McDonald 		break;
1258*8a324c92SDan McDonald 	case MAC_STAT_NORCVBUF:
1259*8a324c92SDan McDonald 		*val = sc->sc_norecvbuf;
1260*8a324c92SDan McDonald 		break;
1261*8a324c92SDan McDonald 	case MAC_STAT_NOXMTBUF:
1262*8a324c92SDan McDonald 		*val = sc->sc_notxbuf;
1263*8a324c92SDan McDonald 		break;
1264*8a324c92SDan McDonald 	case MAC_STAT_IFSPEED:
1265*8a324c92SDan McDonald 		/* always 1 Gbit */
1266*8a324c92SDan McDonald 		*val = 1000000000ULL;
1267*8a324c92SDan McDonald 		break;
1268*8a324c92SDan McDonald 	case ETHER_STAT_LINK_DUPLEX:
1269*8a324c92SDan McDonald 		/* virtual device, always full-duplex */
1270*8a324c92SDan McDonald 		*val = LINK_DUPLEX_FULL;
1271*8a324c92SDan McDonald 		break;
1272*8a324c92SDan McDonald 
1273*8a324c92SDan McDonald 	default:
1274*8a324c92SDan McDonald 		return (ENOTSUP);
1275*8a324c92SDan McDonald 	}
1276*8a324c92SDan McDonald 
1277*8a324c92SDan McDonald 	return (DDI_SUCCESS);
1278*8a324c92SDan McDonald }
1279*8a324c92SDan McDonald 
1280*8a324c92SDan McDonald static int
vioif_set_prop_private(struct vioif_softc * sc,const char * pr_name,uint_t pr_valsize,const void * pr_val)1281*8a324c92SDan McDonald vioif_set_prop_private(struct vioif_softc *sc, const char *pr_name,
1282*8a324c92SDan McDonald     uint_t pr_valsize, const void *pr_val)
1283*8a324c92SDan McDonald {
1284*8a324c92SDan McDonald 	_NOTE(ARGUNUSED(pr_valsize));
1285*8a324c92SDan McDonald 
1286*8a324c92SDan McDonald 	long result;
1287*8a324c92SDan McDonald 
1288*8a324c92SDan McDonald 	if (strcmp(pr_name, vioif_txcopy_thresh) == 0) {
1289*8a324c92SDan McDonald 
1290*8a324c92SDan McDonald 		if (pr_val == NULL)
1291*8a324c92SDan McDonald 			return (EINVAL);
1292*8a324c92SDan McDonald 
1293*8a324c92SDan McDonald 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
1294*8a324c92SDan McDonald 
1295*8a324c92SDan McDonald 		if (result < 0 || result > VIOIF_TX_THRESH_MAX)
1296*8a324c92SDan McDonald 			return (EINVAL);
1297*8a324c92SDan McDonald 		sc->sc_txcopy_thresh = result;
1298*8a324c92SDan McDonald 	}
1299*8a324c92SDan McDonald 	if (strcmp(pr_name, vioif_rxcopy_thresh) == 0) {
1300*8a324c92SDan McDonald 
1301*8a324c92SDan McDonald 		if (pr_val == NULL)
1302*8a324c92SDan McDonald 			return (EINVAL);
1303*8a324c92SDan McDonald 
1304*8a324c92SDan McDonald 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
1305*8a324c92SDan McDonald 
1306*8a324c92SDan McDonald 		if (result < 0 || result > VIOIF_RX_THRESH_MAX)
1307*8a324c92SDan McDonald 			return (EINVAL);
1308*8a324c92SDan McDonald 		sc->sc_rxcopy_thresh = result;
1309*8a324c92SDan McDonald 	}
1310*8a324c92SDan McDonald 	return (0);
1311*8a324c92SDan McDonald }
1312*8a324c92SDan McDonald 
1313*8a324c92SDan McDonald static int
vioif_setprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)1314*8a324c92SDan McDonald vioif_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1315*8a324c92SDan McDonald     uint_t pr_valsize, const void *pr_val)
1316*8a324c92SDan McDonald {
1317*8a324c92SDan McDonald 	struct vioif_softc *sc = arg;
1318*8a324c92SDan McDonald 	const uint32_t *new_mtu;
1319*8a324c92SDan McDonald 	int err;
1320*8a324c92SDan McDonald 
1321*8a324c92SDan McDonald 	switch (pr_num) {
1322*8a324c92SDan McDonald 	case MAC_PROP_MTU:
1323*8a324c92SDan McDonald 		new_mtu = pr_val;
1324*8a324c92SDan McDonald 
1325*8a324c92SDan McDonald 		if (*new_mtu > MAX_MTU) {
1326*8a324c92SDan McDonald 			return (EINVAL);
1327*8a324c92SDan McDonald 		}
1328*8a324c92SDan McDonald 
1329*8a324c92SDan McDonald 		err = mac_maxsdu_update(sc->sc_mac_handle, *new_mtu);
1330*8a324c92SDan McDonald 		if (err) {
1331*8a324c92SDan McDonald 			return (err);
1332*8a324c92SDan McDonald 		}
1333*8a324c92SDan McDonald 		break;
1334*8a324c92SDan McDonald 	case MAC_PROP_PRIVATE:
1335*8a324c92SDan McDonald 		err = vioif_set_prop_private(sc, pr_name,
1336*8a324c92SDan McDonald 		    pr_valsize, pr_val);
1337*8a324c92SDan McDonald 		if (err)
1338*8a324c92SDan McDonald 			return (err);
1339*8a324c92SDan McDonald 		break;
1340*8a324c92SDan McDonald 	default:
1341*8a324c92SDan McDonald 		return (ENOTSUP);
1342*8a324c92SDan McDonald 	}
1343*8a324c92SDan McDonald 
1344*8a324c92SDan McDonald 	return (0);
1345*8a324c92SDan McDonald }
1346*8a324c92SDan McDonald 
1347*8a324c92SDan McDonald static int
vioif_get_prop_private(struct vioif_softc * sc,const char * pr_name,uint_t pr_valsize,void * pr_val)1348*8a324c92SDan McDonald vioif_get_prop_private(struct vioif_softc *sc, const char *pr_name,
1349*8a324c92SDan McDonald     uint_t pr_valsize, void *pr_val)
1350*8a324c92SDan McDonald {
1351*8a324c92SDan McDonald 	int err = ENOTSUP;
1352*8a324c92SDan McDonald 	int value;
1353*8a324c92SDan McDonald 
1354*8a324c92SDan McDonald 	if (strcmp(pr_name, vioif_txcopy_thresh) == 0) {
1355*8a324c92SDan McDonald 
1356*8a324c92SDan McDonald 		value = sc->sc_txcopy_thresh;
1357*8a324c92SDan McDonald 		err = 0;
1358*8a324c92SDan McDonald 		goto done;
1359*8a324c92SDan McDonald 	}
1360*8a324c92SDan McDonald 	if (strcmp(pr_name, vioif_rxcopy_thresh) == 0) {
1361*8a324c92SDan McDonald 
1362*8a324c92SDan McDonald 		value = sc->sc_rxcopy_thresh;
1363*8a324c92SDan McDonald 		err = 0;
1364*8a324c92SDan McDonald 		goto done;
1365*8a324c92SDan McDonald 	}
1366*8a324c92SDan McDonald done:
1367*8a324c92SDan McDonald 	if (err == 0) {
1368*8a324c92SDan McDonald 		(void) snprintf(pr_val, pr_valsize, "%d", value);
1369*8a324c92SDan McDonald 	}
1370*8a324c92SDan McDonald 	return (err);
1371*8a324c92SDan McDonald }
1372*8a324c92SDan McDonald 
1373*8a324c92SDan McDonald static int
vioif_getprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)1374*8a324c92SDan McDonald vioif_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1375*8a324c92SDan McDonald     uint_t pr_valsize, void *pr_val)
1376*8a324c92SDan McDonald {
1377*8a324c92SDan McDonald 	struct vioif_softc *sc = arg;
1378*8a324c92SDan McDonald 	int err = ENOTSUP;
1379*8a324c92SDan McDonald 
1380*8a324c92SDan McDonald 	switch (pr_num) {
1381*8a324c92SDan McDonald 	case MAC_PROP_PRIVATE:
1382*8a324c92SDan McDonald 		err = vioif_get_prop_private(sc, pr_name,
1383*8a324c92SDan McDonald 		    pr_valsize, pr_val);
1384*8a324c92SDan McDonald 		break;
1385*8a324c92SDan McDonald 	default:
1386*8a324c92SDan McDonald 		break;
1387*8a324c92SDan McDonald 	}
1388*8a324c92SDan McDonald 	return (err);
1389*8a324c92SDan McDonald }
1390*8a324c92SDan McDonald 
1391*8a324c92SDan McDonald static void
vioif_propinfo(void * arg,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)1392*8a324c92SDan McDonald vioif_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1393*8a324c92SDan McDonald     mac_prop_info_handle_t prh)
1394*8a324c92SDan McDonald {
1395*8a324c92SDan McDonald 	struct vioif_softc *sc = arg;
1396*8a324c92SDan McDonald 	char valstr[64];
1397*8a324c92SDan McDonald 	int value;
1398*8a324c92SDan McDonald 
1399*8a324c92SDan McDonald 	switch (pr_num) {
1400*8a324c92SDan McDonald 	case MAC_PROP_MTU:
1401*8a324c92SDan McDonald 		mac_prop_info_set_range_uint32(prh, ETHERMIN, MAX_MTU);
1402*8a324c92SDan McDonald 		break;
1403*8a324c92SDan McDonald 
1404*8a324c92SDan McDonald 	case MAC_PROP_PRIVATE:
1405*8a324c92SDan McDonald 		bzero(valstr, sizeof (valstr));
1406*8a324c92SDan McDonald 		if (strcmp(pr_name, vioif_txcopy_thresh) == 0) {
1407*8a324c92SDan McDonald 
1408*8a324c92SDan McDonald 			value = sc->sc_txcopy_thresh;
1409*8a324c92SDan McDonald 		} else	if (strcmp(pr_name,
1410*8a324c92SDan McDonald 		    vioif_rxcopy_thresh) == 0) {
1411*8a324c92SDan McDonald 			value = sc->sc_rxcopy_thresh;
1412*8a324c92SDan McDonald 		} else {
1413*8a324c92SDan McDonald 			return;
1414*8a324c92SDan McDonald 		}
1415*8a324c92SDan McDonald 		(void) snprintf(valstr, sizeof (valstr), "%d", value);
1416*8a324c92SDan McDonald 		break;
1417*8a324c92SDan McDonald 
1418*8a324c92SDan McDonald 	default:
1419*8a324c92SDan McDonald 		break;
1420*8a324c92SDan McDonald 	}
1421*8a324c92SDan McDonald }
1422*8a324c92SDan McDonald 
1423*8a324c92SDan McDonald static boolean_t
vioif_getcapab(void * arg,mac_capab_t cap,void * cap_data)1424*8a324c92SDan McDonald vioif_getcapab(void *arg, mac_capab_t cap, void *cap_data)
1425*8a324c92SDan McDonald {
1426*8a324c92SDan McDonald 	struct vioif_softc *sc = arg;
1427*8a324c92SDan McDonald 
1428*8a324c92SDan McDonald 	switch (cap) {
1429*8a324c92SDan McDonald 	case MAC_CAPAB_HCKSUM:
1430*8a324c92SDan McDonald 		if (sc->sc_tx_csum) {
1431*8a324c92SDan McDonald 			uint32_t *txflags = cap_data;
1432*8a324c92SDan McDonald 
1433*8a324c92SDan McDonald 			*txflags = HCKSUM_INET_PARTIAL;
1434*8a324c92SDan McDonald 			return (B_TRUE);
1435*8a324c92SDan McDonald 		}
1436*8a324c92SDan McDonald 		return (B_FALSE);
1437*8a324c92SDan McDonald 	case MAC_CAPAB_LSO:
1438*8a324c92SDan McDonald 		if (sc->sc_tx_tso4) {
1439*8a324c92SDan McDonald 			mac_capab_lso_t *cap_lso = cap_data;
1440*8a324c92SDan McDonald 
1441*8a324c92SDan McDonald 			cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
1442*8a324c92SDan McDonald 			cap_lso->lso_basic_tcp_ipv4.lso_max = MAX_MTU;
1443*8a324c92SDan McDonald 			return (B_TRUE);
1444*8a324c92SDan McDonald 		}
1445*8a324c92SDan McDonald 		return (B_FALSE);
1446*8a324c92SDan McDonald 	default:
1447*8a324c92SDan McDonald 		break;
1448*8a324c92SDan McDonald 	}
1449*8a324c92SDan McDonald 	return (B_FALSE);
1450*8a324c92SDan McDonald }
1451*8a324c92SDan McDonald 
1452*8a324c92SDan McDonald static mac_callbacks_t vioif_m_callbacks = {
1453*8a324c92SDan McDonald 	.mc_callbacks	= (MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO),
1454*8a324c92SDan McDonald 	.mc_getstat	= vioif_stat,
1455*8a324c92SDan McDonald 	.mc_start	= vioif_start,
1456*8a324c92SDan McDonald 	.mc_stop	= vioif_stop,
1457*8a324c92SDan McDonald 	.mc_setpromisc	= vioif_promisc,
1458*8a324c92SDan McDonald 	.mc_multicst	= vioif_multicst,
1459*8a324c92SDan McDonald 	.mc_unicst	= vioif_unicst,
1460*8a324c92SDan McDonald 	.mc_tx		= vioif_tx,
1461*8a324c92SDan McDonald 	/* Optional callbacks */
1462*8a324c92SDan McDonald 	.mc_reserved	= NULL,		/* reserved */
1463*8a324c92SDan McDonald 	.mc_ioctl	= NULL,		/* mc_ioctl */
1464*8a324c92SDan McDonald 	.mc_getcapab	= vioif_getcapab,		/* mc_getcapab */
1465*8a324c92SDan McDonald 	.mc_open	= NULL,		/* mc_open */
1466*8a324c92SDan McDonald 	.mc_close	= NULL,		/* mc_close */
1467*8a324c92SDan McDonald 	.mc_setprop	= vioif_setprop,
1468*8a324c92SDan McDonald 	.mc_getprop	= vioif_getprop,
1469*8a324c92SDan McDonald 	.mc_propinfo	= vioif_propinfo,
1470*8a324c92SDan McDonald };
1471*8a324c92SDan McDonald 
1472*8a324c92SDan McDonald static void
vioif_show_features(struct vioif_softc * sc,const char * prefix,uint32_t features)1473*8a324c92SDan McDonald vioif_show_features(struct vioif_softc *sc, const char *prefix,
1474*8a324c92SDan McDonald     uint32_t features)
1475*8a324c92SDan McDonald {
1476*8a324c92SDan McDonald 	char buf[512];
1477*8a324c92SDan McDonald 	char *bufp = buf;
1478*8a324c92SDan McDonald 	char *bufend = buf + sizeof (buf);
1479*8a324c92SDan McDonald 
1480*8a324c92SDan McDonald 	/* LINTED E_PTRDIFF_OVERFLOW */
1481*8a324c92SDan McDonald 	bufp += snprintf(bufp, bufend - bufp, prefix);
1482*8a324c92SDan McDonald 	/* LINTED E_PTRDIFF_OVERFLOW */
1483*8a324c92SDan McDonald 	bufp += virtio_show_features(features, bufp, bufend - bufp);
1484*8a324c92SDan McDonald 	*bufp = '\0';
1485*8a324c92SDan McDonald 
1486*8a324c92SDan McDonald 
1487*8a324c92SDan McDonald 	/* Using '!' to only CE_NOTE this to the system log. */
1488*8a324c92SDan McDonald 	dev_err(sc->sc_dev, CE_NOTE, "!%s Vioif (%b)", buf, features,
1489*8a324c92SDan McDonald 	    VIRTIO_NET_FEATURE_BITS);
1490*8a324c92SDan McDonald }
1491*8a324c92SDan McDonald 
1492*8a324c92SDan McDonald /*
1493*8a324c92SDan McDonald  * Find out which features are supported by the device and
1494*8a324c92SDan McDonald  * choose which ones we wish to use.
1495*8a324c92SDan McDonald  */
1496*8a324c92SDan McDonald static int
vioif_dev_features(struct vioif_softc * sc)1497*8a324c92SDan McDonald vioif_dev_features(struct vioif_softc *sc)
1498*8a324c92SDan McDonald {
1499*8a324c92SDan McDonald 	uint32_t host_features;
1500*8a324c92SDan McDonald 
1501*8a324c92SDan McDonald 	host_features = virtio_negotiate_features(&sc->sc_virtio,
1502*8a324c92SDan McDonald 	    VIRTIO_NET_F_CSUM |
1503*8a324c92SDan McDonald 	    VIRTIO_NET_F_HOST_TSO4 |
1504*8a324c92SDan McDonald 	    VIRTIO_NET_F_HOST_ECN |
1505*8a324c92SDan McDonald 	    VIRTIO_NET_F_MAC |
1506*8a324c92SDan McDonald 	    VIRTIO_NET_F_STATUS |
1507*8a324c92SDan McDonald 	    VIRTIO_F_RING_INDIRECT_DESC |
1508*8a324c92SDan McDonald 	    VIRTIO_F_NOTIFY_ON_EMPTY);
1509*8a324c92SDan McDonald 
1510*8a324c92SDan McDonald 	vioif_show_features(sc, "Host features: ", host_features);
1511*8a324c92SDan McDonald 	vioif_show_features(sc, "Negotiated features: ",
1512*8a324c92SDan McDonald 	    sc->sc_virtio.sc_features);
1513*8a324c92SDan McDonald 
1514*8a324c92SDan McDonald 	if (!(sc->sc_virtio.sc_features & VIRTIO_F_RING_INDIRECT_DESC)) {
1515*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_NOTE,
1516*8a324c92SDan McDonald 		    "Host does not support RING_INDIRECT_DESC, bye.");
1517*8a324c92SDan McDonald 		return (DDI_FAILURE);
1518*8a324c92SDan McDonald 	}
1519*8a324c92SDan McDonald 
1520*8a324c92SDan McDonald 	return (DDI_SUCCESS);
1521*8a324c92SDan McDonald }
1522*8a324c92SDan McDonald 
1523*8a324c92SDan McDonald static int
vioif_has_feature(struct vioif_softc * sc,uint32_t feature)1524*8a324c92SDan McDonald vioif_has_feature(struct vioif_softc *sc, uint32_t feature)
1525*8a324c92SDan McDonald {
1526*8a324c92SDan McDonald 	return (virtio_has_feature(&sc->sc_virtio, feature));
1527*8a324c92SDan McDonald }
1528*8a324c92SDan McDonald 
1529*8a324c92SDan McDonald static void
vioif_set_mac(struct vioif_softc * sc)1530*8a324c92SDan McDonald vioif_set_mac(struct vioif_softc *sc)
1531*8a324c92SDan McDonald {
1532*8a324c92SDan McDonald 	int i;
1533*8a324c92SDan McDonald 
1534*8a324c92SDan McDonald 	for (i = 0; i < ETHERADDRL; i++) {
1535*8a324c92SDan McDonald 		virtio_write_device_config_1(&sc->sc_virtio,
1536*8a324c92SDan McDonald 		    VIRTIO_NET_CONFIG_MAC + i, sc->sc_mac[i]);
1537*8a324c92SDan McDonald 	}
1538*8a324c92SDan McDonald }
1539*8a324c92SDan McDonald 
1540*8a324c92SDan McDonald /* Get the mac address out of the hardware, or make up one. */
1541*8a324c92SDan McDonald static void
vioif_get_mac(struct vioif_softc * sc)1542*8a324c92SDan McDonald vioif_get_mac(struct vioif_softc *sc)
1543*8a324c92SDan McDonald {
1544*8a324c92SDan McDonald 	int i;
1545*8a324c92SDan McDonald 	if (sc->sc_virtio.sc_features & VIRTIO_NET_F_MAC) {
1546*8a324c92SDan McDonald 		for (i = 0; i < ETHERADDRL; i++) {
1547*8a324c92SDan McDonald 			sc->sc_mac[i] = virtio_read_device_config_1(
1548*8a324c92SDan McDonald 			    &sc->sc_virtio,
1549*8a324c92SDan McDonald 			    VIRTIO_NET_CONFIG_MAC + i);
1550*8a324c92SDan McDonald 		}
1551*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_NOTE, "Got MAC address from host: %s",
1552*8a324c92SDan McDonald 		    ether_sprintf((struct ether_addr *)sc->sc_mac));
1553*8a324c92SDan McDonald 	} else {
1554*8a324c92SDan McDonald 		/* Get a few random bytes */
1555*8a324c92SDan McDonald 		(void) random_get_pseudo_bytes(sc->sc_mac, ETHERADDRL);
1556*8a324c92SDan McDonald 		/* Make sure it's a unicast MAC */
1557*8a324c92SDan McDonald 		sc->sc_mac[0] &= ~1;
1558*8a324c92SDan McDonald 		/* Set the "locally administered" bit */
1559*8a324c92SDan McDonald 		sc->sc_mac[1] |= 2;
1560*8a324c92SDan McDonald 
1561*8a324c92SDan McDonald 		vioif_set_mac(sc);
1562*8a324c92SDan McDonald 
1563*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_NOTE,
1564*8a324c92SDan McDonald 		    "Generated a random MAC address: %s",
1565*8a324c92SDan McDonald 		    ether_sprintf((struct ether_addr *)sc->sc_mac));
1566*8a324c92SDan McDonald 	}
1567*8a324c92SDan McDonald }
1568*8a324c92SDan McDonald 
1569*8a324c92SDan McDonald /*
1570*8a324c92SDan McDonald  * Virtqueue interrupt handlers
1571*8a324c92SDan McDonald  */
1572*8a324c92SDan McDonald /* ARGSUSED */
1573*8a324c92SDan McDonald uint_t
vioif_rx_handler(caddr_t arg1,caddr_t arg2)1574*8a324c92SDan McDonald vioif_rx_handler(caddr_t arg1, caddr_t arg2)
1575*8a324c92SDan McDonald {
1576*8a324c92SDan McDonald 	struct virtio_softc *vsc = (void *) arg1;
1577*8a324c92SDan McDonald 	struct vioif_softc *sc = container_of(vsc,
1578*8a324c92SDan McDonald 	    struct vioif_softc, sc_virtio);
1579*8a324c92SDan McDonald 
1580*8a324c92SDan McDonald 	(void) vioif_process_rx(sc);
1581*8a324c92SDan McDonald 
1582*8a324c92SDan McDonald 	(void) vioif_populate_rx(sc, KM_NOSLEEP);
1583*8a324c92SDan McDonald 
1584*8a324c92SDan McDonald 	return (DDI_INTR_CLAIMED);
1585*8a324c92SDan McDonald }
1586*8a324c92SDan McDonald 
1587*8a324c92SDan McDonald /* ARGSUSED */
1588*8a324c92SDan McDonald uint_t
vioif_tx_handler(caddr_t arg1,caddr_t arg2)1589*8a324c92SDan McDonald vioif_tx_handler(caddr_t arg1, caddr_t arg2)
1590*8a324c92SDan McDonald {
1591*8a324c92SDan McDonald 	struct virtio_softc *vsc = (void *)arg1;
1592*8a324c92SDan McDonald 	struct vioif_softc *sc = container_of(vsc,
1593*8a324c92SDan McDonald 	    struct vioif_softc, sc_virtio);
1594*8a324c92SDan McDonald 
1595*8a324c92SDan McDonald 	vioif_reclaim_used_tx(sc);
1596*8a324c92SDan McDonald 	return (DDI_INTR_CLAIMED);
1597*8a324c92SDan McDonald }
1598*8a324c92SDan McDonald 
1599*8a324c92SDan McDonald static int
vioif_register_ints(struct vioif_softc * sc)1600*8a324c92SDan McDonald vioif_register_ints(struct vioif_softc *sc)
1601*8a324c92SDan McDonald {
1602*8a324c92SDan McDonald 	int ret;
1603*8a324c92SDan McDonald 
1604*8a324c92SDan McDonald 	struct virtio_int_handler vioif_vq_h[] = {
1605*8a324c92SDan McDonald 		{ vioif_rx_handler },
1606*8a324c92SDan McDonald 		{ vioif_tx_handler },
1607*8a324c92SDan McDonald 		{ NULL }
1608*8a324c92SDan McDonald 	};
1609*8a324c92SDan McDonald 
1610*8a324c92SDan McDonald 	ret = virtio_register_ints(&sc->sc_virtio, NULL, vioif_vq_h);
1611*8a324c92SDan McDonald 
1612*8a324c92SDan McDonald 	return (ret);
1613*8a324c92SDan McDonald }
1614*8a324c92SDan McDonald 
1615*8a324c92SDan McDonald 
1616*8a324c92SDan McDonald static void
vioif_check_features(struct vioif_softc * sc)1617*8a324c92SDan McDonald vioif_check_features(struct vioif_softc *sc)
1618*8a324c92SDan McDonald {
1619*8a324c92SDan McDonald 	if (vioif_has_feature(sc, VIRTIO_NET_F_CSUM)) {
1620*8a324c92SDan McDonald 		/* The GSO/GRO featured depend on CSUM, check them here. */
1621*8a324c92SDan McDonald 		sc->sc_tx_csum = 1;
1622*8a324c92SDan McDonald 		sc->sc_rx_csum = 1;
1623*8a324c92SDan McDonald 
1624*8a324c92SDan McDonald 		if (!vioif_has_feature(sc, VIRTIO_NET_F_GUEST_CSUM)) {
1625*8a324c92SDan McDonald 			sc->sc_rx_csum = 0;
1626*8a324c92SDan McDonald 		}
1627*8a324c92SDan McDonald 		cmn_err(CE_NOTE, "Csum enabled.");
1628*8a324c92SDan McDonald 
1629*8a324c92SDan McDonald 		if (vioif_has_feature(sc, VIRTIO_NET_F_HOST_TSO4)) {
1630*8a324c92SDan McDonald 
1631*8a324c92SDan McDonald 			sc->sc_tx_tso4 = 1;
1632*8a324c92SDan McDonald 			/*
1633*8a324c92SDan McDonald 			 * We don't seem to have a way to ask the system
1634*8a324c92SDan McDonald 			 * not to send us LSO packets with Explicit
1635*8a324c92SDan McDonald 			 * Congestion Notification bit set, so we require
1636*8a324c92SDan McDonald 			 * the device to support it in order to do
1637*8a324c92SDan McDonald 			 * LSO.
1638*8a324c92SDan McDonald 			 */
1639*8a324c92SDan McDonald 			if (!vioif_has_feature(sc, VIRTIO_NET_F_HOST_ECN)) {
1640*8a324c92SDan McDonald 				dev_err(sc->sc_dev, CE_NOTE,
1641*8a324c92SDan McDonald 				    "TSO4 supported, but not ECN. "
1642*8a324c92SDan McDonald 				    "Not using LSO.");
1643*8a324c92SDan McDonald 				sc->sc_tx_tso4 = 0;
1644*8a324c92SDan McDonald 			} else {
1645*8a324c92SDan McDonald 				cmn_err(CE_NOTE, "LSO enabled");
1646*8a324c92SDan McDonald 			}
1647*8a324c92SDan McDonald 		}
1648*8a324c92SDan McDonald 	}
1649*8a324c92SDan McDonald }
1650*8a324c92SDan McDonald 
1651*8a324c92SDan McDonald static int
vioif_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)1652*8a324c92SDan McDonald vioif_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
1653*8a324c92SDan McDonald {
1654*8a324c92SDan McDonald 	int ret, instance;
1655*8a324c92SDan McDonald 	struct vioif_softc *sc;
1656*8a324c92SDan McDonald 	struct virtio_softc *vsc;
1657*8a324c92SDan McDonald 	mac_register_t *macp;
1658*8a324c92SDan McDonald 	char cache_name[CACHE_NAME_SIZE];
1659*8a324c92SDan McDonald 
1660*8a324c92SDan McDonald 	instance = ddi_get_instance(devinfo);
1661*8a324c92SDan McDonald 
1662*8a324c92SDan McDonald 	switch (cmd) {
1663*8a324c92SDan McDonald 	case DDI_ATTACH:
1664*8a324c92SDan McDonald 		break;
1665*8a324c92SDan McDonald 
1666*8a324c92SDan McDonald 	case DDI_RESUME:
1667*8a324c92SDan McDonald 	case DDI_PM_RESUME:
1668*8a324c92SDan McDonald 		/* We do not support suspend/resume for vioif. */
1669*8a324c92SDan McDonald 		goto exit;
1670*8a324c92SDan McDonald 
1671*8a324c92SDan McDonald 	default:
1672*8a324c92SDan McDonald 		goto exit;
1673*8a324c92SDan McDonald 	}
1674*8a324c92SDan McDonald 
1675*8a324c92SDan McDonald 	sc = kmem_zalloc(sizeof (struct vioif_softc), KM_SLEEP);
1676*8a324c92SDan McDonald 	ddi_set_driver_private(devinfo, sc);
1677*8a324c92SDan McDonald 
1678*8a324c92SDan McDonald 	vsc = &sc->sc_virtio;
1679*8a324c92SDan McDonald 
1680*8a324c92SDan McDonald 	/* Duplicate for less typing */
1681*8a324c92SDan McDonald 	sc->sc_dev = devinfo;
1682*8a324c92SDan McDonald 	vsc->sc_dev = devinfo;
1683*8a324c92SDan McDonald 
1684*8a324c92SDan McDonald 	/*
1685*8a324c92SDan McDonald 	 * Initialize interrupt kstat.
1686*8a324c92SDan McDonald 	 */
1687*8a324c92SDan McDonald 	sc->sc_intrstat = kstat_create("vioif", instance, "intr", "controller",
1688*8a324c92SDan McDonald 	    KSTAT_TYPE_INTR, 1, 0);
1689*8a324c92SDan McDonald 	if (sc->sc_intrstat == NULL) {
1690*8a324c92SDan McDonald 		dev_err(devinfo, CE_WARN, "kstat_create failed");
1691*8a324c92SDan McDonald 		goto exit_intrstat;
1692*8a324c92SDan McDonald 	}
1693*8a324c92SDan McDonald 	kstat_install(sc->sc_intrstat);
1694*8a324c92SDan McDonald 
1695*8a324c92SDan McDonald 	/* map BAR 0 */
1696*8a324c92SDan McDonald 	ret = ddi_regs_map_setup(devinfo, 1,
1697*8a324c92SDan McDonald 	    (caddr_t *)&sc->sc_virtio.sc_io_addr,
1698*8a324c92SDan McDonald 	    0, 0, &vioif_attr, &sc->sc_virtio.sc_ioh);
1699*8a324c92SDan McDonald 	if (ret != DDI_SUCCESS) {
1700*8a324c92SDan McDonald 		dev_err(devinfo, CE_WARN, "unable to map bar 0: %d", ret);
1701*8a324c92SDan McDonald 		goto exit_map;
1702*8a324c92SDan McDonald 	}
1703*8a324c92SDan McDonald 
1704*8a324c92SDan McDonald 	virtio_device_reset(&sc->sc_virtio);
1705*8a324c92SDan McDonald 	virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
1706*8a324c92SDan McDonald 	virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
1707*8a324c92SDan McDonald 
1708*8a324c92SDan McDonald 	ret = vioif_dev_features(sc);
1709*8a324c92SDan McDonald 	if (ret)
1710*8a324c92SDan McDonald 		goto exit_features;
1711*8a324c92SDan McDonald 
1712*8a324c92SDan McDonald 	vsc->sc_nvqs = vioif_has_feature(sc, VIRTIO_NET_F_CTRL_VQ) ? 3 : 2;
1713*8a324c92SDan McDonald 
1714*8a324c92SDan McDonald 	(void) snprintf(cache_name, CACHE_NAME_SIZE, "vioif%d_rx", instance);
1715*8a324c92SDan McDonald 	sc->sc_rxbuf_cache = kmem_cache_create(cache_name,
1716*8a324c92SDan McDonald 	    sizeof (struct vioif_rx_buf), 0, vioif_rx_construct,
1717*8a324c92SDan McDonald 	    vioif_rx_destruct, NULL, sc, NULL, KM_SLEEP);
1718*8a324c92SDan McDonald 	if (sc->sc_rxbuf_cache == NULL) {
1719*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_WARN, "Can't allocate the buffer cache");
1720*8a324c92SDan McDonald 		goto exit_cache;
1721*8a324c92SDan McDonald 	}
1722*8a324c92SDan McDonald 
1723*8a324c92SDan McDonald 	ret = vioif_register_ints(sc);
1724*8a324c92SDan McDonald 	if (ret) {
1725*8a324c92SDan McDonald 		dev_err(sc->sc_dev, CE_WARN,
1726*8a324c92SDan McDonald 		    "Failed to allocate interrupt(s)!");
1727*8a324c92SDan McDonald 		goto exit_ints;
1728*8a324c92SDan McDonald 	}
1729*8a324c92SDan McDonald 
1730*8a324c92SDan McDonald 	/*
1731*8a324c92SDan McDonald 	 * Register layout determined, can now access the
1732*8a324c92SDan McDonald 	 * device-specific bits
1733*8a324c92SDan McDonald 	 */
1734*8a324c92SDan McDonald 	vioif_get_mac(sc);
1735*8a324c92SDan McDonald 
1736*8a324c92SDan McDonald 	sc->sc_rx_vq = virtio_alloc_vq(&sc->sc_virtio, 0,
1737*8a324c92SDan McDonald 	    VIOIF_RX_QLEN, VIOIF_INDIRECT_MAX, "rx");
1738*8a324c92SDan McDonald 	if (!sc->sc_rx_vq)
1739*8a324c92SDan McDonald 		goto exit_alloc1;
1740*8a324c92SDan McDonald 	virtio_stop_vq_intr(sc->sc_rx_vq);
1741*8a324c92SDan McDonald 
1742*8a324c92SDan McDonald 	sc->sc_tx_vq = virtio_alloc_vq(&sc->sc_virtio, 1,
1743*8a324c92SDan McDonald 	    VIOIF_TX_QLEN, VIOIF_INDIRECT_MAX, "tx");
1744*8a324c92SDan McDonald 	if (!sc->sc_rx_vq)
1745*8a324c92SDan McDonald 		goto exit_alloc2;
1746*8a324c92SDan McDonald 	virtio_stop_vq_intr(sc->sc_tx_vq);
1747*8a324c92SDan McDonald 
1748*8a324c92SDan McDonald 	if (vioif_has_feature(sc, VIRTIO_NET_F_CTRL_VQ)) {
1749*8a324c92SDan McDonald 		sc->sc_ctrl_vq = virtio_alloc_vq(&sc->sc_virtio, 2,
1750*8a324c92SDan McDonald 		    VIOIF_CTRL_QLEN, 0, "ctrl");
1751*8a324c92SDan McDonald 		if (!sc->sc_ctrl_vq) {
1752*8a324c92SDan McDonald 			goto exit_alloc3;
1753*8a324c92SDan McDonald 		}
1754*8a324c92SDan McDonald 		virtio_stop_vq_intr(sc->sc_ctrl_vq);
1755*8a324c92SDan McDonald 	}
1756*8a324c92SDan McDonald 
1757*8a324c92SDan McDonald 	virtio_set_status(&sc->sc_virtio,
1758*8a324c92SDan McDonald 	    VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK);
1759*8a324c92SDan McDonald 
1760*8a324c92SDan McDonald 	sc->sc_rxloan = 0;
1761*8a324c92SDan McDonald 
1762*8a324c92SDan McDonald 	/* set some reasonable-small default values */
1763*8a324c92SDan McDonald 	sc->sc_rxcopy_thresh = 300;
1764*8a324c92SDan McDonald 	sc->sc_txcopy_thresh = 300;
1765*8a324c92SDan McDonald 	sc->sc_mtu = ETHERMTU;
1766*8a324c92SDan McDonald 
1767*8a324c92SDan McDonald 	vioif_check_features(sc);
1768*8a324c92SDan McDonald 
1769*8a324c92SDan McDonald 	if (vioif_alloc_mems(sc))
1770*8a324c92SDan McDonald 		goto exit_alloc_mems;
1771*8a324c92SDan McDonald 
1772*8a324c92SDan McDonald 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
1773*8a324c92SDan McDonald 		dev_err(devinfo, CE_WARN, "Failed to allocate a mac_register");
1774*8a324c92SDan McDonald 		goto exit_macalloc;
1775*8a324c92SDan McDonald 	}
1776*8a324c92SDan McDonald 
1777*8a324c92SDan McDonald 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
1778*8a324c92SDan McDonald 	macp->m_driver = sc;
1779*8a324c92SDan McDonald 	macp->m_dip = devinfo;
1780*8a324c92SDan McDonald 	macp->m_src_addr = sc->sc_mac;
1781*8a324c92SDan McDonald 	macp->m_callbacks = &vioif_m_callbacks;
1782*8a324c92SDan McDonald 	macp->m_min_sdu = 0;
1783*8a324c92SDan McDonald 	macp->m_max_sdu = sc->sc_mtu;
1784*8a324c92SDan McDonald 	macp->m_margin = VLAN_TAGSZ;
1785*8a324c92SDan McDonald 	macp->m_priv_props = vioif_priv_props;
1786*8a324c92SDan McDonald 
1787*8a324c92SDan McDonald 	sc->sc_macp = macp;
1788*8a324c92SDan McDonald 
1789*8a324c92SDan McDonald 	/* Pre-fill the rx ring. */
1790*8a324c92SDan McDonald 	(void) vioif_populate_rx(sc, KM_SLEEP);
1791*8a324c92SDan McDonald 
1792*8a324c92SDan McDonald 	ret = mac_register(macp, &sc->sc_mac_handle);
1793*8a324c92SDan McDonald 	if (ret != 0) {
1794*8a324c92SDan McDonald 		dev_err(devinfo, CE_WARN, "vioif_attach: "
1795*8a324c92SDan McDonald 		    "mac_register() failed, ret=%d", ret);
1796*8a324c92SDan McDonald 		goto exit_register;
1797*8a324c92SDan McDonald 	}
1798*8a324c92SDan McDonald 
1799*8a324c92SDan McDonald 	ret = virtio_enable_ints(&sc->sc_virtio);
1800*8a324c92SDan McDonald 	if (ret) {
1801*8a324c92SDan McDonald 		dev_err(devinfo, CE_WARN, "Failed to enable interrupts");
1802*8a324c92SDan McDonald 		goto exit_enable_ints;
1803*8a324c92SDan McDonald 	}
1804*8a324c92SDan McDonald 
1805*8a324c92SDan McDonald 	mac_link_update(sc->sc_mac_handle, LINK_STATE_UP);
1806*8a324c92SDan McDonald 	return (DDI_SUCCESS);
1807*8a324c92SDan McDonald 
1808*8a324c92SDan McDonald exit_enable_ints:
1809*8a324c92SDan McDonald 	(void) mac_unregister(sc->sc_mac_handle);
1810*8a324c92SDan McDonald exit_register:
1811*8a324c92SDan McDonald 	mac_free(macp);
1812*8a324c92SDan McDonald exit_macalloc:
1813*8a324c92SDan McDonald 	vioif_free_mems(sc);
1814*8a324c92SDan McDonald exit_alloc_mems:
1815*8a324c92SDan McDonald 	virtio_release_ints(&sc->sc_virtio);
1816*8a324c92SDan McDonald 	if (sc->sc_ctrl_vq)
1817*8a324c92SDan McDonald 		virtio_free_vq(sc->sc_ctrl_vq);
1818*8a324c92SDan McDonald exit_alloc3:
1819*8a324c92SDan McDonald 	virtio_free_vq(sc->sc_tx_vq);
1820*8a324c92SDan McDonald exit_alloc2:
1821*8a324c92SDan McDonald 	virtio_free_vq(sc->sc_rx_vq);
1822*8a324c92SDan McDonald exit_alloc1:
1823*8a324c92SDan McDonald exit_ints:
1824*8a324c92SDan McDonald 	kmem_cache_destroy(sc->sc_rxbuf_cache);
1825*8a324c92SDan McDonald exit_cache:
1826*8a324c92SDan McDonald exit_features:
1827*8a324c92SDan McDonald 	virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
1828*8a324c92SDan McDonald 	ddi_regs_map_free(&sc->sc_virtio.sc_ioh);
1829*8a324c92SDan McDonald exit_intrstat:
1830*8a324c92SDan McDonald exit_map:
1831*8a324c92SDan McDonald 	kstat_delete(sc->sc_intrstat);
1832*8a324c92SDan McDonald 	kmem_free(sc, sizeof (struct vioif_softc));
1833*8a324c92SDan McDonald exit:
1834*8a324c92SDan McDonald 	return (DDI_FAILURE);
1835*8a324c92SDan McDonald }
1836*8a324c92SDan McDonald 
1837*8a324c92SDan McDonald static int
vioif_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)1838*8a324c92SDan McDonald vioif_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
1839*8a324c92SDan McDonald {
1840*8a324c92SDan McDonald 	struct vioif_softc *sc;
1841*8a324c92SDan McDonald 
1842*8a324c92SDan McDonald 	if ((sc = ddi_get_driver_private(devinfo)) == NULL)
1843*8a324c92SDan McDonald 		return (DDI_FAILURE);
1844*8a324c92SDan McDonald 
1845*8a324c92SDan McDonald 	switch (cmd) {
1846*8a324c92SDan McDonald 	case DDI_DETACH:
1847*8a324c92SDan McDonald 		break;
1848*8a324c92SDan McDonald 
1849*8a324c92SDan McDonald 	case DDI_PM_SUSPEND:
1850*8a324c92SDan McDonald 		/* We do not support suspend/resume for vioif. */
1851*8a324c92SDan McDonald 		return (DDI_FAILURE);
1852*8a324c92SDan McDonald 
1853*8a324c92SDan McDonald 	default:
1854*8a324c92SDan McDonald 		return (DDI_FAILURE);
1855*8a324c92SDan McDonald 	}
1856*8a324c92SDan McDonald 
1857*8a324c92SDan McDonald 	if (sc->sc_rxloan) {
1858*8a324c92SDan McDonald 		dev_err(devinfo, CE_WARN, "!Some rx buffers are still upstream,"
1859*8a324c92SDan McDonald 		    " not detaching.");
1860*8a324c92SDan McDonald 		return (DDI_FAILURE);
1861*8a324c92SDan McDonald 	}
1862*8a324c92SDan McDonald 
1863*8a324c92SDan McDonald 	virtio_stop_vq_intr(sc->sc_rx_vq);
1864*8a324c92SDan McDonald 	virtio_stop_vq_intr(sc->sc_tx_vq);
1865*8a324c92SDan McDonald 
1866*8a324c92SDan McDonald 	virtio_release_ints(&sc->sc_virtio);
1867*8a324c92SDan McDonald 
1868*8a324c92SDan McDonald 	if (mac_unregister(sc->sc_mac_handle)) {
1869*8a324c92SDan McDonald 		return (DDI_FAILURE);
1870*8a324c92SDan McDonald 	}
1871*8a324c92SDan McDonald 
1872*8a324c92SDan McDonald 	mac_free(sc->sc_macp);
1873*8a324c92SDan McDonald 
1874*8a324c92SDan McDonald 	vioif_free_mems(sc);
1875*8a324c92SDan McDonald 	virtio_free_vq(sc->sc_rx_vq);
1876*8a324c92SDan McDonald 	virtio_free_vq(sc->sc_tx_vq);
1877*8a324c92SDan McDonald 
1878*8a324c92SDan McDonald 	virtio_device_reset(&sc->sc_virtio);
1879*8a324c92SDan McDonald 
1880*8a324c92SDan McDonald 	ddi_regs_map_free(&sc->sc_virtio.sc_ioh);
1881*8a324c92SDan McDonald 
1882*8a324c92SDan McDonald 	kmem_cache_destroy(sc->sc_rxbuf_cache);
1883*8a324c92SDan McDonald 	kstat_delete(sc->sc_intrstat);
1884*8a324c92SDan McDonald 	kmem_free(sc, sizeof (struct vioif_softc));
1885*8a324c92SDan McDonald 
1886*8a324c92SDan McDonald 	return (DDI_SUCCESS);
1887*8a324c92SDan McDonald }
1888*8a324c92SDan McDonald 
1889*8a324c92SDan McDonald static int
vioif_quiesce(dev_info_t * devinfo)1890*8a324c92SDan McDonald vioif_quiesce(dev_info_t *devinfo)
1891*8a324c92SDan McDonald {
1892*8a324c92SDan McDonald 	struct vioif_softc *sc;
1893*8a324c92SDan McDonald 
1894*8a324c92SDan McDonald 	if ((sc = ddi_get_driver_private(devinfo)) == NULL)
1895*8a324c92SDan McDonald 		return (DDI_FAILURE);
1896*8a324c92SDan McDonald 
1897*8a324c92SDan McDonald 	virtio_stop_vq_intr(sc->sc_rx_vq);
1898*8a324c92SDan McDonald 	virtio_stop_vq_intr(sc->sc_tx_vq);
1899*8a324c92SDan McDonald 	virtio_device_reset(&sc->sc_virtio);
1900*8a324c92SDan McDonald 
1901*8a324c92SDan McDonald 	return (DDI_SUCCESS);
1902*8a324c92SDan McDonald }
1903*8a324c92SDan McDonald 
1904*8a324c92SDan McDonald int
_init(void)1905*8a324c92SDan McDonald _init(void)
1906*8a324c92SDan McDonald {
1907*8a324c92SDan McDonald 	int ret = 0;
1908*8a324c92SDan McDonald 
1909*8a324c92SDan McDonald 	mac_init_ops(&vioif_ops, "vioif");
1910*8a324c92SDan McDonald 
1911*8a324c92SDan McDonald 	ret = mod_install(&modlinkage);
1912*8a324c92SDan McDonald 	if (ret != DDI_SUCCESS) {
1913*8a324c92SDan McDonald 		mac_fini_ops(&vioif_ops);
1914*8a324c92SDan McDonald 		return (ret);
1915*8a324c92SDan McDonald 	}
1916*8a324c92SDan McDonald 
1917*8a324c92SDan McDonald 	return (0);
1918*8a324c92SDan McDonald }
1919*8a324c92SDan McDonald 
1920*8a324c92SDan McDonald int
_fini(void)1921*8a324c92SDan McDonald _fini(void)
1922*8a324c92SDan McDonald {
1923*8a324c92SDan McDonald 	int ret;
1924*8a324c92SDan McDonald 
1925*8a324c92SDan McDonald 	ret = mod_remove(&modlinkage);
1926*8a324c92SDan McDonald 	if (ret == DDI_SUCCESS) {
1927*8a324c92SDan McDonald 		mac_fini_ops(&vioif_ops);
1928*8a324c92SDan McDonald 	}
1929*8a324c92SDan McDonald 
1930*8a324c92SDan McDonald 	return (ret);
1931*8a324c92SDan McDonald }
1932*8a324c92SDan McDonald 
1933*8a324c92SDan McDonald int
_info(struct modinfo * pModinfo)1934*8a324c92SDan McDonald _info(struct modinfo *pModinfo)
1935*8a324c92SDan McDonald {
1936*8a324c92SDan McDonald 	return (mod_info(&modlinkage, pModinfo));
1937*8a324c92SDan McDonald }
1938