xref: /titanic_52/usr/src/uts/common/xen/io/xnf.c (revision 1a5e258f5471356ca102c7176637cdce45bac147)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev 
22843e1988Sjohnlev /*
23fd0939efSDavid Edmondson  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
27843e1988Sjohnlev /*
28843e1988Sjohnlev  *
29843e1988Sjohnlev  * Copyright (c) 2004 Christian Limpach.
30843e1988Sjohnlev  * All rights reserved.
31843e1988Sjohnlev  *
32843e1988Sjohnlev  * Redistribution and use in source and binary forms, with or without
33843e1988Sjohnlev  * modification, are permitted provided that the following conditions
34843e1988Sjohnlev  * are met:
35843e1988Sjohnlev  * 1. Redistributions of source code must retain the above copyright
36843e1988Sjohnlev  *    notice, this list of conditions and the following disclaimer.
37843e1988Sjohnlev  * 2. Redistributions in binary form must reproduce the above copyright
38843e1988Sjohnlev  *    notice, this list of conditions and the following disclaimer in the
39843e1988Sjohnlev  *    documentation and/or other materials provided with the distribution.
40843e1988Sjohnlev  * 3. This section intentionally left blank.
41843e1988Sjohnlev  * 4. The name of the author may not be used to endorse or promote products
42843e1988Sjohnlev  *    derived from this software without specific prior written permission.
43843e1988Sjohnlev  *
44843e1988Sjohnlev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
45843e1988Sjohnlev  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
46843e1988Sjohnlev  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
47843e1988Sjohnlev  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
48843e1988Sjohnlev  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
49843e1988Sjohnlev  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
50843e1988Sjohnlev  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
51843e1988Sjohnlev  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
52843e1988Sjohnlev  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
53843e1988Sjohnlev  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54843e1988Sjohnlev  */
55843e1988Sjohnlev /*
56843e1988Sjohnlev  * Section 3 of the above license was updated in response to bug 6379571.
57843e1988Sjohnlev  */
58843e1988Sjohnlev 
59843e1988Sjohnlev /*
6056567907SDavid Edmondson  * xnf.c - GLDv3 network driver for domU.
6156567907SDavid Edmondson  */
6256567907SDavid Edmondson 
6356567907SDavid Edmondson /*
6456567907SDavid Edmondson  * This driver uses four per-instance locks:
6556567907SDavid Edmondson  *
6656567907SDavid Edmondson  * xnf_gref_lock:
6756567907SDavid Edmondson  *
6856567907SDavid Edmondson  *    Protects access to the grant reference list stored in
6956567907SDavid Edmondson  *    xnf_gref_head. Grant references should be acquired and released
7056567907SDavid Edmondson  *    using gref_get() and gref_put() respectively.
7156567907SDavid Edmondson  *
7256567907SDavid Edmondson  * xnf_schedlock:
7356567907SDavid Edmondson  *
7456567907SDavid Edmondson  *    Protects:
7556567907SDavid Edmondson  *    xnf_need_sched - used to record that a previous transmit attempt
7656567907SDavid Edmondson  *       failed (and consequently it will be necessary to call
7756567907SDavid Edmondson  *       mac_tx_update() when transmit resources are available).
7856567907SDavid Edmondson  *    xnf_pending_multicast - the number of multicast requests that
7956567907SDavid Edmondson  *       have been submitted to the backend for which we have not
8056567907SDavid Edmondson  *       processed responses.
8156567907SDavid Edmondson  *
8256567907SDavid Edmondson  * xnf_txlock:
8356567907SDavid Edmondson  *
8456567907SDavid Edmondson  *    Protects the transmit ring (xnf_tx_ring) and associated
8556567907SDavid Edmondson  *    structures (notably xnf_tx_pkt_id and xnf_tx_pkt_id_head).
8656567907SDavid Edmondson  *
8756567907SDavid Edmondson  * xnf_rxlock:
8856567907SDavid Edmondson  *
8956567907SDavid Edmondson  *    Protects the receive ring (xnf_rx_ring) and associated
9056567907SDavid Edmondson  *    structures (notably xnf_rx_pkt_info).
9156567907SDavid Edmondson  *
9256567907SDavid Edmondson  * If driver-global state that affects both the transmit and receive
9356567907SDavid Edmondson  * rings is manipulated, both xnf_txlock and xnf_rxlock should be
9456567907SDavid Edmondson  * held, in that order.
9556567907SDavid Edmondson  *
9656567907SDavid Edmondson  * xnf_schedlock is acquired both whilst holding xnf_txlock and
9756567907SDavid Edmondson  * without. It should always be acquired after xnf_txlock if both are
9856567907SDavid Edmondson  * held.
9956567907SDavid Edmondson  *
10056567907SDavid Edmondson  * Notes:
10156567907SDavid Edmondson  * - atomic_add_64() is used to manipulate counters where we require
10256567907SDavid Edmondson  *   accuracy. For counters intended only for observation by humans,
10356567907SDavid Edmondson  *   post increment/decrement are used instead.
104843e1988Sjohnlev  */
105843e1988Sjohnlev 
106843e1988Sjohnlev #include <sys/types.h>
107843e1988Sjohnlev #include <sys/errno.h>
108843e1988Sjohnlev #include <sys/param.h>
109843e1988Sjohnlev #include <sys/sysmacros.h>
110843e1988Sjohnlev #include <sys/systm.h>
111843e1988Sjohnlev #include <sys/stream.h>
112843e1988Sjohnlev #include <sys/strsubr.h>
11356567907SDavid Edmondson #include <sys/strsun.h>
114843e1988Sjohnlev #include <sys/conf.h>
115843e1988Sjohnlev #include <sys/ddi.h>
116843e1988Sjohnlev #include <sys/devops.h>
117843e1988Sjohnlev #include <sys/sunddi.h>
118843e1988Sjohnlev #include <sys/sunndi.h>
119843e1988Sjohnlev #include <sys/dlpi.h>
120843e1988Sjohnlev #include <sys/ethernet.h>
121843e1988Sjohnlev #include <sys/strsun.h>
122843e1988Sjohnlev #include <sys/pattr.h>
123843e1988Sjohnlev #include <inet/ip.h>
124a859da42SDavid Edmondson #include <inet/ip_impl.h>
125a859da42SDavid Edmondson #include <sys/gld.h>
126843e1988Sjohnlev #include <sys/modctl.h>
127da14cebeSEric Cheng #include <sys/mac_provider.h>
128843e1988Sjohnlev #include <sys/mac_ether.h>
129843e1988Sjohnlev #include <sys/bootinfo.h>
130843e1988Sjohnlev #include <sys/mach_mmu.h>
131551bc2a6Smrj #ifdef	XPV_HVM_DRIVER
132551bc2a6Smrj #include <sys/xpv_support.h>
133551bc2a6Smrj #include <sys/hypervisor.h>
134551bc2a6Smrj #else
135551bc2a6Smrj #include <sys/hypervisor.h>
136843e1988Sjohnlev #include <sys/evtchn_impl.h>
137843e1988Sjohnlev #include <sys/balloon_impl.h>
138551bc2a6Smrj #endif
139551bc2a6Smrj #include <xen/public/io/netif.h>
140551bc2a6Smrj #include <sys/gnttab.h>
141843e1988Sjohnlev #include <xen/sys/xendev.h>
142551bc2a6Smrj #include <sys/sdt.h>
14356567907SDavid Edmondson #include <sys/note.h>
14456567907SDavid Edmondson #include <sys/debug.h>
145551bc2a6Smrj 
146551bc2a6Smrj #include <io/xnf.h>
147551bc2a6Smrj 
148843e1988Sjohnlev #if defined(DEBUG) || defined(__lint)
149843e1988Sjohnlev #define	XNF_DEBUG
15056567907SDavid Edmondson #endif
15156567907SDavid Edmondson 
15256567907SDavid Edmondson #ifdef XNF_DEBUG
15356567907SDavid Edmondson int xnf_debug = 0;
15456567907SDavid Edmondson xnf_t *xnf_debug_instance = NULL;
155843e1988Sjohnlev #endif
156843e1988Sjohnlev 
157843e1988Sjohnlev /*
158843e1988Sjohnlev  * On a 32 bit PAE system physical and machine addresses are larger
159843e1988Sjohnlev  * than 32 bits.  ddi_btop() on such systems take an unsigned long
160843e1988Sjohnlev  * argument, and so addresses above 4G are truncated before ddi_btop()
161843e1988Sjohnlev  * gets to see them.  To avoid this, code the shift operation here.
162843e1988Sjohnlev  */
163843e1988Sjohnlev #define	xnf_btop(addr)	((addr) >> PAGESHIFT)
164843e1988Sjohnlev 
165843e1988Sjohnlev unsigned int	xnf_max_tx_frags = 1;
166843e1988Sjohnlev 
16756567907SDavid Edmondson /*
16856567907SDavid Edmondson  * Should we use the multicast control feature if the backend provides
16956567907SDavid Edmondson  * it?
17056567907SDavid Edmondson  */
17156567907SDavid Edmondson boolean_t xnf_multicast_control = B_TRUE;
17256567907SDavid Edmondson 
17356567907SDavid Edmondson /*
17456567907SDavid Edmondson  * Received packets below this size are copied to a new streams buffer
17556567907SDavid Edmondson  * rather than being desballoc'ed.
17656567907SDavid Edmondson  *
17756567907SDavid Edmondson  * This value is chosen to accommodate traffic where there are a large
17856567907SDavid Edmondson  * number of small packets. For data showing a typical distribution,
17956567907SDavid Edmondson  * see:
18056567907SDavid Edmondson  *
18156567907SDavid Edmondson  * Sinha07a:
18256567907SDavid Edmondson  *	Rishi Sinha, Christos Papadopoulos, and John
18356567907SDavid Edmondson  *	Heidemann. Internet Packet Size Distributions: Some
18456567907SDavid Edmondson  *	Observations. Technical Report ISI-TR-2007-643,
18556567907SDavid Edmondson  *	USC/Information Sciences Institute, May, 2007. Orignally
18656567907SDavid Edmondson  *	released October 2005 as web page
18756567907SDavid Edmondson  *	http://netweb.usc.edu/~sinha/pkt-sizes/.
18856567907SDavid Edmondson  *	<http://www.isi.edu/~johnh/PAPERS/Sinha07a.html>.
18956567907SDavid Edmondson  */
19056567907SDavid Edmondson size_t xnf_rx_copy_limit = 64;
19156567907SDavid Edmondson 
19256567907SDavid Edmondson #define	INVALID_GRANT_HANDLE	((grant_handle_t)-1)
19356567907SDavid Edmondson #define	INVALID_GRANT_REF	((grant_ref_t)-1)
19456567907SDavid Edmondson #define	INVALID_TX_ID		((uint16_t)-1)
19556567907SDavid Edmondson 
19656567907SDavid Edmondson #define	TX_ID_TO_TXID(p, id) (&((p)->xnf_tx_pkt_id[(id)]))
19756567907SDavid Edmondson #define	TX_ID_VALID(i) (((i) != INVALID_TX_ID) && ((i) < NET_TX_RING_SIZE))
19856567907SDavid Edmondson 
199843e1988Sjohnlev /* Required system entry points */
200843e1988Sjohnlev static int	xnf_attach(dev_info_t *, ddi_attach_cmd_t);
201843e1988Sjohnlev static int	xnf_detach(dev_info_t *, ddi_detach_cmd_t);
202843e1988Sjohnlev 
203843e1988Sjohnlev /* Required driver entry points for Nemo */
204843e1988Sjohnlev static int	xnf_start(void *);
205843e1988Sjohnlev static void	xnf_stop(void *);
206843e1988Sjohnlev static int	xnf_set_mac_addr(void *, const uint8_t *);
207843e1988Sjohnlev static int	xnf_set_multicast(void *, boolean_t, const uint8_t *);
208843e1988Sjohnlev static int	xnf_set_promiscuous(void *, boolean_t);
209843e1988Sjohnlev static mblk_t	*xnf_send(void *, mblk_t *);
210843e1988Sjohnlev static uint_t	xnf_intr(caddr_t);
211843e1988Sjohnlev static int	xnf_stat(void *, uint_t, uint64_t *);
212843e1988Sjohnlev static boolean_t xnf_getcapab(void *, mac_capab_t, void *);
213843e1988Sjohnlev 
214843e1988Sjohnlev /* Driver private functions */
215843e1988Sjohnlev static int xnf_alloc_dma_resources(xnf_t *);
216843e1988Sjohnlev static void xnf_release_dma_resources(xnf_t *);
217843e1988Sjohnlev static void xnf_release_mblks(xnf_t *);
21856567907SDavid Edmondson 
21956567907SDavid Edmondson static int xnf_buf_constructor(void *, void *, int);
22056567907SDavid Edmondson static void xnf_buf_destructor(void *, void *);
22156567907SDavid Edmondson static xnf_buf_t *xnf_buf_get(xnf_t *, int, boolean_t);
22256567907SDavid Edmondson #pragma inline(xnf_buf_get)
22356567907SDavid Edmondson static void xnf_buf_put(xnf_t *, xnf_buf_t *, boolean_t);
22456567907SDavid Edmondson #pragma inline(xnf_buf_put)
22556567907SDavid Edmondson static void xnf_buf_refresh(xnf_buf_t *);
22656567907SDavid Edmondson #pragma inline(xnf_buf_refresh)
22756567907SDavid Edmondson static void xnf_buf_recycle(xnf_buf_t *);
22856567907SDavid Edmondson 
22956567907SDavid Edmondson static int xnf_tx_buf_constructor(void *, void *, int);
23056567907SDavid Edmondson static void xnf_tx_buf_destructor(void *, void *);
23156567907SDavid Edmondson 
23256567907SDavid Edmondson static grant_ref_t gref_get(xnf_t *);
23356567907SDavid Edmondson #pragma inline(gref_get)
23456567907SDavid Edmondson static void gref_put(xnf_t *, grant_ref_t);
23556567907SDavid Edmondson #pragma inline(gref_put)
23656567907SDavid Edmondson 
23756567907SDavid Edmondson static xnf_txid_t *txid_get(xnf_t *);
23856567907SDavid Edmondson #pragma inline(txid_get)
23956567907SDavid Edmondson static void txid_put(xnf_t *, xnf_txid_t *);
24056567907SDavid Edmondson #pragma inline(txid_put)
24156567907SDavid Edmondson 
242843e1988Sjohnlev void xnf_send_driver_status(int, int);
24356567907SDavid Edmondson static void xnf_rxbuf_hang(xnf_t *, xnf_buf_t *);
24456567907SDavid Edmondson static int xnf_tx_clean_ring(xnf_t  *);
245843e1988Sjohnlev static void oe_state_change(dev_info_t *, ddi_eventcookie_t,
246843e1988Sjohnlev     void *, void *);
24756567907SDavid Edmondson static boolean_t xnf_kstat_init(xnf_t *);
24856567907SDavid Edmondson static void xnf_rx_collect(xnf_t *);
249843e1988Sjohnlev 
250843e1988Sjohnlev static mac_callbacks_t xnf_callbacks = {
25156567907SDavid Edmondson 	MC_GETCAPAB,
252843e1988Sjohnlev 	xnf_stat,
253843e1988Sjohnlev 	xnf_start,
254843e1988Sjohnlev 	xnf_stop,
255843e1988Sjohnlev 	xnf_set_promiscuous,
256843e1988Sjohnlev 	xnf_set_multicast,
257843e1988Sjohnlev 	xnf_set_mac_addr,
258843e1988Sjohnlev 	xnf_send,
25956567907SDavid Edmondson 	NULL,
2600dc2366fSVenugopal Iyer 	NULL,
261843e1988Sjohnlev 	xnf_getcapab
262843e1988Sjohnlev };
263843e1988Sjohnlev 
264843e1988Sjohnlev /* DMA attributes for network ring buffer */
265843e1988Sjohnlev static ddi_dma_attr_t ringbuf_dma_attr = {
266843e1988Sjohnlev 	DMA_ATTR_V0,		/* version of this structure */
267843e1988Sjohnlev 	0,			/* lowest usable address */
268843e1988Sjohnlev 	0xffffffffffffffffULL,	/* highest usable address */
269843e1988Sjohnlev 	0x7fffffff,		/* maximum DMAable byte count */
270843e1988Sjohnlev 	MMU_PAGESIZE,		/* alignment in bytes */
271843e1988Sjohnlev 	0x7ff,			/* bitmap of burst sizes */
272843e1988Sjohnlev 	1,			/* minimum transfer */
273843e1988Sjohnlev 	0xffffffffU,		/* maximum transfer */
274843e1988Sjohnlev 	0xffffffffffffffffULL,	/* maximum segment length */
275843e1988Sjohnlev 	1,			/* maximum number of segments */
276843e1988Sjohnlev 	1,			/* granularity */
277843e1988Sjohnlev 	0,			/* flags (reserved) */
278843e1988Sjohnlev };
279843e1988Sjohnlev 
28056567907SDavid Edmondson /* DMA attributes for transmit and receive data */
28156567907SDavid Edmondson static ddi_dma_attr_t buf_dma_attr = {
282843e1988Sjohnlev 	DMA_ATTR_V0,		/* version of this structure */
283843e1988Sjohnlev 	0,			/* lowest usable address */
284843e1988Sjohnlev 	0xffffffffffffffffULL,	/* highest usable address */
285843e1988Sjohnlev 	0x7fffffff,		/* maximum DMAable byte count */
286843e1988Sjohnlev 	MMU_PAGESIZE,		/* alignment in bytes */
287843e1988Sjohnlev 	0x7ff,			/* bitmap of burst sizes */
288843e1988Sjohnlev 	1,			/* minimum transfer */
289843e1988Sjohnlev 	0xffffffffU,		/* maximum transfer */
290843e1988Sjohnlev 	0xffffffffffffffffULL,	/* maximum segment length */
291843e1988Sjohnlev 	1,			/* maximum number of segments */
292843e1988Sjohnlev 	1,			/* granularity */
293843e1988Sjohnlev 	0,			/* flags (reserved) */
294843e1988Sjohnlev };
295843e1988Sjohnlev 
296843e1988Sjohnlev /* DMA access attributes for registers and descriptors */
297843e1988Sjohnlev static ddi_device_acc_attr_t accattr = {
298843e1988Sjohnlev 	DDI_DEVICE_ATTR_V0,
299843e1988Sjohnlev 	DDI_STRUCTURE_LE_ACC,	/* This is a little-endian device */
300843e1988Sjohnlev 	DDI_STRICTORDER_ACC
301843e1988Sjohnlev };
302843e1988Sjohnlev 
303843e1988Sjohnlev /* DMA access attributes for data: NOT to be byte swapped. */
304843e1988Sjohnlev static ddi_device_acc_attr_t data_accattr = {
305843e1988Sjohnlev 	DDI_DEVICE_ATTR_V0,
306843e1988Sjohnlev 	DDI_NEVERSWAP_ACC,
307843e1988Sjohnlev 	DDI_STRICTORDER_ACC
308843e1988Sjohnlev };
309843e1988Sjohnlev 
310843e1988Sjohnlev DDI_DEFINE_STREAM_OPS(xnf_dev_ops, nulldev, nulldev, xnf_attach, xnf_detach,
31119397407SSherry Moore     nodev, NULL, D_MP, NULL, ddi_quiesce_not_supported);
312843e1988Sjohnlev 
313843e1988Sjohnlev static struct modldrv xnf_modldrv = {
314a859da42SDavid Edmondson 	&mod_driverops,
315a859da42SDavid Edmondson 	"Virtual Ethernet driver",
316a859da42SDavid Edmondson 	&xnf_dev_ops
317843e1988Sjohnlev };
318843e1988Sjohnlev 
319843e1988Sjohnlev static struct modlinkage modlinkage = {
320843e1988Sjohnlev 	MODREV_1, &xnf_modldrv, NULL
321843e1988Sjohnlev };
322843e1988Sjohnlev 
323843e1988Sjohnlev int
324843e1988Sjohnlev _init(void)
325843e1988Sjohnlev {
326843e1988Sjohnlev 	int r;
327843e1988Sjohnlev 
328843e1988Sjohnlev 	mac_init_ops(&xnf_dev_ops, "xnf");
329843e1988Sjohnlev 	r = mod_install(&modlinkage);
330843e1988Sjohnlev 	if (r != DDI_SUCCESS)
331843e1988Sjohnlev 		mac_fini_ops(&xnf_dev_ops);
332843e1988Sjohnlev 
333843e1988Sjohnlev 	return (r);
334843e1988Sjohnlev }
335843e1988Sjohnlev 
336843e1988Sjohnlev int
337843e1988Sjohnlev _fini(void)
338843e1988Sjohnlev {
33956567907SDavid Edmondson 	return (EBUSY); /* XXPV should be removable */
340843e1988Sjohnlev }
341843e1988Sjohnlev 
342843e1988Sjohnlev int
343843e1988Sjohnlev _info(struct modinfo *modinfop)
344843e1988Sjohnlev {
345843e1988Sjohnlev 	return (mod_info(&modlinkage, modinfop));
346843e1988Sjohnlev }
347843e1988Sjohnlev 
34856567907SDavid Edmondson /*
34956567907SDavid Edmondson  * Acquire a grant reference.
35056567907SDavid Edmondson  */
35156567907SDavid Edmondson static grant_ref_t
35256567907SDavid Edmondson gref_get(xnf_t *xnfp)
35356567907SDavid Edmondson {
35456567907SDavid Edmondson 	grant_ref_t gref;
35556567907SDavid Edmondson 
35656567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_gref_lock);
35756567907SDavid Edmondson 
35856567907SDavid Edmondson 	do {
35956567907SDavid Edmondson 		gref = gnttab_claim_grant_reference(&xnfp->xnf_gref_head);
36056567907SDavid Edmondson 
36156567907SDavid Edmondson 	} while ((gref == INVALID_GRANT_REF) &&
36256567907SDavid Edmondson 	    (gnttab_alloc_grant_references(16, &xnfp->xnf_gref_head) == 0));
36356567907SDavid Edmondson 
36456567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_gref_lock);
36556567907SDavid Edmondson 
36656567907SDavid Edmondson 	if (gref == INVALID_GRANT_REF) {
36756567907SDavid Edmondson 		xnfp->xnf_stat_gref_failure++;
36856567907SDavid Edmondson 	} else {
369*1a5e258fSJosef 'Jeff' Sipek 		atomic_inc_64(&xnfp->xnf_stat_gref_outstanding);
37056567907SDavid Edmondson 		if (xnfp->xnf_stat_gref_outstanding > xnfp->xnf_stat_gref_peak)
37156567907SDavid Edmondson 			xnfp->xnf_stat_gref_peak =
37256567907SDavid Edmondson 			    xnfp->xnf_stat_gref_outstanding;
37356567907SDavid Edmondson 	}
37456567907SDavid Edmondson 
37556567907SDavid Edmondson 	return (gref);
37656567907SDavid Edmondson }
37756567907SDavid Edmondson 
37856567907SDavid Edmondson /*
37956567907SDavid Edmondson  * Release a grant reference.
38056567907SDavid Edmondson  */
38156567907SDavid Edmondson static void
38256567907SDavid Edmondson gref_put(xnf_t *xnfp, grant_ref_t gref)
38356567907SDavid Edmondson {
38456567907SDavid Edmondson 	ASSERT(gref != INVALID_GRANT_REF);
38556567907SDavid Edmondson 
38656567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_gref_lock);
38756567907SDavid Edmondson 	gnttab_release_grant_reference(&xnfp->xnf_gref_head, gref);
38856567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_gref_lock);
38956567907SDavid Edmondson 
390*1a5e258fSJosef 'Jeff' Sipek 	atomic_dec_64(&xnfp->xnf_stat_gref_outstanding);
39156567907SDavid Edmondson }
39256567907SDavid Edmondson 
39356567907SDavid Edmondson /*
39456567907SDavid Edmondson  * Acquire a transmit id.
39556567907SDavid Edmondson  */
39656567907SDavid Edmondson static xnf_txid_t *
39756567907SDavid Edmondson txid_get(xnf_t *xnfp)
39856567907SDavid Edmondson {
39956567907SDavid Edmondson 	xnf_txid_t *tidp;
40056567907SDavid Edmondson 
40156567907SDavid Edmondson 	ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
40256567907SDavid Edmondson 
40356567907SDavid Edmondson 	if (xnfp->xnf_tx_pkt_id_head == INVALID_TX_ID)
40456567907SDavid Edmondson 		return (NULL);
40556567907SDavid Edmondson 
40656567907SDavid Edmondson 	ASSERT(TX_ID_VALID(xnfp->xnf_tx_pkt_id_head));
40756567907SDavid Edmondson 
40856567907SDavid Edmondson 	tidp = TX_ID_TO_TXID(xnfp, xnfp->xnf_tx_pkt_id_head);
40956567907SDavid Edmondson 	xnfp->xnf_tx_pkt_id_head = tidp->next;
41056567907SDavid Edmondson 	tidp->next = INVALID_TX_ID;
41156567907SDavid Edmondson 
41256567907SDavid Edmondson 	ASSERT(tidp->txbuf == NULL);
41356567907SDavid Edmondson 
41456567907SDavid Edmondson 	return (tidp);
41556567907SDavid Edmondson }
41656567907SDavid Edmondson 
41756567907SDavid Edmondson /*
41856567907SDavid Edmondson  * Release a transmit id.
41956567907SDavid Edmondson  */
42056567907SDavid Edmondson static void
42156567907SDavid Edmondson txid_put(xnf_t *xnfp, xnf_txid_t *tidp)
42256567907SDavid Edmondson {
42356567907SDavid Edmondson 	ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
42456567907SDavid Edmondson 	ASSERT(TX_ID_VALID(tidp->id));
42556567907SDavid Edmondson 	ASSERT(tidp->next == INVALID_TX_ID);
42656567907SDavid Edmondson 
42756567907SDavid Edmondson 	tidp->txbuf = NULL;
42856567907SDavid Edmondson 	tidp->next = xnfp->xnf_tx_pkt_id_head;
42956567907SDavid Edmondson 	xnfp->xnf_tx_pkt_id_head = tidp->id;
43056567907SDavid Edmondson }
43156567907SDavid Edmondson 
43256567907SDavid Edmondson /*
43356567907SDavid Edmondson  * Get `wanted' slots in the transmit ring, waiting for at least that
43456567907SDavid Edmondson  * number if `wait' is B_TRUE. Force the ring to be cleaned by setting
43556567907SDavid Edmondson  * `wanted' to zero.
43656567907SDavid Edmondson  *
43756567907SDavid Edmondson  * Return the number of slots available.
43856567907SDavid Edmondson  */
43956567907SDavid Edmondson static int
44056567907SDavid Edmondson tx_slots_get(xnf_t *xnfp, int wanted, boolean_t wait)
44156567907SDavid Edmondson {
44256567907SDavid Edmondson 	int slotsfree;
44356567907SDavid Edmondson 	boolean_t forced_clean = (wanted == 0);
44456567907SDavid Edmondson 
44556567907SDavid Edmondson 	ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
44656567907SDavid Edmondson 
44756567907SDavid Edmondson 	/* LINTED: constant in conditional context */
44856567907SDavid Edmondson 	while (B_TRUE) {
44956567907SDavid Edmondson 		slotsfree = RING_FREE_REQUESTS(&xnfp->xnf_tx_ring);
45056567907SDavid Edmondson 
45156567907SDavid Edmondson 		if ((slotsfree < wanted) || forced_clean)
45256567907SDavid Edmondson 			slotsfree = xnf_tx_clean_ring(xnfp);
45356567907SDavid Edmondson 
45456567907SDavid Edmondson 		/*
45556567907SDavid Edmondson 		 * If there are more than we need free, tell other
45656567907SDavid Edmondson 		 * people to come looking again. We hold txlock, so we
45756567907SDavid Edmondson 		 * are able to take our slots before anyone else runs.
45856567907SDavid Edmondson 		 */
45956567907SDavid Edmondson 		if (slotsfree > wanted)
46056567907SDavid Edmondson 			cv_broadcast(&xnfp->xnf_cv_tx_slots);
46156567907SDavid Edmondson 
46256567907SDavid Edmondson 		if (slotsfree >= wanted)
46356567907SDavid Edmondson 			break;
46456567907SDavid Edmondson 
46556567907SDavid Edmondson 		if (!wait)
46656567907SDavid Edmondson 			break;
46756567907SDavid Edmondson 
46856567907SDavid Edmondson 		cv_wait(&xnfp->xnf_cv_tx_slots, &xnfp->xnf_txlock);
46956567907SDavid Edmondson 	}
47056567907SDavid Edmondson 
47156567907SDavid Edmondson 	ASSERT(slotsfree <= RING_SIZE(&(xnfp->xnf_tx_ring)));
47256567907SDavid Edmondson 
47356567907SDavid Edmondson 	return (slotsfree);
47456567907SDavid Edmondson }
47556567907SDavid Edmondson 
476843e1988Sjohnlev static int
477843e1988Sjohnlev xnf_setup_rings(xnf_t *xnfp)
478843e1988Sjohnlev {
479843e1988Sjohnlev 	domid_t			oeid;
48056567907SDavid Edmondson 	struct xenbus_device	*xsd;
48156567907SDavid Edmondson 	RING_IDX		i;
48256567907SDavid Edmondson 	int			err;
48356567907SDavid Edmondson 	xnf_txid_t		*tidp;
48456567907SDavid Edmondson 	xnf_buf_t **bdescp;
485843e1988Sjohnlev 
486551bc2a6Smrj 	oeid = xvdi_get_oeid(xnfp->xnf_devinfo);
487551bc2a6Smrj 	xsd = xvdi_get_xsd(xnfp->xnf_devinfo);
488843e1988Sjohnlev 
48956567907SDavid Edmondson 	if (xnfp->xnf_tx_ring_ref != INVALID_GRANT_REF)
490551bc2a6Smrj 		gnttab_end_foreign_access(xnfp->xnf_tx_ring_ref, 0, 0);
491843e1988Sjohnlev 
492843e1988Sjohnlev 	err = gnttab_grant_foreign_access(oeid,
493551bc2a6Smrj 	    xnf_btop(pa_to_ma(xnfp->xnf_tx_ring_phys_addr)), 0);
494843e1988Sjohnlev 	if (err <= 0) {
495843e1988Sjohnlev 		err = -err;
496843e1988Sjohnlev 		xenbus_dev_error(xsd, err, "granting access to tx ring page");
497843e1988Sjohnlev 		goto out;
498843e1988Sjohnlev 	}
499551bc2a6Smrj 	xnfp->xnf_tx_ring_ref = (grant_ref_t)err;
500843e1988Sjohnlev 
50156567907SDavid Edmondson 	if (xnfp->xnf_rx_ring_ref != INVALID_GRANT_REF)
502551bc2a6Smrj 		gnttab_end_foreign_access(xnfp->xnf_rx_ring_ref, 0, 0);
503843e1988Sjohnlev 
504843e1988Sjohnlev 	err = gnttab_grant_foreign_access(oeid,
505551bc2a6Smrj 	    xnf_btop(pa_to_ma(xnfp->xnf_rx_ring_phys_addr)), 0);
506843e1988Sjohnlev 	if (err <= 0) {
507843e1988Sjohnlev 		err = -err;
508843e1988Sjohnlev 		xenbus_dev_error(xsd, err, "granting access to rx ring page");
509843e1988Sjohnlev 		goto out;
510843e1988Sjohnlev 	}
511551bc2a6Smrj 	xnfp->xnf_rx_ring_ref = (grant_ref_t)err;
512843e1988Sjohnlev 
513551bc2a6Smrj 	mutex_enter(&xnfp->xnf_txlock);
514843e1988Sjohnlev 
51556567907SDavid Edmondson 	/*
51656567907SDavid Edmondson 	 * Setup/cleanup the TX ring.  Note that this can lose packets
51756567907SDavid Edmondson 	 * after a resume, but we expect to stagger on.
51856567907SDavid Edmondson 	 */
51956567907SDavid Edmondson 	xnfp->xnf_tx_pkt_id_head = INVALID_TX_ID; /* I.e. emtpy list. */
52056567907SDavid Edmondson 	for (i = 0, tidp = &xnfp->xnf_tx_pkt_id[0];
52156567907SDavid Edmondson 	    i < NET_TX_RING_SIZE;
52256567907SDavid Edmondson 	    i++, tidp++) {
52356567907SDavid Edmondson 		xnf_txbuf_t *txp;
524843e1988Sjohnlev 
52556567907SDavid Edmondson 		tidp->id = i;
526843e1988Sjohnlev 
52756567907SDavid Edmondson 		txp = tidp->txbuf;
52856567907SDavid Edmondson 		if (txp == NULL) {
52956567907SDavid Edmondson 			tidp->next = INVALID_TX_ID; /* Appease txid_put(). */
53056567907SDavid Edmondson 			txid_put(xnfp, tidp);
531843e1988Sjohnlev 			continue;
532843e1988Sjohnlev 		}
533843e1988Sjohnlev 
53456567907SDavid Edmondson 		ASSERT(txp->tx_txreq.gref != INVALID_GRANT_REF);
53556567907SDavid Edmondson 		ASSERT(txp->tx_mp != NULL);
536843e1988Sjohnlev 
53756567907SDavid Edmondson 		switch (txp->tx_type) {
53856567907SDavid Edmondson 		case TX_DATA:
53956567907SDavid Edmondson 			VERIFY(gnttab_query_foreign_access(txp->tx_txreq.gref)
54056567907SDavid Edmondson 			    == 0);
541843e1988Sjohnlev 
54256567907SDavid Edmondson 			if (txp->tx_bdesc == NULL) {
54356567907SDavid Edmondson 				(void) gnttab_end_foreign_access_ref(
54456567907SDavid Edmondson 				    txp->tx_txreq.gref, 1);
54556567907SDavid Edmondson 				gref_put(xnfp, txp->tx_txreq.gref);
54656567907SDavid Edmondson 				(void) ddi_dma_unbind_handle(
54756567907SDavid Edmondson 				    txp->tx_dma_handle);
54856567907SDavid Edmondson 			} else {
54956567907SDavid Edmondson 				xnf_buf_put(xnfp, txp->tx_bdesc, B_TRUE);
550843e1988Sjohnlev 			}
551843e1988Sjohnlev 
55256567907SDavid Edmondson 			freemsg(txp->tx_mp);
55356567907SDavid Edmondson 			txid_put(xnfp, tidp);
55456567907SDavid Edmondson 			kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
555843e1988Sjohnlev 
55656567907SDavid Edmondson 			break;
55756567907SDavid Edmondson 
55856567907SDavid Edmondson 		case TX_MCAST_REQ:
55956567907SDavid Edmondson 			txp->tx_type = TX_MCAST_RSP;
56056567907SDavid Edmondson 			txp->tx_status = NETIF_RSP_DROPPED;
56156567907SDavid Edmondson 			cv_broadcast(&xnfp->xnf_cv_multicast);
56256567907SDavid Edmondson 
56356567907SDavid Edmondson 			/*
56456567907SDavid Edmondson 			 * The request consumed two slots in the ring,
56556567907SDavid Edmondson 			 * yet only a single xnf_txid_t is used. Step
56656567907SDavid Edmondson 			 * over the empty slot.
56756567907SDavid Edmondson 			 */
56856567907SDavid Edmondson 			i++;
56956567907SDavid Edmondson 			ASSERT(i < NET_TX_RING_SIZE);
57056567907SDavid Edmondson 
57156567907SDavid Edmondson 			break;
57256567907SDavid Edmondson 
57356567907SDavid Edmondson 		case TX_MCAST_RSP:
57456567907SDavid Edmondson 			break;
57556567907SDavid Edmondson 		}
57656567907SDavid Edmondson 	}
577a859da42SDavid Edmondson 
578a859da42SDavid Edmondson 	/* LINTED: constant in conditional context */
579a859da42SDavid Edmondson 	SHARED_RING_INIT(xnfp->xnf_tx_ring.sring);
58056567907SDavid Edmondson 	/* LINTED: constant in conditional context */
58156567907SDavid Edmondson 	FRONT_RING_INIT(&xnfp->xnf_tx_ring,
58256567907SDavid Edmondson 	    xnfp->xnf_tx_ring.sring, PAGESIZE);
583843e1988Sjohnlev 
584551bc2a6Smrj 	mutex_exit(&xnfp->xnf_txlock);
585843e1988Sjohnlev 
58656567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_rxlock);
587551bc2a6Smrj 
588843e1988Sjohnlev 	/*
58956567907SDavid Edmondson 	 * Clean out any buffers currently posted to the receive ring
59056567907SDavid Edmondson 	 * before we reset it.
591843e1988Sjohnlev 	 */
59256567907SDavid Edmondson 	for (i = 0, bdescp = &xnfp->xnf_rx_pkt_info[0];
59356567907SDavid Edmondson 	    i < NET_RX_RING_SIZE;
59456567907SDavid Edmondson 	    i++, bdescp++) {
59556567907SDavid Edmondson 		if (*bdescp != NULL) {
59656567907SDavid Edmondson 			xnf_buf_put(xnfp, *bdescp, B_FALSE);
59756567907SDavid Edmondson 			*bdescp = NULL;
59856567907SDavid Edmondson 		}
59956567907SDavid Edmondson 	}
600a859da42SDavid Edmondson 
601a859da42SDavid Edmondson 	/* LINTED: constant in conditional context */
602a859da42SDavid Edmondson 	SHARED_RING_INIT(xnfp->xnf_rx_ring.sring);
60356567907SDavid Edmondson 	/* LINTED: constant in conditional context */
60456567907SDavid Edmondson 	FRONT_RING_INIT(&xnfp->xnf_rx_ring,
60556567907SDavid Edmondson 	    xnfp->xnf_rx_ring.sring, PAGESIZE);
606a859da42SDavid Edmondson 
60756567907SDavid Edmondson 	/*
60856567907SDavid Edmondson 	 * Fill the ring with buffers.
60956567907SDavid Edmondson 	 */
610843e1988Sjohnlev 	for (i = 0; i < NET_RX_RING_SIZE; i++) {
61156567907SDavid Edmondson 		xnf_buf_t *bdesc;
61256567907SDavid Edmondson 
61356567907SDavid Edmondson 		bdesc = xnf_buf_get(xnfp, KM_SLEEP, B_FALSE);
61456567907SDavid Edmondson 		VERIFY(bdesc != NULL);
61556567907SDavid Edmondson 		xnf_rxbuf_hang(xnfp, bdesc);
616843e1988Sjohnlev 	}
61756567907SDavid Edmondson 
618843e1988Sjohnlev 	/* LINTED: constant in conditional context */
619551bc2a6Smrj 	RING_PUSH_REQUESTS(&xnfp->xnf_rx_ring);
620843e1988Sjohnlev 
62156567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_rxlock);
622843e1988Sjohnlev 
623843e1988Sjohnlev 	return (0);
624843e1988Sjohnlev 
625843e1988Sjohnlev out:
62656567907SDavid Edmondson 	if (xnfp->xnf_tx_ring_ref != INVALID_GRANT_REF)
627551bc2a6Smrj 		gnttab_end_foreign_access(xnfp->xnf_tx_ring_ref, 0, 0);
62856567907SDavid Edmondson 	xnfp->xnf_tx_ring_ref = INVALID_GRANT_REF;
629843e1988Sjohnlev 
63056567907SDavid Edmondson 	if (xnfp->xnf_rx_ring_ref != INVALID_GRANT_REF)
631551bc2a6Smrj 		gnttab_end_foreign_access(xnfp->xnf_rx_ring_ref, 0, 0);
63256567907SDavid Edmondson 	xnfp->xnf_rx_ring_ref = INVALID_GRANT_REF;
633843e1988Sjohnlev 
634843e1988Sjohnlev 	return (err);
635843e1988Sjohnlev }
636843e1988Sjohnlev 
637843e1988Sjohnlev /*
638843e1988Sjohnlev  * Connect driver to back end, called to set up communication with
639843e1988Sjohnlev  * back end driver both initially and on resume after restore/migrate.
640843e1988Sjohnlev  */
641843e1988Sjohnlev void
642843e1988Sjohnlev xnf_be_connect(xnf_t *xnfp)
643843e1988Sjohnlev {
644843e1988Sjohnlev 	const char	*message;
645843e1988Sjohnlev 	xenbus_transaction_t xbt;
646843e1988Sjohnlev 	struct		xenbus_device *xsd;
647843e1988Sjohnlev 	char		*xsname;
648a390c5f4Scz147101 	int		err;
649843e1988Sjohnlev 
650551bc2a6Smrj 	ASSERT(!xnfp->xnf_connected);
651843e1988Sjohnlev 
652551bc2a6Smrj 	xsd = xvdi_get_xsd(xnfp->xnf_devinfo);
653551bc2a6Smrj 	xsname = xvdi_get_xsname(xnfp->xnf_devinfo);
654843e1988Sjohnlev 
655843e1988Sjohnlev 	err = xnf_setup_rings(xnfp);
656843e1988Sjohnlev 	if (err != 0) {
657843e1988Sjohnlev 		cmn_err(CE_WARN, "failed to set up tx/rx rings");
658843e1988Sjohnlev 		xenbus_dev_error(xsd, err, "setting up ring");
659843e1988Sjohnlev 		return;
660843e1988Sjohnlev 	}
661843e1988Sjohnlev 
662843e1988Sjohnlev again:
663843e1988Sjohnlev 	err = xenbus_transaction_start(&xbt);
664843e1988Sjohnlev 	if (err != 0) {
665843e1988Sjohnlev 		xenbus_dev_error(xsd, EIO, "starting transaction");
666843e1988Sjohnlev 		return;
667843e1988Sjohnlev 	}
668843e1988Sjohnlev 
669843e1988Sjohnlev 	err = xenbus_printf(xbt, xsname, "tx-ring-ref", "%u",
670551bc2a6Smrj 	    xnfp->xnf_tx_ring_ref);
671843e1988Sjohnlev 	if (err != 0) {
672843e1988Sjohnlev 		message = "writing tx ring-ref";
673843e1988Sjohnlev 		goto abort_transaction;
674843e1988Sjohnlev 	}
675843e1988Sjohnlev 
676843e1988Sjohnlev 	err = xenbus_printf(xbt, xsname, "rx-ring-ref", "%u",
677551bc2a6Smrj 	    xnfp->xnf_rx_ring_ref);
678843e1988Sjohnlev 	if (err != 0) {
679843e1988Sjohnlev 		message = "writing rx ring-ref";
680843e1988Sjohnlev 		goto abort_transaction;
681843e1988Sjohnlev 	}
682843e1988Sjohnlev 
683551bc2a6Smrj 	err = xenbus_printf(xbt, xsname, "event-channel", "%u",
684551bc2a6Smrj 	    xnfp->xnf_evtchn);
685843e1988Sjohnlev 	if (err != 0) {
686843e1988Sjohnlev 		message = "writing event-channel";
687843e1988Sjohnlev 		goto abort_transaction;
688843e1988Sjohnlev 	}
689843e1988Sjohnlev 
690843e1988Sjohnlev 	err = xenbus_printf(xbt, xsname, "feature-rx-notify", "%d", 1);
691843e1988Sjohnlev 	if (err != 0) {
692843e1988Sjohnlev 		message = "writing feature-rx-notify";
693843e1988Sjohnlev 		goto abort_transaction;
694843e1988Sjohnlev 	}
695843e1988Sjohnlev 
69656567907SDavid Edmondson 	err = xenbus_printf(xbt, xsname, "request-rx-copy", "%d", 1);
697551bc2a6Smrj 	if (err != 0) {
698551bc2a6Smrj 		message = "writing request-rx-copy";
699551bc2a6Smrj 		goto abort_transaction;
700551bc2a6Smrj 	}
701843e1988Sjohnlev 
70256567907SDavid Edmondson 	if (xnfp->xnf_be_mcast_control) {
70356567907SDavid Edmondson 		err = xenbus_printf(xbt, xsname, "request-multicast-control",
70456567907SDavid Edmondson 		    "%d", 1);
705843e1988Sjohnlev 		if (err != 0) {
70656567907SDavid Edmondson 			message = "writing request-multicast-control";
70756567907SDavid Edmondson 			goto abort_transaction;
70856567907SDavid Edmondson 		}
70956567907SDavid Edmondson 	}
71056567907SDavid Edmondson 
71156567907SDavid Edmondson 	err = xvdi_switch_state(xnfp->xnf_devinfo, xbt, XenbusStateConnected);
71256567907SDavid Edmondson 	if (err != 0) {
71356567907SDavid Edmondson 		message = "switching state to XenbusStateConnected";
714843e1988Sjohnlev 		goto abort_transaction;
715843e1988Sjohnlev 	}
716843e1988Sjohnlev 
717843e1988Sjohnlev 	err = xenbus_transaction_end(xbt, 0);
718843e1988Sjohnlev 	if (err != 0) {
719843e1988Sjohnlev 		if (err == EAGAIN)
720843e1988Sjohnlev 			goto again;
721843e1988Sjohnlev 		xenbus_dev_error(xsd, err, "completing transaction");
722843e1988Sjohnlev 	}
723843e1988Sjohnlev 
724843e1988Sjohnlev 	return;
725843e1988Sjohnlev 
726843e1988Sjohnlev abort_transaction:
727843e1988Sjohnlev 	(void) xenbus_transaction_end(xbt, 1);
728843e1988Sjohnlev 	xenbus_dev_error(xsd, err, "%s", message);
729843e1988Sjohnlev }
730843e1988Sjohnlev 
731843e1988Sjohnlev /*
73256567907SDavid Edmondson  * Read configuration information from xenstore.
733a390c5f4Scz147101  */
734a390c5f4Scz147101 void
735a390c5f4Scz147101 xnf_read_config(xnf_t *xnfp)
736a390c5f4Scz147101 {
73756567907SDavid Edmondson 	int err, be_cap;
738a390c5f4Scz147101 	char mac[ETHERADDRL * 3];
73956567907SDavid Edmondson 	char *oename = xvdi_get_oename(xnfp->xnf_devinfo);
740a390c5f4Scz147101 
74156567907SDavid Edmondson 	err = xenbus_scanf(XBT_NULL, oename, "mac",
742a390c5f4Scz147101 	    "%s", (char *)&mac[0]);
743a390c5f4Scz147101 	if (err != 0) {
744a390c5f4Scz147101 		/*
745a390c5f4Scz147101 		 * bad: we're supposed to be set up with a proper mac
746a390c5f4Scz147101 		 * addr. at this point
747a390c5f4Scz147101 		 */
748a390c5f4Scz147101 		cmn_err(CE_WARN, "%s%d: no mac address",
749a390c5f4Scz147101 		    ddi_driver_name(xnfp->xnf_devinfo),
750a390c5f4Scz147101 		    ddi_get_instance(xnfp->xnf_devinfo));
751a390c5f4Scz147101 			return;
752a390c5f4Scz147101 	}
753a390c5f4Scz147101 	if (ether_aton(mac, xnfp->xnf_mac_addr) != ETHERADDRL) {
754a390c5f4Scz147101 		err = ENOENT;
755a390c5f4Scz147101 		xenbus_dev_error(xvdi_get_xsd(xnfp->xnf_devinfo), ENOENT,
756a390c5f4Scz147101 		    "parsing %s/mac", xvdi_get_xsname(xnfp->xnf_devinfo));
757a390c5f4Scz147101 		return;
758a390c5f4Scz147101 	}
759a390c5f4Scz147101 
76056567907SDavid Edmondson 	err = xenbus_scanf(XBT_NULL, oename,
76156567907SDavid Edmondson 	    "feature-rx-copy", "%d", &be_cap);
762a390c5f4Scz147101 	/*
763a390c5f4Scz147101 	 * If we fail to read the store we assume that the key is
764a390c5f4Scz147101 	 * absent, implying an older domain at the far end.  Older
76556567907SDavid Edmondson 	 * domains cannot do HV copy.
766a390c5f4Scz147101 	 */
767a390c5f4Scz147101 	if (err != 0)
76856567907SDavid Edmondson 		be_cap = 0;
76956567907SDavid Edmondson 	xnfp->xnf_be_rx_copy = (be_cap != 0);
77056567907SDavid Edmondson 
77156567907SDavid Edmondson 	err = xenbus_scanf(XBT_NULL, oename,
77256567907SDavid Edmondson 	    "feature-multicast-control", "%d", &be_cap);
773a390c5f4Scz147101 	/*
77456567907SDavid Edmondson 	 * If we fail to read the store we assume that the key is
77556567907SDavid Edmondson 	 * absent, implying an older domain at the far end.  Older
77656567907SDavid Edmondson 	 * domains do not support multicast control.
777a390c5f4Scz147101 	 */
77856567907SDavid Edmondson 	if (err != 0)
77956567907SDavid Edmondson 		be_cap = 0;
78056567907SDavid Edmondson 	xnfp->xnf_be_mcast_control = (be_cap != 0) && xnf_multicast_control;
781a390c5f4Scz147101 }
782a390c5f4Scz147101 
783a390c5f4Scz147101 /*
784843e1988Sjohnlev  *  attach(9E) -- Attach a device to the system
785843e1988Sjohnlev  */
786843e1988Sjohnlev static int
787843e1988Sjohnlev xnf_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
788843e1988Sjohnlev {
789843e1988Sjohnlev 	mac_register_t *macp;
790843e1988Sjohnlev 	xnf_t *xnfp;
791843e1988Sjohnlev 	int err;
79256567907SDavid Edmondson 	char cachename[32];
793843e1988Sjohnlev 
794843e1988Sjohnlev #ifdef XNF_DEBUG
79556567907SDavid Edmondson 	if (xnf_debug & XNF_DEBUG_DDI)
796843e1988Sjohnlev 		printf("xnf%d: attach(0x%p)\n", ddi_get_instance(devinfo),
797843e1988Sjohnlev 		    (void *)devinfo);
798843e1988Sjohnlev #endif
799843e1988Sjohnlev 
800843e1988Sjohnlev 	switch (cmd) {
801843e1988Sjohnlev 	case DDI_RESUME:
802843e1988Sjohnlev 		xnfp = ddi_get_driver_private(devinfo);
80356567907SDavid Edmondson 		xnfp->xnf_gen++;
804843e1988Sjohnlev 
805843e1988Sjohnlev 		(void) xvdi_resume(devinfo);
806843e1988Sjohnlev 		(void) xvdi_alloc_evtchn(devinfo);
807551bc2a6Smrj 		xnfp->xnf_evtchn = xvdi_get_evtchn(devinfo);
808551bc2a6Smrj #ifdef XPV_HVM_DRIVER
809551bc2a6Smrj 		ec_bind_evtchn_to_handler(xnfp->xnf_evtchn, IPL_VIF, xnf_intr,
810551bc2a6Smrj 		    xnfp);
811551bc2a6Smrj #else
812843e1988Sjohnlev 		(void) ddi_add_intr(devinfo, 0, NULL, NULL, xnf_intr,
813843e1988Sjohnlev 		    (caddr_t)xnfp);
814551bc2a6Smrj #endif
815843e1988Sjohnlev 		return (DDI_SUCCESS);
816843e1988Sjohnlev 
817843e1988Sjohnlev 	case DDI_ATTACH:
818843e1988Sjohnlev 		break;
819843e1988Sjohnlev 
820843e1988Sjohnlev 	default:
821843e1988Sjohnlev 		return (DDI_FAILURE);
822843e1988Sjohnlev 	}
823843e1988Sjohnlev 
824843e1988Sjohnlev 	/*
825843e1988Sjohnlev 	 *  Allocate gld_mac_info_t and xnf_instance structures
826843e1988Sjohnlev 	 */
827843e1988Sjohnlev 	macp = mac_alloc(MAC_VERSION);
828843e1988Sjohnlev 	if (macp == NULL)
829843e1988Sjohnlev 		return (DDI_FAILURE);
830843e1988Sjohnlev 	xnfp = kmem_zalloc(sizeof (*xnfp), KM_SLEEP);
831843e1988Sjohnlev 
832843e1988Sjohnlev 	macp->m_dip = devinfo;
833843e1988Sjohnlev 	macp->m_driver = xnfp;
834551bc2a6Smrj 	xnfp->xnf_devinfo = devinfo;
835843e1988Sjohnlev 
836843e1988Sjohnlev 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
837551bc2a6Smrj 	macp->m_src_addr = xnfp->xnf_mac_addr;
838843e1988Sjohnlev 	macp->m_callbacks = &xnf_callbacks;
839843e1988Sjohnlev 	macp->m_min_sdu = 0;
840843e1988Sjohnlev 	macp->m_max_sdu = XNF_MAXPKT;
841843e1988Sjohnlev 
842551bc2a6Smrj 	xnfp->xnf_running = B_FALSE;
843551bc2a6Smrj 	xnfp->xnf_connected = B_FALSE;
84456567907SDavid Edmondson 	xnfp->xnf_be_rx_copy = B_FALSE;
84556567907SDavid Edmondson 	xnfp->xnf_be_mcast_control = B_FALSE;
84664c5e63cSDavid Edmondson 	xnfp->xnf_need_sched = B_FALSE;
847551bc2a6Smrj 
84856567907SDavid Edmondson 	xnfp->xnf_rx_head = NULL;
84956567907SDavid Edmondson 	xnfp->xnf_rx_tail = NULL;
85056567907SDavid Edmondson 	xnfp->xnf_rx_new_buffers_posted = B_FALSE;
85156567907SDavid Edmondson 
852551bc2a6Smrj #ifdef XPV_HVM_DRIVER
8536eb35ee7Srab 	/*
8546eb35ee7Srab 	 * Report our version to dom0.
8556eb35ee7Srab 	 */
856349b53ddSStuart Maybee 	if (xenbus_printf(XBT_NULL, "guest/xnf", "version", "%d",
8576eb35ee7Srab 	    HVMPV_XNF_VERS))
8586eb35ee7Srab 		cmn_err(CE_WARN, "xnf: couldn't write version\n");
859551bc2a6Smrj #endif
860843e1988Sjohnlev 
861843e1988Sjohnlev 	/*
862843e1988Sjohnlev 	 * Get the iblock cookie with which to initialize the mutexes.
863843e1988Sjohnlev 	 */
864551bc2a6Smrj 	if (ddi_get_iblock_cookie(devinfo, 0, &xnfp->xnf_icookie)
865843e1988Sjohnlev 	    != DDI_SUCCESS)
866843e1988Sjohnlev 		goto failure;
86756567907SDavid Edmondson 
868551bc2a6Smrj 	mutex_init(&xnfp->xnf_txlock,
869551bc2a6Smrj 	    NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87056567907SDavid Edmondson 	mutex_init(&xnfp->xnf_rxlock,
871551bc2a6Smrj 	    NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87256567907SDavid Edmondson 	mutex_init(&xnfp->xnf_schedlock,
87356567907SDavid Edmondson 	    NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87456567907SDavid Edmondson 	mutex_init(&xnfp->xnf_gref_lock,
87556567907SDavid Edmondson 	    NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
876843e1988Sjohnlev 
87756567907SDavid Edmondson 	cv_init(&xnfp->xnf_cv_state, NULL, CV_DEFAULT, NULL);
87856567907SDavid Edmondson 	cv_init(&xnfp->xnf_cv_multicast, NULL, CV_DEFAULT, NULL);
87956567907SDavid Edmondson 	cv_init(&xnfp->xnf_cv_tx_slots, NULL, CV_DEFAULT, NULL);
88056567907SDavid Edmondson 
88156567907SDavid Edmondson 	(void) sprintf(cachename, "xnf_buf_cache_%d",
88256567907SDavid Edmondson 	    ddi_get_instance(devinfo));
88356567907SDavid Edmondson 	xnfp->xnf_buf_cache = kmem_cache_create(cachename,
88456567907SDavid Edmondson 	    sizeof (xnf_buf_t), 0,
88556567907SDavid Edmondson 	    xnf_buf_constructor, xnf_buf_destructor,
88656567907SDavid Edmondson 	    NULL, xnfp, NULL, 0);
88756567907SDavid Edmondson 	if (xnfp->xnf_buf_cache == NULL)
88856567907SDavid Edmondson 		goto failure_0;
88956567907SDavid Edmondson 
89056567907SDavid Edmondson 	(void) sprintf(cachename, "xnf_tx_buf_cache_%d",
89156567907SDavid Edmondson 	    ddi_get_instance(devinfo));
89256567907SDavid Edmondson 	xnfp->xnf_tx_buf_cache = kmem_cache_create(cachename,
89356567907SDavid Edmondson 	    sizeof (xnf_txbuf_t), 0,
89456567907SDavid Edmondson 	    xnf_tx_buf_constructor, xnf_tx_buf_destructor,
89556567907SDavid Edmondson 	    NULL, xnfp, NULL, 0);
89656567907SDavid Edmondson 	if (xnfp->xnf_tx_buf_cache == NULL)
897551bc2a6Smrj 		goto failure_1;
89856567907SDavid Edmondson 
89956567907SDavid Edmondson 	xnfp->xnf_gref_head = INVALID_GRANT_REF;
90056567907SDavid Edmondson 
901843e1988Sjohnlev 	if (xnf_alloc_dma_resources(xnfp) == DDI_FAILURE) {
902843e1988Sjohnlev 		cmn_err(CE_WARN, "xnf%d: failed to allocate and initialize "
903551bc2a6Smrj 		    "driver data structures",
904551bc2a6Smrj 		    ddi_get_instance(xnfp->xnf_devinfo));
90556567907SDavid Edmondson 		goto failure_2;
906843e1988Sjohnlev 	}
907843e1988Sjohnlev 
908551bc2a6Smrj 	xnfp->xnf_rx_ring.sring->rsp_event =
909551bc2a6Smrj 	    xnfp->xnf_tx_ring.sring->rsp_event = 1;
910843e1988Sjohnlev 
91156567907SDavid Edmondson 	xnfp->xnf_tx_ring_ref = INVALID_GRANT_REF;
91256567907SDavid Edmondson 	xnfp->xnf_rx_ring_ref = INVALID_GRANT_REF;
913843e1988Sjohnlev 
914843e1988Sjohnlev 	/* set driver private pointer now */
915843e1988Sjohnlev 	ddi_set_driver_private(devinfo, xnfp);
916843e1988Sjohnlev 
917843e1988Sjohnlev 	if (!xnf_kstat_init(xnfp))
91856567907SDavid Edmondson 		goto failure_3;
919843e1988Sjohnlev 
920843e1988Sjohnlev 	/*
921843e1988Sjohnlev 	 * Allocate an event channel, add the interrupt handler and
922843e1988Sjohnlev 	 * bind it to the event channel.
923843e1988Sjohnlev 	 */
924843e1988Sjohnlev 	(void) xvdi_alloc_evtchn(devinfo);
925551bc2a6Smrj 	xnfp->xnf_evtchn = xvdi_get_evtchn(devinfo);
926551bc2a6Smrj #ifdef XPV_HVM_DRIVER
927551bc2a6Smrj 	ec_bind_evtchn_to_handler(xnfp->xnf_evtchn, IPL_VIF, xnf_intr, xnfp);
928551bc2a6Smrj #else
929843e1988Sjohnlev 	(void) ddi_add_intr(devinfo, 0, NULL, NULL, xnf_intr, (caddr_t)xnfp);
930551bc2a6Smrj #endif
931843e1988Sjohnlev 
932551bc2a6Smrj 	err = mac_register(macp, &xnfp->xnf_mh);
933843e1988Sjohnlev 	mac_free(macp);
934843e1988Sjohnlev 	macp = NULL;
935843e1988Sjohnlev 	if (err != 0)
93656567907SDavid Edmondson 		goto failure_4;
93756567907SDavid Edmondson 
93856567907SDavid Edmondson 	if (xvdi_add_event_handler(devinfo, XS_OE_STATE, oe_state_change, NULL)
93956567907SDavid Edmondson 	    != DDI_SUCCESS)
94056567907SDavid Edmondson 		goto failure_5;
941843e1988Sjohnlev 
942fb07ba1cSfvdl #ifdef XPV_HVM_DRIVER
943fb07ba1cSfvdl 	/*
944fb07ba1cSfvdl 	 * In the HVM case, this driver essentially replaces a driver for
945fb07ba1cSfvdl 	 * a 'real' PCI NIC. Without the "model" property set to
946fb07ba1cSfvdl 	 * "Ethernet controller", like the PCI code does, netbooting does
947fb07ba1cSfvdl 	 * not work correctly, as strplumb_get_netdev_path() will not find
948fb07ba1cSfvdl 	 * this interface.
949fb07ba1cSfvdl 	 */
950fb07ba1cSfvdl 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, devinfo, "model",
951fb07ba1cSfvdl 	    "Ethernet controller");
952fb07ba1cSfvdl #endif
953fb07ba1cSfvdl 
95456567907SDavid Edmondson #ifdef XNF_DEBUG
95556567907SDavid Edmondson 	if (xnf_debug_instance == NULL)
95656567907SDavid Edmondson 		xnf_debug_instance = xnfp;
95756567907SDavid Edmondson #endif
958a390c5f4Scz147101 
959843e1988Sjohnlev 	return (DDI_SUCCESS);
960843e1988Sjohnlev 
96156567907SDavid Edmondson failure_5:
9621665cdc0SDavid Edmondson 	(void) mac_unregister(xnfp->xnf_mh);
96356567907SDavid Edmondson 
96456567907SDavid Edmondson failure_4:
965551bc2a6Smrj #ifdef XPV_HVM_DRIVER
966551bc2a6Smrj 	ec_unbind_evtchn(xnfp->xnf_evtchn);
967ea8190a2Ssmaybe 	xvdi_free_evtchn(devinfo);
968551bc2a6Smrj #else
969551bc2a6Smrj 	ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
970551bc2a6Smrj #endif
971551bc2a6Smrj 	xnfp->xnf_evtchn = INVALID_EVTCHN;
97256567907SDavid Edmondson 	kstat_delete(xnfp->xnf_kstat_aux);
97356567907SDavid Edmondson 
97456567907SDavid Edmondson failure_3:
97556567907SDavid Edmondson 	xnf_release_dma_resources(xnfp);
976843e1988Sjohnlev 
977a390c5f4Scz147101 failure_2:
97856567907SDavid Edmondson 	kmem_cache_destroy(xnfp->xnf_tx_buf_cache);
979a390c5f4Scz147101 
980551bc2a6Smrj failure_1:
98156567907SDavid Edmondson 	kmem_cache_destroy(xnfp->xnf_buf_cache);
98256567907SDavid Edmondson 
98356567907SDavid Edmondson failure_0:
98456567907SDavid Edmondson 	cv_destroy(&xnfp->xnf_cv_tx_slots);
98556567907SDavid Edmondson 	cv_destroy(&xnfp->xnf_cv_multicast);
98656567907SDavid Edmondson 	cv_destroy(&xnfp->xnf_cv_state);
98756567907SDavid Edmondson 
98856567907SDavid Edmondson 	mutex_destroy(&xnfp->xnf_gref_lock);
98956567907SDavid Edmondson 	mutex_destroy(&xnfp->xnf_schedlock);
99056567907SDavid Edmondson 	mutex_destroy(&xnfp->xnf_rxlock);
991551bc2a6Smrj 	mutex_destroy(&xnfp->xnf_txlock);
992843e1988Sjohnlev 
993843e1988Sjohnlev failure:
994843e1988Sjohnlev 	kmem_free(xnfp, sizeof (*xnfp));
995843e1988Sjohnlev 	if (macp != NULL)
996843e1988Sjohnlev 		mac_free(macp);
997843e1988Sjohnlev 
998843e1988Sjohnlev 	return (DDI_FAILURE);
999843e1988Sjohnlev }
1000843e1988Sjohnlev 
1001843e1988Sjohnlev /*  detach(9E) -- Detach a device from the system */
1002843e1988Sjohnlev static int
1003843e1988Sjohnlev xnf_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
1004843e1988Sjohnlev {
1005843e1988Sjohnlev 	xnf_t *xnfp;		/* Our private device info */
1006843e1988Sjohnlev 
1007843e1988Sjohnlev #ifdef XNF_DEBUG
100856567907SDavid Edmondson 	if (xnf_debug & XNF_DEBUG_DDI)
1009843e1988Sjohnlev 		printf("xnf_detach(0x%p)\n", (void *)devinfo);
1010843e1988Sjohnlev #endif
1011843e1988Sjohnlev 
1012843e1988Sjohnlev 	xnfp = ddi_get_driver_private(devinfo);
1013843e1988Sjohnlev 
1014843e1988Sjohnlev 	switch (cmd) {
1015843e1988Sjohnlev 	case DDI_SUSPEND:
1016551bc2a6Smrj #ifdef XPV_HVM_DRIVER
1017551bc2a6Smrj 		ec_unbind_evtchn(xnfp->xnf_evtchn);
1018ea8190a2Ssmaybe 		xvdi_free_evtchn(devinfo);
1019551bc2a6Smrj #else
1020551bc2a6Smrj 		ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
1021551bc2a6Smrj #endif
1022843e1988Sjohnlev 
1023843e1988Sjohnlev 		xvdi_suspend(devinfo);
1024843e1988Sjohnlev 
102556567907SDavid Edmondson 		mutex_enter(&xnfp->xnf_rxlock);
1026551bc2a6Smrj 		mutex_enter(&xnfp->xnf_txlock);
1027843e1988Sjohnlev 
1028551bc2a6Smrj 		xnfp->xnf_evtchn = INVALID_EVTCHN;
1029551bc2a6Smrj 		xnfp->xnf_connected = B_FALSE;
1030551bc2a6Smrj 		mutex_exit(&xnfp->xnf_txlock);
103156567907SDavid Edmondson 		mutex_exit(&xnfp->xnf_rxlock);
10324bae950fSMax zhen 
10334bae950fSMax zhen 		/* claim link to be down after disconnect */
10344bae950fSMax zhen 		mac_link_update(xnfp->xnf_mh, LINK_STATE_DOWN);
1035843e1988Sjohnlev 		return (DDI_SUCCESS);
1036843e1988Sjohnlev 
1037843e1988Sjohnlev 	case DDI_DETACH:
1038843e1988Sjohnlev 		break;
1039843e1988Sjohnlev 
1040843e1988Sjohnlev 	default:
1041843e1988Sjohnlev 		return (DDI_FAILURE);
1042843e1988Sjohnlev 	}
1043843e1988Sjohnlev 
1044551bc2a6Smrj 	if (xnfp->xnf_connected)
1045843e1988Sjohnlev 		return (DDI_FAILURE);
1046843e1988Sjohnlev 
104756567907SDavid Edmondson 	/*
104856567907SDavid Edmondson 	 * Cannot detach if we have xnf_buf_t outstanding.
104956567907SDavid Edmondson 	 */
105056567907SDavid Edmondson 	if (xnfp->xnf_stat_buf_allocated > 0)
1051843e1988Sjohnlev 		return (DDI_FAILURE);
1052843e1988Sjohnlev 
1053551bc2a6Smrj 	if (mac_unregister(xnfp->xnf_mh) != 0)
1054843e1988Sjohnlev 		return (DDI_FAILURE);
1055843e1988Sjohnlev 
1056a390c5f4Scz147101 	kstat_delete(xnfp->xnf_kstat_aux);
1057a390c5f4Scz147101 
1058843e1988Sjohnlev 	/* Stop the receiver */
1059843e1988Sjohnlev 	xnf_stop(xnfp);
1060843e1988Sjohnlev 
1061843e1988Sjohnlev 	xvdi_remove_event_handler(devinfo, XS_OE_STATE);
1062843e1988Sjohnlev 
1063843e1988Sjohnlev 	/* Remove the interrupt */
1064551bc2a6Smrj #ifdef XPV_HVM_DRIVER
1065551bc2a6Smrj 	ec_unbind_evtchn(xnfp->xnf_evtchn);
1066ea8190a2Ssmaybe 	xvdi_free_evtchn(devinfo);
1067551bc2a6Smrj #else
1068551bc2a6Smrj 	ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
1069551bc2a6Smrj #endif
1070843e1988Sjohnlev 
1071843e1988Sjohnlev 	/* Release any pending xmit mblks */
1072843e1988Sjohnlev 	xnf_release_mblks(xnfp);
1073843e1988Sjohnlev 
1074843e1988Sjohnlev 	/* Release all DMA resources */
1075843e1988Sjohnlev 	xnf_release_dma_resources(xnfp);
1076843e1988Sjohnlev 
107756567907SDavid Edmondson 	cv_destroy(&xnfp->xnf_cv_tx_slots);
107856567907SDavid Edmondson 	cv_destroy(&xnfp->xnf_cv_multicast);
107956567907SDavid Edmondson 	cv_destroy(&xnfp->xnf_cv_state);
108056567907SDavid Edmondson 
108156567907SDavid Edmondson 	kmem_cache_destroy(xnfp->xnf_tx_buf_cache);
108256567907SDavid Edmondson 	kmem_cache_destroy(xnfp->xnf_buf_cache);
108356567907SDavid Edmondson 
108456567907SDavid Edmondson 	mutex_destroy(&xnfp->xnf_gref_lock);
108556567907SDavid Edmondson 	mutex_destroy(&xnfp->xnf_schedlock);
108656567907SDavid Edmondson 	mutex_destroy(&xnfp->xnf_rxlock);
1087551bc2a6Smrj 	mutex_destroy(&xnfp->xnf_txlock);
1088843e1988Sjohnlev 
1089843e1988Sjohnlev 	kmem_free(xnfp, sizeof (*xnfp));
1090843e1988Sjohnlev 
1091843e1988Sjohnlev 	return (DDI_SUCCESS);
1092843e1988Sjohnlev }
1093843e1988Sjohnlev 
1094843e1988Sjohnlev /*
1095843e1988Sjohnlev  *  xnf_set_mac_addr() -- set the physical network address on the board.
1096843e1988Sjohnlev  */
1097843e1988Sjohnlev static int
1098843e1988Sjohnlev xnf_set_mac_addr(void *arg, const uint8_t *macaddr)
1099843e1988Sjohnlev {
110056567907SDavid Edmondson 	_NOTE(ARGUNUSED(arg, macaddr));
1101843e1988Sjohnlev 
1102843e1988Sjohnlev 	/*
1103843e1988Sjohnlev 	 * We can't set our macaddr.
1104843e1988Sjohnlev 	 */
1105843e1988Sjohnlev 	return (ENOTSUP);
1106843e1988Sjohnlev }
1107843e1988Sjohnlev 
1108843e1988Sjohnlev /*
1109843e1988Sjohnlev  *  xnf_set_multicast() -- set (enable) or disable a multicast address.
1110843e1988Sjohnlev  *
1111843e1988Sjohnlev  *  Program the hardware to enable/disable the multicast address
111256567907SDavid Edmondson  *  in "mca".  Enable if "add" is true, disable if false.
1113843e1988Sjohnlev  */
1114843e1988Sjohnlev static int
1115843e1988Sjohnlev xnf_set_multicast(void *arg, boolean_t add, const uint8_t *mca)
1116843e1988Sjohnlev {
1117843e1988Sjohnlev 	xnf_t *xnfp = arg;
111856567907SDavid Edmondson 	xnf_txbuf_t *txp;
111956567907SDavid Edmondson 	int n_slots;
112056567907SDavid Edmondson 	RING_IDX slot;
112156567907SDavid Edmondson 	xnf_txid_t *tidp;
112256567907SDavid Edmondson 	netif_tx_request_t *txrp;
112356567907SDavid Edmondson 	struct netif_extra_info *erp;
112456567907SDavid Edmondson 	boolean_t notify, result;
1125843e1988Sjohnlev 
1126843e1988Sjohnlev 	/*
112756567907SDavid Edmondson 	 * If the backend does not support multicast control then we
112856567907SDavid Edmondson 	 * must assume that the right packets will just arrive.
1129843e1988Sjohnlev 	 */
113056567907SDavid Edmondson 	if (!xnfp->xnf_be_mcast_control)
1131843e1988Sjohnlev 		return (0);
113256567907SDavid Edmondson 
113356567907SDavid Edmondson 	txp = kmem_cache_alloc(xnfp->xnf_tx_buf_cache, KM_SLEEP);
113456567907SDavid Edmondson 
113556567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_txlock);
113656567907SDavid Edmondson 
113756567907SDavid Edmondson 	/*
113856567907SDavid Edmondson 	 * If we're not yet connected then claim success. This is
113956567907SDavid Edmondson 	 * acceptable because we refresh the entire set of multicast
114056567907SDavid Edmondson 	 * addresses when we get connected.
114156567907SDavid Edmondson 	 *
114256567907SDavid Edmondson 	 * We can't wait around here because the MAC layer expects
114356567907SDavid Edmondson 	 * this to be a non-blocking operation - waiting ends up
114456567907SDavid Edmondson 	 * causing a deadlock during resume.
114556567907SDavid Edmondson 	 */
114656567907SDavid Edmondson 	if (!xnfp->xnf_connected) {
114756567907SDavid Edmondson 		mutex_exit(&xnfp->xnf_txlock);
114856567907SDavid Edmondson 		return (0);
114956567907SDavid Edmondson 	}
115056567907SDavid Edmondson 
115156567907SDavid Edmondson 	/*
115256567907SDavid Edmondson 	 * 1. Acquire two slots in the ring.
115356567907SDavid Edmondson 	 * 2. Fill in the slots.
115456567907SDavid Edmondson 	 * 3. Request notification when the operation is done.
115556567907SDavid Edmondson 	 * 4. Kick the peer.
115656567907SDavid Edmondson 	 * 5. Wait for the response via xnf_tx_clean_ring().
115756567907SDavid Edmondson 	 */
115856567907SDavid Edmondson 
115956567907SDavid Edmondson 	n_slots = tx_slots_get(xnfp, 2, B_TRUE);
116056567907SDavid Edmondson 	ASSERT(n_slots >= 2);
116156567907SDavid Edmondson 
116256567907SDavid Edmondson 	slot = xnfp->xnf_tx_ring.req_prod_pvt;
116356567907SDavid Edmondson 	tidp = txid_get(xnfp);
116456567907SDavid Edmondson 	VERIFY(tidp != NULL);
116556567907SDavid Edmondson 
116656567907SDavid Edmondson 	txp->tx_type = TX_MCAST_REQ;
116756567907SDavid Edmondson 	txp->tx_slot = slot;
116856567907SDavid Edmondson 
116956567907SDavid Edmondson 	txrp = RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot);
117056567907SDavid Edmondson 	erp = (struct netif_extra_info *)
117156567907SDavid Edmondson 	    RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot + 1);
117256567907SDavid Edmondson 
117356567907SDavid Edmondson 	txrp->gref = 0;
117456567907SDavid Edmondson 	txrp->size = 0;
117556567907SDavid Edmondson 	txrp->offset = 0;
117656567907SDavid Edmondson 	/* Set tx_txreq.id to appease xnf_tx_clean_ring(). */
117756567907SDavid Edmondson 	txrp->id = txp->tx_txreq.id = tidp->id;
117856567907SDavid Edmondson 	txrp->flags = NETTXF_extra_info;
117956567907SDavid Edmondson 
118056567907SDavid Edmondson 	erp->type = add ? XEN_NETIF_EXTRA_TYPE_MCAST_ADD :
118156567907SDavid Edmondson 	    XEN_NETIF_EXTRA_TYPE_MCAST_DEL;
118256567907SDavid Edmondson 	bcopy((void *)mca, &erp->u.mcast.addr, ETHERADDRL);
118356567907SDavid Edmondson 
118456567907SDavid Edmondson 	tidp->txbuf = txp;
118556567907SDavid Edmondson 
118656567907SDavid Edmondson 	xnfp->xnf_tx_ring.req_prod_pvt = slot + 2;
118756567907SDavid Edmondson 
118856567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_schedlock);
118956567907SDavid Edmondson 	xnfp->xnf_pending_multicast++;
119056567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_schedlock);
119156567907SDavid Edmondson 
119256567907SDavid Edmondson 	/* LINTED: constant in conditional context */
119356567907SDavid Edmondson 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_tx_ring,
119456567907SDavid Edmondson 	    notify);
119556567907SDavid Edmondson 	if (notify)
119656567907SDavid Edmondson 		ec_notify_via_evtchn(xnfp->xnf_evtchn);
119756567907SDavid Edmondson 
119856567907SDavid Edmondson 	while (txp->tx_type == TX_MCAST_REQ)
119956567907SDavid Edmondson 		cv_wait(&xnfp->xnf_cv_multicast,
120056567907SDavid Edmondson 		    &xnfp->xnf_txlock);
120156567907SDavid Edmondson 
120256567907SDavid Edmondson 	ASSERT(txp->tx_type == TX_MCAST_RSP);
120356567907SDavid Edmondson 
120456567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_schedlock);
120556567907SDavid Edmondson 	xnfp->xnf_pending_multicast--;
120656567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_schedlock);
120756567907SDavid Edmondson 
120856567907SDavid Edmondson 	result = (txp->tx_status == NETIF_RSP_OKAY);
120956567907SDavid Edmondson 
121056567907SDavid Edmondson 	txid_put(xnfp, tidp);
121156567907SDavid Edmondson 
121256567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_txlock);
121356567907SDavid Edmondson 
121456567907SDavid Edmondson 	kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
121556567907SDavid Edmondson 
121656567907SDavid Edmondson 	return (result ? 0 : 1);
1217843e1988Sjohnlev }
1218843e1988Sjohnlev 
1219843e1988Sjohnlev /*
1220843e1988Sjohnlev  * xnf_set_promiscuous() -- set or reset promiscuous mode on the board
1221843e1988Sjohnlev  *
1222843e1988Sjohnlev  *  Program the hardware to enable/disable promiscuous mode.
1223843e1988Sjohnlev  */
1224843e1988Sjohnlev static int
1225843e1988Sjohnlev xnf_set_promiscuous(void *arg, boolean_t on)
1226843e1988Sjohnlev {
122756567907SDavid Edmondson 	_NOTE(ARGUNUSED(arg, on));
1228843e1988Sjohnlev 
1229843e1988Sjohnlev 	/*
1230843e1988Sjohnlev 	 * We can't really do this, but we pretend that we can in
1231843e1988Sjohnlev 	 * order that snoop will work.
1232843e1988Sjohnlev 	 */
1233843e1988Sjohnlev 	return (0);
1234843e1988Sjohnlev }
1235843e1988Sjohnlev 
1236843e1988Sjohnlev /*
1237843e1988Sjohnlev  * Clean buffers that we have responses for from the transmit ring.
1238843e1988Sjohnlev  */
1239843e1988Sjohnlev static int
124056567907SDavid Edmondson xnf_tx_clean_ring(xnf_t *xnfp)
1241843e1988Sjohnlev {
1242a390c5f4Scz147101 	boolean_t work_to_do;
1243843e1988Sjohnlev 
1244551bc2a6Smrj 	ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
1245843e1988Sjohnlev 
1246a390c5f4Scz147101 loop:
124766f1a35aSschuster 	while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_tx_ring)) {
124856567907SDavid Edmondson 		RING_IDX cons, prod, i;
124956567907SDavid Edmondson 
125056567907SDavid Edmondson 		cons = xnfp->xnf_tx_ring.rsp_cons;
125156567907SDavid Edmondson 		prod = xnfp->xnf_tx_ring.sring->rsp_prod;
1252843e1988Sjohnlev 		membar_consumer();
1253843e1988Sjohnlev 		/*
125456567907SDavid Edmondson 		 * Clean tx requests from ring that we have responses
125556567907SDavid Edmondson 		 * for.
1256843e1988Sjohnlev 		 */
125756567907SDavid Edmondson 		DTRACE_PROBE2(xnf_tx_clean_range, int, cons, int, prod);
125856567907SDavid Edmondson 		for (i = cons; i != prod; i++) {
125956567907SDavid Edmondson 			netif_tx_response_t *trp;
126056567907SDavid Edmondson 			xnf_txid_t *tidp;
126156567907SDavid Edmondson 			xnf_txbuf_t *txp;
126256567907SDavid Edmondson 
126356567907SDavid Edmondson 			trp = RING_GET_RESPONSE(&xnfp->xnf_tx_ring, i);
126456567907SDavid Edmondson 			ASSERT(TX_ID_VALID(trp->id));
126556567907SDavid Edmondson 
126656567907SDavid Edmondson 			tidp = TX_ID_TO_TXID(xnfp, trp->id);
126756567907SDavid Edmondson 			ASSERT(tidp->id == trp->id);
126856567907SDavid Edmondson 			ASSERT(tidp->next == INVALID_TX_ID);
126956567907SDavid Edmondson 
127056567907SDavid Edmondson 			txp = tidp->txbuf;
127156567907SDavid Edmondson 			ASSERT(txp != NULL);
127256567907SDavid Edmondson 			ASSERT(txp->tx_txreq.id == trp->id);
127356567907SDavid Edmondson 
127456567907SDavid Edmondson 			switch (txp->tx_type) {
127556567907SDavid Edmondson 			case TX_DATA:
127656567907SDavid Edmondson 				if (gnttab_query_foreign_access(
127756567907SDavid Edmondson 				    txp->tx_txreq.gref) != 0)
127856567907SDavid Edmondson 					cmn_err(CE_PANIC,
127956567907SDavid Edmondson 					    "tx grant %d still in use by "
128056567907SDavid Edmondson 					    "backend domain",
128156567907SDavid Edmondson 					    txp->tx_txreq.gref);
128256567907SDavid Edmondson 
128356567907SDavid Edmondson 				if (txp->tx_bdesc == NULL) {
128456567907SDavid Edmondson 					(void) gnttab_end_foreign_access_ref(
128556567907SDavid Edmondson 					    txp->tx_txreq.gref, 1);
128656567907SDavid Edmondson 					gref_put(xnfp, txp->tx_txreq.gref);
128756567907SDavid Edmondson 					(void) ddi_dma_unbind_handle(
128856567907SDavid Edmondson 					    txp->tx_dma_handle);
128956567907SDavid Edmondson 				} else {
129056567907SDavid Edmondson 					xnf_buf_put(xnfp, txp->tx_bdesc,
129156567907SDavid Edmondson 					    B_TRUE);
1292843e1988Sjohnlev 				}
129356567907SDavid Edmondson 
129456567907SDavid Edmondson 				freemsg(txp->tx_mp);
129556567907SDavid Edmondson 				txid_put(xnfp, tidp);
129656567907SDavid Edmondson 				kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
129756567907SDavid Edmondson 
129856567907SDavid Edmondson 				break;
129956567907SDavid Edmondson 
130056567907SDavid Edmondson 			case TX_MCAST_REQ:
130156567907SDavid Edmondson 				txp->tx_type = TX_MCAST_RSP;
130256567907SDavid Edmondson 				txp->tx_status = trp->status;
130356567907SDavid Edmondson 				cv_broadcast(&xnfp->xnf_cv_multicast);
130456567907SDavid Edmondson 
130556567907SDavid Edmondson 				break;
130656567907SDavid Edmondson 
130756567907SDavid Edmondson 			case TX_MCAST_RSP:
130856567907SDavid Edmondson 				break;
130956567907SDavid Edmondson 
131056567907SDavid Edmondson 			default:
131156567907SDavid Edmondson 				cmn_err(CE_PANIC, "xnf_tx_clean_ring: "
131256567907SDavid Edmondson 				    "invalid xnf_txbuf_t type: %d",
131356567907SDavid Edmondson 				    txp->tx_type);
131456567907SDavid Edmondson 				break;
131556567907SDavid Edmondson 			}
131656567907SDavid Edmondson 		}
131756567907SDavid Edmondson 		/*
131856567907SDavid Edmondson 		 * Record the last response we dealt with so that we
131956567907SDavid Edmondson 		 * know where to start next time around.
132056567907SDavid Edmondson 		 */
132156567907SDavid Edmondson 		xnfp->xnf_tx_ring.rsp_cons = prod;
1322843e1988Sjohnlev 		membar_enter();
132366f1a35aSschuster 	}
132466f1a35aSschuster 
1325a390c5f4Scz147101 	/* LINTED: constant in conditional context */
1326a390c5f4Scz147101 	RING_FINAL_CHECK_FOR_RESPONSES(&xnfp->xnf_tx_ring, work_to_do);
1327a390c5f4Scz147101 	if (work_to_do)
1328a390c5f4Scz147101 		goto loop;
1329a390c5f4Scz147101 
133066f1a35aSschuster 	return (RING_FREE_REQUESTS(&xnfp->xnf_tx_ring));
1331843e1988Sjohnlev }
1332843e1988Sjohnlev 
1333843e1988Sjohnlev /*
133456567907SDavid Edmondson  * Allocate and fill in a look-aside buffer for the packet `mp'. Used
133556567907SDavid Edmondson  * to ensure that the packet is physically contiguous and contained
133656567907SDavid Edmondson  * within a single page.
1337843e1988Sjohnlev  */
133856567907SDavid Edmondson static xnf_buf_t *
133956567907SDavid Edmondson xnf_tx_pullup(xnf_t *xnfp, mblk_t *mp)
1340843e1988Sjohnlev {
134156567907SDavid Edmondson 	xnf_buf_t *bd;
1342843e1988Sjohnlev 	caddr_t bp;
1343843e1988Sjohnlev 
134456567907SDavid Edmondson 	bd = xnf_buf_get(xnfp, KM_SLEEP, B_TRUE);
134556567907SDavid Edmondson 	if (bd == NULL)
134656567907SDavid Edmondson 		return (NULL);
134756567907SDavid Edmondson 
134856567907SDavid Edmondson 	bp = bd->buf;
134956567907SDavid Edmondson 	while (mp != NULL) {
135056567907SDavid Edmondson 		size_t len = MBLKL(mp);
135156567907SDavid Edmondson 
135256567907SDavid Edmondson 		bcopy(mp->b_rptr, bp, len);
1353843e1988Sjohnlev 		bp += len;
135456567907SDavid Edmondson 
135556567907SDavid Edmondson 		mp = mp->b_cont;
1356843e1988Sjohnlev 	}
1357843e1988Sjohnlev 
135856567907SDavid Edmondson 	ASSERT((bp - bd->buf) <= PAGESIZE);
135956567907SDavid Edmondson 
136056567907SDavid Edmondson 	xnfp->xnf_stat_tx_pullup++;
136156567907SDavid Edmondson 
136256567907SDavid Edmondson 	return (bd);
136356567907SDavid Edmondson }
136456567907SDavid Edmondson 
136556567907SDavid Edmondson /*
136656567907SDavid Edmondson  * Insert the pseudo-header checksum into the packet `buf'.
136756567907SDavid Edmondson  */
1368a859da42SDavid Edmondson void
1369a859da42SDavid Edmondson xnf_pseudo_cksum(caddr_t buf, int length)
1370a859da42SDavid Edmondson {
1371a859da42SDavid Edmondson 	struct ether_header *ehp;
1372a859da42SDavid Edmondson 	uint16_t sap, len, *stuff;
1373a859da42SDavid Edmondson 	uint32_t cksum;
1374a859da42SDavid Edmondson 	size_t offset;
1375a859da42SDavid Edmondson 	ipha_t *ipha;
1376a859da42SDavid Edmondson 	ipaddr_t src, dst;
1377a859da42SDavid Edmondson 
1378a859da42SDavid Edmondson 	ASSERT(length >= sizeof (*ehp));
1379a859da42SDavid Edmondson 	ehp = (struct ether_header *)buf;
1380a859da42SDavid Edmondson 
1381a859da42SDavid Edmondson 	if (ntohs(ehp->ether_type) == VLAN_TPID) {
1382a859da42SDavid Edmondson 		struct ether_vlan_header *evhp;
1383a859da42SDavid Edmondson 
1384a859da42SDavid Edmondson 		ASSERT(length >= sizeof (*evhp));
1385a859da42SDavid Edmondson 		evhp = (struct ether_vlan_header *)buf;
1386a859da42SDavid Edmondson 		sap = ntohs(evhp->ether_type);
1387a859da42SDavid Edmondson 		offset = sizeof (*evhp);
1388a859da42SDavid Edmondson 	} else {
1389a859da42SDavid Edmondson 		sap = ntohs(ehp->ether_type);
1390a859da42SDavid Edmondson 		offset = sizeof (*ehp);
1391a859da42SDavid Edmondson 	}
1392a859da42SDavid Edmondson 
1393a859da42SDavid Edmondson 	ASSERT(sap == ETHERTYPE_IP);
1394a859da42SDavid Edmondson 
1395a859da42SDavid Edmondson 	/* Packet should have been pulled up by the caller. */
1396a859da42SDavid Edmondson 	if ((offset + sizeof (ipha_t)) > length) {
1397a859da42SDavid Edmondson 		cmn_err(CE_WARN, "xnf_pseudo_cksum: no room for checksum");
1398a859da42SDavid Edmondson 		return;
1399a859da42SDavid Edmondson 	}
1400a859da42SDavid Edmondson 
1401a859da42SDavid Edmondson 	ipha = (ipha_t *)(buf + offset);
1402a859da42SDavid Edmondson 
1403a859da42SDavid Edmondson 	ASSERT(IPH_HDR_LENGTH(ipha) == IP_SIMPLE_HDR_LENGTH);
1404a859da42SDavid Edmondson 
1405a859da42SDavid Edmondson 	len = ntohs(ipha->ipha_length) - IP_SIMPLE_HDR_LENGTH;
1406a859da42SDavid Edmondson 
1407a859da42SDavid Edmondson 	switch (ipha->ipha_protocol) {
1408a859da42SDavid Edmondson 	case IPPROTO_TCP:
1409a859da42SDavid Edmondson 		stuff = IPH_TCPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH);
1410a859da42SDavid Edmondson 		cksum = IP_TCP_CSUM_COMP;
1411a859da42SDavid Edmondson 		break;
1412a859da42SDavid Edmondson 	case IPPROTO_UDP:
1413a859da42SDavid Edmondson 		stuff = IPH_UDPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH);
1414a859da42SDavid Edmondson 		cksum = IP_UDP_CSUM_COMP;
1415a859da42SDavid Edmondson 		break;
1416a859da42SDavid Edmondson 	default:
1417a859da42SDavid Edmondson 		cmn_err(CE_WARN, "xnf_pseudo_cksum: unexpected protocol %d",
1418a859da42SDavid Edmondson 		    ipha->ipha_protocol);
1419a859da42SDavid Edmondson 		return;
1420a859da42SDavid Edmondson 	}
1421a859da42SDavid Edmondson 
1422a859da42SDavid Edmondson 	src = ipha->ipha_src;
1423a859da42SDavid Edmondson 	dst = ipha->ipha_dst;
1424a859da42SDavid Edmondson 
1425a859da42SDavid Edmondson 	cksum += (dst >> 16) + (dst & 0xFFFF);
1426a859da42SDavid Edmondson 	cksum += (src >> 16) + (src & 0xFFFF);
1427a859da42SDavid Edmondson 	cksum += htons(len);
1428a859da42SDavid Edmondson 
1429a859da42SDavid Edmondson 	cksum = (cksum >> 16) + (cksum & 0xFFFF);
1430a859da42SDavid Edmondson 	cksum = (cksum >> 16) + (cksum & 0xFFFF);
1431a859da42SDavid Edmondson 
1432a859da42SDavid Edmondson 	ASSERT(cksum <= 0xFFFF);
1433a859da42SDavid Edmondson 
1434a859da42SDavid Edmondson 	*stuff = (uint16_t)(cksum ? cksum : ~cksum);
1435a859da42SDavid Edmondson }
1436a859da42SDavid Edmondson 
1437843e1988Sjohnlev /*
143856567907SDavid Edmondson  * Push a list of prepared packets (`txp') into the transmit ring.
1439843e1988Sjohnlev  */
144056567907SDavid Edmondson static xnf_txbuf_t *
144156567907SDavid Edmondson tx_push_packets(xnf_t *xnfp, xnf_txbuf_t *txp)
1442843e1988Sjohnlev {
144356567907SDavid Edmondson 	int slots_free;
14443a79cf1eSschuster 	RING_IDX slot;
144556567907SDavid Edmondson 	boolean_t notify;
1446843e1988Sjohnlev 
144756567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_txlock);
1448843e1988Sjohnlev 
144956567907SDavid Edmondson 	ASSERT(xnfp->xnf_running);
1450843e1988Sjohnlev 
1451843e1988Sjohnlev 	/*
145256567907SDavid Edmondson 	 * Wait until we are connected to the backend.
1453843e1988Sjohnlev 	 */
145456567907SDavid Edmondson 	while (!xnfp->xnf_connected)
145556567907SDavid Edmondson 		cv_wait(&xnfp->xnf_cv_state, &xnfp->xnf_txlock);
145656567907SDavid Edmondson 
145756567907SDavid Edmondson 	slots_free = tx_slots_get(xnfp, 1, B_FALSE);
145856567907SDavid Edmondson 	DTRACE_PROBE1(xnf_send_slotsfree, int, slots_free);
1459843e1988Sjohnlev 
146066f1a35aSschuster 	slot = xnfp->xnf_tx_ring.req_prod_pvt;
1461843e1988Sjohnlev 
146256567907SDavid Edmondson 	while ((txp != NULL) && (slots_free > 0)) {
146356567907SDavid Edmondson 		xnf_txid_t *tidp;
146456567907SDavid Edmondson 		netif_tx_request_t *txrp;
146556567907SDavid Edmondson 
146656567907SDavid Edmondson 		tidp = txid_get(xnfp);
146756567907SDavid Edmondson 		VERIFY(tidp != NULL);
146856567907SDavid Edmondson 
146956567907SDavid Edmondson 		txrp = RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot);
147056567907SDavid Edmondson 
147156567907SDavid Edmondson 		txp->tx_slot = slot;
147256567907SDavid Edmondson 		txp->tx_txreq.id = tidp->id;
147356567907SDavid Edmondson 		*txrp = txp->tx_txreq;
147456567907SDavid Edmondson 
147556567907SDavid Edmondson 		tidp->txbuf = txp;
147656567907SDavid Edmondson 
147756567907SDavid Edmondson 		xnfp->xnf_stat_opackets++;
147856567907SDavid Edmondson 		xnfp->xnf_stat_obytes += txp->tx_txreq.size;
147956567907SDavid Edmondson 
148056567907SDavid Edmondson 		txp = txp->tx_next;
148156567907SDavid Edmondson 		slots_free--;
148256567907SDavid Edmondson 		slot++;
148356567907SDavid Edmondson 
148456567907SDavid Edmondson 	}
148556567907SDavid Edmondson 
148656567907SDavid Edmondson 	xnfp->xnf_tx_ring.req_prod_pvt = slot;
148756567907SDavid Edmondson 
148856567907SDavid Edmondson 	/*
148956567907SDavid Edmondson 	 * Tell the peer that we sent something, if it cares.
149056567907SDavid Edmondson 	 */
149156567907SDavid Edmondson 	/* LINTED: constant in conditional context */
149256567907SDavid Edmondson 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_tx_ring,
149356567907SDavid Edmondson 	    notify);
149456567907SDavid Edmondson 	if (notify)
149556567907SDavid Edmondson 		ec_notify_via_evtchn(xnfp->xnf_evtchn);
149656567907SDavid Edmondson 
149756567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_txlock);
149856567907SDavid Edmondson 
149956567907SDavid Edmondson 	return (txp);
1500843e1988Sjohnlev }
1501843e1988Sjohnlev 
1502843e1988Sjohnlev /*
150356567907SDavid Edmondson  * Send the chain of packets `mp'. Called by the MAC framework.
1504843e1988Sjohnlev  */
150556567907SDavid Edmondson static mblk_t *
150656567907SDavid Edmondson xnf_send(void *arg, mblk_t *mp)
150756567907SDavid Edmondson {
150856567907SDavid Edmondson 	xnf_t *xnfp = arg;
150956567907SDavid Edmondson 	domid_t oeid;
151056567907SDavid Edmondson 	xnf_txbuf_t *head, *tail;
151156567907SDavid Edmondson 	mblk_t *ml;
151256567907SDavid Edmondson 	int prepared;
151356567907SDavid Edmondson 
151456567907SDavid Edmondson 	oeid = xvdi_get_oeid(xnfp->xnf_devinfo);
151556567907SDavid Edmondson 
1516843e1988Sjohnlev 	/*
151756567907SDavid Edmondson 	 * Prepare packets for transmission.
1518843e1988Sjohnlev 	 */
151956567907SDavid Edmondson 	head = tail = NULL;
152056567907SDavid Edmondson 	prepared = 0;
152156567907SDavid Edmondson 	while (mp != NULL) {
152256567907SDavid Edmondson 		xnf_txbuf_t *txp;
152356567907SDavid Edmondson 		int n_chunks, length;
152456567907SDavid Edmondson 		boolean_t page_oops;
152556567907SDavid Edmondson 		uint32_t pflags;
152656567907SDavid Edmondson 
152756567907SDavid Edmondson 		for (ml = mp, n_chunks = length = 0, page_oops = B_FALSE;
152856567907SDavid Edmondson 		    ml != NULL;
152956567907SDavid Edmondson 		    ml = ml->b_cont, n_chunks++) {
153056567907SDavid Edmondson 
153156567907SDavid Edmondson 			/*
153256567907SDavid Edmondson 			 * Test if this buffer includes a page
153356567907SDavid Edmondson 			 * boundary. The test assumes that the range
153456567907SDavid Edmondson 			 * b_rptr...b_wptr can include only a single
153556567907SDavid Edmondson 			 * boundary.
153656567907SDavid Edmondson 			 */
153756567907SDavid Edmondson 			if (xnf_btop((size_t)ml->b_rptr) !=
153856567907SDavid Edmondson 			    xnf_btop((size_t)ml->b_wptr)) {
1539551bc2a6Smrj 				xnfp->xnf_stat_tx_pagebndry++;
154056567907SDavid Edmondson 				page_oops = B_TRUE;
1541843e1988Sjohnlev 			}
1542843e1988Sjohnlev 
154356567907SDavid Edmondson 			length += MBLKL(ml);
154456567907SDavid Edmondson 		}
154556567907SDavid Edmondson 		DTRACE_PROBE1(xnf_send_b_cont, int, n_chunks);
1546843e1988Sjohnlev 
1547843e1988Sjohnlev 		/*
154856567907SDavid Edmondson 		 * Make sure packet isn't too large.
1549843e1988Sjohnlev 		 */
155056567907SDavid Edmondson 		if (length > XNF_FRAMESIZE) {
155156567907SDavid Edmondson 			cmn_err(CE_WARN,
155256567907SDavid Edmondson 			    "xnf%d: oversized packet (%d bytes) dropped",
155356567907SDavid Edmondson 			    ddi_get_instance(xnfp->xnf_devinfo), length);
155456567907SDavid Edmondson 			freemsg(mp);
155556567907SDavid Edmondson 			continue;
155656567907SDavid Edmondson 		}
1557843e1988Sjohnlev 
155856567907SDavid Edmondson 		txp = kmem_cache_alloc(xnfp->xnf_tx_buf_cache, KM_SLEEP);
155956567907SDavid Edmondson 
156056567907SDavid Edmondson 		txp->tx_type = TX_DATA;
156156567907SDavid Edmondson 
156256567907SDavid Edmondson 		if ((n_chunks > xnf_max_tx_frags) || page_oops) {
156356567907SDavid Edmondson 			/*
156456567907SDavid Edmondson 			 * Loan a side buffer rather than the mblk
156556567907SDavid Edmondson 			 * itself.
156656567907SDavid Edmondson 			 */
156756567907SDavid Edmondson 			txp->tx_bdesc = xnf_tx_pullup(xnfp, mp);
156856567907SDavid Edmondson 			if (txp->tx_bdesc == NULL) {
156956567907SDavid Edmondson 				kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
157056567907SDavid Edmondson 				break;
157156567907SDavid Edmondson 			}
157256567907SDavid Edmondson 
157356567907SDavid Edmondson 			txp->tx_bufp = txp->tx_bdesc->buf;
157456567907SDavid Edmondson 			txp->tx_mfn = txp->tx_bdesc->buf_mfn;
157556567907SDavid Edmondson 			txp->tx_txreq.gref = txp->tx_bdesc->grant_ref;
157656567907SDavid Edmondson 
157756567907SDavid Edmondson 		} else {
157856567907SDavid Edmondson 			int rc;
157956567907SDavid Edmondson 			ddi_dma_cookie_t dma_cookie;
158056567907SDavid Edmondson 			uint_t ncookies;
158156567907SDavid Edmondson 
158256567907SDavid Edmondson 			rc = ddi_dma_addr_bind_handle(txp->tx_dma_handle,
158356567907SDavid Edmondson 			    NULL, (char *)mp->b_rptr, length,
158456567907SDavid Edmondson 			    DDI_DMA_WRITE | DDI_DMA_STREAMING,
158556567907SDavid Edmondson 			    DDI_DMA_DONTWAIT, 0, &dma_cookie,
158656567907SDavid Edmondson 			    &ncookies);
1587843e1988Sjohnlev 			if (rc != DDI_DMA_MAPPED) {
1588843e1988Sjohnlev 				ASSERT(rc != DDI_DMA_INUSE);
1589843e1988Sjohnlev 				ASSERT(rc != DDI_DMA_PARTIAL_MAP);
159056567907SDavid Edmondson 
1591843e1988Sjohnlev #ifdef XNF_DEBUG
159256567907SDavid Edmondson 				if (rc != DDI_DMA_NORESOURCES)
159356567907SDavid Edmondson 					cmn_err(CE_WARN,
159456567907SDavid Edmondson 					    "xnf%d: bind_handle failed (%x)",
159556567907SDavid Edmondson 					    ddi_get_instance(xnfp->xnf_devinfo),
159656567907SDavid Edmondson 					    rc);
1597843e1988Sjohnlev #endif
159856567907SDavid Edmondson 				kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
159956567907SDavid Edmondson 				break;
160056567907SDavid Edmondson 			}
160156567907SDavid Edmondson 			ASSERT(ncookies == 1);
160256567907SDavid Edmondson 
160356567907SDavid Edmondson 			txp->tx_bdesc = NULL;
160456567907SDavid Edmondson 			txp->tx_bufp = (caddr_t)mp->b_rptr;
160556567907SDavid Edmondson 			txp->tx_mfn =
160656567907SDavid Edmondson 			    xnf_btop(pa_to_ma(dma_cookie.dmac_laddress));
160756567907SDavid Edmondson 			txp->tx_txreq.gref = gref_get(xnfp);
160856567907SDavid Edmondson 			if (txp->tx_txreq.gref == INVALID_GRANT_REF) {
160956567907SDavid Edmondson 				(void) ddi_dma_unbind_handle(
161056567907SDavid Edmondson 				    txp->tx_dma_handle);
161156567907SDavid Edmondson 				kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
161256567907SDavid Edmondson 				break;
161356567907SDavid Edmondson 			}
161456567907SDavid Edmondson 			gnttab_grant_foreign_access_ref(txp->tx_txreq.gref,
161556567907SDavid Edmondson 			    oeid, txp->tx_mfn, 1);
1616843e1988Sjohnlev 		}
1617843e1988Sjohnlev 
161856567907SDavid Edmondson 		txp->tx_next = NULL;
161956567907SDavid Edmondson 		txp->tx_mp = mp;
162056567907SDavid Edmondson 		txp->tx_txreq.size = length;
162156567907SDavid Edmondson 		txp->tx_txreq.offset = (uintptr_t)txp->tx_bufp & PAGEOFFSET;
162256567907SDavid Edmondson 		txp->tx_txreq.flags = 0;
16230dc2366fSVenugopal Iyer 		mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &pflags);
1624843e1988Sjohnlev 		if (pflags != 0) {
1625843e1988Sjohnlev 			/*
1626843e1988Sjohnlev 			 * If the local protocol stack requests checksum
1627843e1988Sjohnlev 			 * offload we set the 'checksum blank' flag,
1628843e1988Sjohnlev 			 * indicating to the peer that we need the checksum
1629843e1988Sjohnlev 			 * calculated for us.
1630843e1988Sjohnlev 			 *
1631843e1988Sjohnlev 			 * We _don't_ set the validated flag, because we haven't
1632843e1988Sjohnlev 			 * validated that the data and the checksum match.
1633843e1988Sjohnlev 			 */
163456567907SDavid Edmondson 			xnf_pseudo_cksum(txp->tx_bufp, length);
163556567907SDavid Edmondson 			txp->tx_txreq.flags |= NETTXF_csum_blank;
163656567907SDavid Edmondson 
1637551bc2a6Smrj 			xnfp->xnf_stat_tx_cksum_deferred++;
1638843e1988Sjohnlev 		}
1639843e1988Sjohnlev 
164056567907SDavid Edmondson 		if (head == NULL) {
164156567907SDavid Edmondson 			ASSERT(tail == NULL);
1642843e1988Sjohnlev 
164356567907SDavid Edmondson 			head = txp;
164456567907SDavid Edmondson 		} else {
164556567907SDavid Edmondson 			ASSERT(tail != NULL);
1646843e1988Sjohnlev 
164756567907SDavid Edmondson 			tail->tx_next = txp;
1648843e1988Sjohnlev 		}
164956567907SDavid Edmondson 		tail = txp;
1650843e1988Sjohnlev 
165156567907SDavid Edmondson 		mp = mp->b_next;
165256567907SDavid Edmondson 		prepared++;
1653843e1988Sjohnlev 
1654843e1988Sjohnlev 		/*
165556567907SDavid Edmondson 		 * There is no point in preparing more than
165656567907SDavid Edmondson 		 * NET_TX_RING_SIZE, as we won't be able to push them
165756567907SDavid Edmondson 		 * into the ring in one go and would hence have to
165856567907SDavid Edmondson 		 * un-prepare the extra.
1659843e1988Sjohnlev 		 */
166056567907SDavid Edmondson 		if (prepared == NET_TX_RING_SIZE)
1661843e1988Sjohnlev 			break;
1662843e1988Sjohnlev 	}
1663843e1988Sjohnlev 
166456567907SDavid Edmondson 	DTRACE_PROBE1(xnf_send_prepared, int, prepared);
166556567907SDavid Edmondson 
166656567907SDavid Edmondson 	if (mp != NULL) {
166756567907SDavid Edmondson #ifdef XNF_DEBUG
166856567907SDavid Edmondson 		int notprepared = 0;
166956567907SDavid Edmondson 		mblk_t *l = mp;
167056567907SDavid Edmondson 
167156567907SDavid Edmondson 		while (l != NULL) {
167256567907SDavid Edmondson 			notprepared++;
167356567907SDavid Edmondson 			l = l->b_next;
1674843e1988Sjohnlev 		}
1675843e1988Sjohnlev 
167656567907SDavid Edmondson 		DTRACE_PROBE1(xnf_send_notprepared, int, notprepared);
167756567907SDavid Edmondson #else /* !XNF_DEBUG */
167856567907SDavid Edmondson 		DTRACE_PROBE1(xnf_send_notprepared, int, -1);
167956567907SDavid Edmondson #endif /* XNF_DEBUG */
168066f1a35aSschuster 	}
1681843e1988Sjohnlev 
168256567907SDavid Edmondson 	/*
168356567907SDavid Edmondson 	 * Push the packets we have prepared into the ring. They may
168456567907SDavid Edmondson 	 * not all go.
168556567907SDavid Edmondson 	 */
168656567907SDavid Edmondson 	if (head != NULL)
168756567907SDavid Edmondson 		head = tx_push_packets(xnfp, head);
168856567907SDavid Edmondson 
168956567907SDavid Edmondson 	/*
169056567907SDavid Edmondson 	 * If some packets that we prepared were not sent, unprepare
169156567907SDavid Edmondson 	 * them and add them back to the head of those we didn't
169256567907SDavid Edmondson 	 * prepare.
169356567907SDavid Edmondson 	 */
169456567907SDavid Edmondson 	{
169556567907SDavid Edmondson 		xnf_txbuf_t *loop;
169656567907SDavid Edmondson 		mblk_t *mp_head, *mp_tail;
169756567907SDavid Edmondson 		int unprepared = 0;
169856567907SDavid Edmondson 
169956567907SDavid Edmondson 		mp_head = mp_tail = NULL;
170056567907SDavid Edmondson 		loop = head;
170156567907SDavid Edmondson 
170256567907SDavid Edmondson 		while (loop != NULL) {
170356567907SDavid Edmondson 			xnf_txbuf_t *next = loop->tx_next;
170456567907SDavid Edmondson 
170556567907SDavid Edmondson 			if (loop->tx_bdesc == NULL) {
170656567907SDavid Edmondson 				(void) gnttab_end_foreign_access_ref(
170756567907SDavid Edmondson 				    loop->tx_txreq.gref, 1);
170856567907SDavid Edmondson 				gref_put(xnfp, loop->tx_txreq.gref);
170956567907SDavid Edmondson 				(void) ddi_dma_unbind_handle(
171056567907SDavid Edmondson 				    loop->tx_dma_handle);
171156567907SDavid Edmondson 			} else {
171256567907SDavid Edmondson 				xnf_buf_put(xnfp, loop->tx_bdesc, B_TRUE);
171356567907SDavid Edmondson 			}
171456567907SDavid Edmondson 
171556567907SDavid Edmondson 			ASSERT(loop->tx_mp != NULL);
171656567907SDavid Edmondson 			if (mp_head == NULL)
171756567907SDavid Edmondson 				mp_head = loop->tx_mp;
171856567907SDavid Edmondson 			mp_tail = loop->tx_mp;
171956567907SDavid Edmondson 
172056567907SDavid Edmondson 			kmem_cache_free(xnfp->xnf_tx_buf_cache, loop);
172156567907SDavid Edmondson 			loop = next;
172256567907SDavid Edmondson 			unprepared++;
172356567907SDavid Edmondson 		}
172456567907SDavid Edmondson 
172556567907SDavid Edmondson 		if (mp_tail == NULL) {
172656567907SDavid Edmondson 			ASSERT(mp_head == NULL);
172756567907SDavid Edmondson 		} else {
172856567907SDavid Edmondson 			ASSERT(mp_head != NULL);
172956567907SDavid Edmondson 
173056567907SDavid Edmondson 			mp_tail->b_next = mp;
173156567907SDavid Edmondson 			mp = mp_head;
173256567907SDavid Edmondson 		}
173356567907SDavid Edmondson 
173456567907SDavid Edmondson 		DTRACE_PROBE1(xnf_send_unprepared, int, unprepared);
173556567907SDavid Edmondson 	}
173656567907SDavid Edmondson 
173756567907SDavid Edmondson 	/*
173856567907SDavid Edmondson 	 * If any mblks are left then we have deferred for some reason
173956567907SDavid Edmondson 	 * and need to ask for a re-schedule later. This is typically
174056567907SDavid Edmondson 	 * due to the ring filling.
174156567907SDavid Edmondson 	 */
174256567907SDavid Edmondson 	if (mp != NULL) {
174356567907SDavid Edmondson 		mutex_enter(&xnfp->xnf_schedlock);
1744e779793cSDavid Edmondson 		xnfp->xnf_need_sched = B_TRUE;
174556567907SDavid Edmondson 		mutex_exit(&xnfp->xnf_schedlock);
174664c5e63cSDavid Edmondson 
174756567907SDavid Edmondson 		xnfp->xnf_stat_tx_defer++;
174856567907SDavid Edmondson 	}
1749843e1988Sjohnlev 
1750843e1988Sjohnlev 	return (mp);
1751843e1988Sjohnlev }
1752843e1988Sjohnlev 
1753843e1988Sjohnlev /*
175456567907SDavid Edmondson  * Notification of RX packets. Currently no TX-complete interrupt is
175556567907SDavid Edmondson  * used, as we clean the TX ring lazily.
1756843e1988Sjohnlev  */
1757843e1988Sjohnlev static uint_t
1758843e1988Sjohnlev xnf_intr(caddr_t arg)
1759843e1988Sjohnlev {
1760843e1988Sjohnlev 	xnf_t *xnfp = (xnf_t *)arg;
176156567907SDavid Edmondson 	mblk_t *mp;
176256567907SDavid Edmondson 	boolean_t need_sched, clean_ring;
1763843e1988Sjohnlev 
176456567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_rxlock);
1765843e1988Sjohnlev 
176656567907SDavid Edmondson 	/*
176756567907SDavid Edmondson 	 * Interrupts before we are connected are spurious.
176856567907SDavid Edmondson 	 */
1769a390c5f4Scz147101 	if (!xnfp->xnf_connected) {
177056567907SDavid Edmondson 		mutex_exit(&xnfp->xnf_rxlock);
1771551bc2a6Smrj 		xnfp->xnf_stat_unclaimed_interrupts++;
1772843e1988Sjohnlev 		return (DDI_INTR_UNCLAIMED);
1773843e1988Sjohnlev 	}
1774843e1988Sjohnlev 
177556567907SDavid Edmondson 	/*
177656567907SDavid Edmondson 	 * Receive side processing.
177756567907SDavid Edmondson 	 */
177856567907SDavid Edmondson 	do {
177956567907SDavid Edmondson 		/*
178056567907SDavid Edmondson 		 * Collect buffers from the ring.
178156567907SDavid Edmondson 		 */
178256567907SDavid Edmondson 		xnf_rx_collect(xnfp);
1783843e1988Sjohnlev 
178456567907SDavid Edmondson 		/*
178556567907SDavid Edmondson 		 * Interrupt me when the next receive buffer is consumed.
178656567907SDavid Edmondson 		 */
178756567907SDavid Edmondson 		xnfp->xnf_rx_ring.sring->rsp_event =
178856567907SDavid Edmondson 		    xnfp->xnf_rx_ring.rsp_cons + 1;
178956567907SDavid Edmondson 		xen_mb();
179056567907SDavid Edmondson 
179156567907SDavid Edmondson 	} while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_rx_ring));
179256567907SDavid Edmondson 
179356567907SDavid Edmondson 	if (xnfp->xnf_rx_new_buffers_posted) {
179456567907SDavid Edmondson 		boolean_t notify;
179556567907SDavid Edmondson 
179656567907SDavid Edmondson 		/*
179756567907SDavid Edmondson 		 * Indicate to the peer that we have re-filled the
179856567907SDavid Edmondson 		 * receive ring, if it cares.
179956567907SDavid Edmondson 		 */
180056567907SDavid Edmondson 		/* LINTED: constant in conditional context */
180156567907SDavid Edmondson 		RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_rx_ring, notify);
180256567907SDavid Edmondson 		if (notify)
180356567907SDavid Edmondson 			ec_notify_via_evtchn(xnfp->xnf_evtchn);
180456567907SDavid Edmondson 		xnfp->xnf_rx_new_buffers_posted = B_FALSE;
180556567907SDavid Edmondson 	}
180656567907SDavid Edmondson 
180756567907SDavid Edmondson 	mp = xnfp->xnf_rx_head;
180856567907SDavid Edmondson 	xnfp->xnf_rx_head = xnfp->xnf_rx_tail = NULL;
180956567907SDavid Edmondson 
181056567907SDavid Edmondson 	xnfp->xnf_stat_interrupts++;
181156567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_rxlock);
1812551bc2a6Smrj 
1813551bc2a6Smrj 	if (mp != NULL)
1814da14cebeSEric Cheng 		mac_rx(xnfp->xnf_mh, NULL, mp);
181564c5e63cSDavid Edmondson 
1816843e1988Sjohnlev 	/*
181756567907SDavid Edmondson 	 * Transmit side processing.
181856567907SDavid Edmondson 	 *
181956567907SDavid Edmondson 	 * If a previous transmit attempt failed or we have pending
182056567907SDavid Edmondson 	 * multicast requests, clean the ring.
182156567907SDavid Edmondson 	 *
182256567907SDavid Edmondson 	 * If we previously stalled transmission and cleaning produces
182356567907SDavid Edmondson 	 * some free slots, tell upstream to attempt sending again.
182456567907SDavid Edmondson 	 *
182556567907SDavid Edmondson 	 * The odd style is to avoid acquiring xnf_txlock unless we
182656567907SDavid Edmondson 	 * will actually look inside the tx machinery.
1827843e1988Sjohnlev 	 */
182856567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_schedlock);
182956567907SDavid Edmondson 	need_sched = xnfp->xnf_need_sched;
183056567907SDavid Edmondson 	clean_ring = need_sched || (xnfp->xnf_pending_multicast > 0);
183156567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_schedlock);
183256567907SDavid Edmondson 
183356567907SDavid Edmondson 	if (clean_ring) {
183456567907SDavid Edmondson 		int free_slots;
183556567907SDavid Edmondson 
1836551bc2a6Smrj 		mutex_enter(&xnfp->xnf_txlock);
183756567907SDavid Edmondson 		free_slots = tx_slots_get(xnfp, 0, B_FALSE);
183856567907SDavid Edmondson 
183956567907SDavid Edmondson 		if (need_sched && (free_slots > 0)) {
184056567907SDavid Edmondson 			mutex_enter(&xnfp->xnf_schedlock);
184164c5e63cSDavid Edmondson 			xnfp->xnf_need_sched = B_FALSE;
184256567907SDavid Edmondson 			mutex_exit(&xnfp->xnf_schedlock);
184356567907SDavid Edmondson 
184456567907SDavid Edmondson 			mac_tx_update(xnfp->xnf_mh);
1845843e1988Sjohnlev 		}
184664c5e63cSDavid Edmondson 		mutex_exit(&xnfp->xnf_txlock);
184756567907SDavid Edmondson 	}
184864c5e63cSDavid Edmondson 
184964c5e63cSDavid Edmondson 	return (DDI_INTR_CLAIMED);
1850843e1988Sjohnlev }
1851843e1988Sjohnlev 
1852843e1988Sjohnlev /*
1853843e1988Sjohnlev  *  xnf_start() -- start the board receiving and enable interrupts.
1854843e1988Sjohnlev  */
1855843e1988Sjohnlev static int
1856843e1988Sjohnlev xnf_start(void *arg)
1857843e1988Sjohnlev {
1858843e1988Sjohnlev 	xnf_t *xnfp = arg;
1859843e1988Sjohnlev 
1860843e1988Sjohnlev #ifdef XNF_DEBUG
186156567907SDavid Edmondson 	if (xnf_debug & XNF_DEBUG_TRACE)
1862843e1988Sjohnlev 		printf("xnf%d start(0x%p)\n",
1863551bc2a6Smrj 		    ddi_get_instance(xnfp->xnf_devinfo), (void *)xnfp);
1864843e1988Sjohnlev #endif
1865843e1988Sjohnlev 
186656567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_rxlock);
1867551bc2a6Smrj 	mutex_enter(&xnfp->xnf_txlock);
1868843e1988Sjohnlev 
1869843e1988Sjohnlev 	/* Accept packets from above. */
1870551bc2a6Smrj 	xnfp->xnf_running = B_TRUE;
1871843e1988Sjohnlev 
1872551bc2a6Smrj 	mutex_exit(&xnfp->xnf_txlock);
187356567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_rxlock);
1874843e1988Sjohnlev 
1875843e1988Sjohnlev 	return (0);
1876843e1988Sjohnlev }
1877843e1988Sjohnlev 
1878843e1988Sjohnlev /* xnf_stop() - disable hardware */
1879843e1988Sjohnlev static void
1880843e1988Sjohnlev xnf_stop(void *arg)
1881843e1988Sjohnlev {
1882843e1988Sjohnlev 	xnf_t *xnfp = arg;
1883843e1988Sjohnlev 
1884843e1988Sjohnlev #ifdef XNF_DEBUG
188556567907SDavid Edmondson 	if (xnf_debug & XNF_DEBUG_TRACE)
1886843e1988Sjohnlev 		printf("xnf%d stop(0x%p)\n",
1887551bc2a6Smrj 		    ddi_get_instance(xnfp->xnf_devinfo), (void *)xnfp);
1888843e1988Sjohnlev #endif
1889843e1988Sjohnlev 
189056567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_rxlock);
1891551bc2a6Smrj 	mutex_enter(&xnfp->xnf_txlock);
1892843e1988Sjohnlev 
1893551bc2a6Smrj 	xnfp->xnf_running = B_FALSE;
1894843e1988Sjohnlev 
1895551bc2a6Smrj 	mutex_exit(&xnfp->xnf_txlock);
189656567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_rxlock);
1897843e1988Sjohnlev }
1898843e1988Sjohnlev 
1899843e1988Sjohnlev /*
190056567907SDavid Edmondson  * Hang buffer `bdesc' on the RX ring.
1901843e1988Sjohnlev  */
1902843e1988Sjohnlev static void
190356567907SDavid Edmondson xnf_rxbuf_hang(xnf_t *xnfp, xnf_buf_t *bdesc)
1904843e1988Sjohnlev {
190556567907SDavid Edmondson 	netif_rx_request_t *reqp;
1906843e1988Sjohnlev 	RING_IDX hang_ix;
1907843e1988Sjohnlev 
190856567907SDavid Edmondson 	ASSERT(MUTEX_HELD(&xnfp->xnf_rxlock));
1909843e1988Sjohnlev 
1910551bc2a6Smrj 	reqp = RING_GET_REQUEST(&xnfp->xnf_rx_ring,
1911551bc2a6Smrj 	    xnfp->xnf_rx_ring.req_prod_pvt);
1912551bc2a6Smrj 	hang_ix = (RING_IDX) (reqp - RING_GET_REQUEST(&xnfp->xnf_rx_ring, 0));
191356567907SDavid Edmondson 	ASSERT(xnfp->xnf_rx_pkt_info[hang_ix] == NULL);
1914551bc2a6Smrj 
191556567907SDavid Edmondson 	reqp->id = bdesc->id = hang_ix;
1916843e1988Sjohnlev 	reqp->gref = bdesc->grant_ref;
1917843e1988Sjohnlev 
191856567907SDavid Edmondson 	xnfp->xnf_rx_pkt_info[hang_ix] = bdesc;
191956567907SDavid Edmondson 	xnfp->xnf_rx_ring.req_prod_pvt++;
192056567907SDavid Edmondson 
192156567907SDavid Edmondson 	xnfp->xnf_rx_new_buffers_posted = B_TRUE;
192256567907SDavid Edmondson }
1923551bc2a6Smrj 
1924551bc2a6Smrj /*
192556567907SDavid Edmondson  * Collect packets from the RX ring, storing them in `xnfp' for later
192656567907SDavid Edmondson  * use.
192756567907SDavid Edmondson  */
192856567907SDavid Edmondson static void
192956567907SDavid Edmondson xnf_rx_collect(xnf_t *xnfp)
193056567907SDavid Edmondson {
193156567907SDavid Edmondson 	mblk_t *head, *tail;
193256567907SDavid Edmondson 
193356567907SDavid Edmondson 	ASSERT(MUTEX_HELD(&xnfp->xnf_rxlock));
193456567907SDavid Edmondson 
193556567907SDavid Edmondson 	/*
193656567907SDavid Edmondson 	 * Loop over unconsumed responses:
1937551bc2a6Smrj 	 * 1. get a response
1938551bc2a6Smrj 	 * 2. take corresponding buffer off recv. ring
1939551bc2a6Smrj 	 * 3. indicate this by setting slot to NULL
1940551bc2a6Smrj 	 * 4. create a new message and
1941551bc2a6Smrj 	 * 5. copy data in, adjust ptr
1942551bc2a6Smrj 	 */
1943551bc2a6Smrj 
1944551bc2a6Smrj 	head = tail = NULL;
1945551bc2a6Smrj 
1946551bc2a6Smrj 	while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_rx_ring)) {
194756567907SDavid Edmondson 		netif_rx_response_t *rxpkt;
194856567907SDavid Edmondson 		xnf_buf_t *bdesc;
194956567907SDavid Edmondson 		ssize_t len;
195056567907SDavid Edmondson 		size_t off;
195156567907SDavid Edmondson 		mblk_t *mp = NULL;
195256567907SDavid Edmondson 		boolean_t hwcsum = B_FALSE;
195356567907SDavid Edmondson 		grant_ref_t ref;
1954551bc2a6Smrj 
1955551bc2a6Smrj 		/* 1. */
1956551bc2a6Smrj 		rxpkt = RING_GET_RESPONSE(&xnfp->xnf_rx_ring,
1957551bc2a6Smrj 		    xnfp->xnf_rx_ring.rsp_cons);
1958551bc2a6Smrj 
195956567907SDavid Edmondson 		DTRACE_PROBE4(xnf_rx_got_rsp, int, (int)rxpkt->id,
196056567907SDavid Edmondson 		    int, (int)rxpkt->offset,
196156567907SDavid Edmondson 		    int, (int)rxpkt->flags,
196256567907SDavid Edmondson 		    int, (int)rxpkt->status);
1963551bc2a6Smrj 
1964551bc2a6Smrj 		/*
1965551bc2a6Smrj 		 * 2.
1966551bc2a6Smrj 		 */
196756567907SDavid Edmondson 		bdesc = xnfp->xnf_rx_pkt_info[rxpkt->id];
1968551bc2a6Smrj 
196956567907SDavid Edmondson 		/*
197056567907SDavid Edmondson 		 * 3.
197156567907SDavid Edmondson 		 */
197256567907SDavid Edmondson 		xnfp->xnf_rx_pkt_info[rxpkt->id] = NULL;
197356567907SDavid Edmondson 		ASSERT(bdesc->id == rxpkt->id);
197456567907SDavid Edmondson 
197556567907SDavid Edmondson 		ref = bdesc->grant_ref;
197656567907SDavid Edmondson 		off = rxpkt->offset;
197756567907SDavid Edmondson 		len = rxpkt->status;
197856567907SDavid Edmondson 
197956567907SDavid Edmondson 		if (!xnfp->xnf_running) {
198056567907SDavid Edmondson 			DTRACE_PROBE4(xnf_rx_not_running,
198156567907SDavid Edmondson 			    int, rxpkt->status,
1982551bc2a6Smrj 			    char *, bdesc->buf, int, rxpkt->offset,
1983551bc2a6Smrj 			    char *, ((char *)bdesc->buf) + rxpkt->offset);
198456567907SDavid Edmondson 
198556567907SDavid Edmondson 			xnfp->xnf_stat_drop++;
198656567907SDavid Edmondson 
198756567907SDavid Edmondson 		} else if (len <= 0) {
198856567907SDavid Edmondson 			DTRACE_PROBE4(xnf_rx_pkt_status_negative,
198956567907SDavid Edmondson 			    int, rxpkt->status,
199056567907SDavid Edmondson 			    char *, bdesc->buf, int, rxpkt->offset,
199156567907SDavid Edmondson 			    char *, ((char *)bdesc->buf) + rxpkt->offset);
199256567907SDavid Edmondson 
199356567907SDavid Edmondson 			xnfp->xnf_stat_errrx++;
199456567907SDavid Edmondson 
199556567907SDavid Edmondson 			switch (len) {
199656567907SDavid Edmondson 			case 0:
199756567907SDavid Edmondson 				xnfp->xnf_stat_runt++;
199856567907SDavid Edmondson 				break;
199956567907SDavid Edmondson 			case NETIF_RSP_ERROR:
200056567907SDavid Edmondson 				xnfp->xnf_stat_mac_rcv_error++;
200156567907SDavid Edmondson 				break;
200256567907SDavid Edmondson 			case NETIF_RSP_DROPPED:
200356567907SDavid Edmondson 				xnfp->xnf_stat_norxbuf++;
200456567907SDavid Edmondson 				break;
2005551bc2a6Smrj 			}
200656567907SDavid Edmondson 
200756567907SDavid Edmondson 		} else if (bdesc->grant_ref == INVALID_GRANT_REF) {
200856567907SDavid Edmondson 			cmn_err(CE_WARN, "Bad rx grant reference %d "
200956567907SDavid Edmondson 			    "from domain %d", ref,
201056567907SDavid Edmondson 			    xvdi_get_oeid(xnfp->xnf_devinfo));
201156567907SDavid Edmondson 
201256567907SDavid Edmondson 		} else if ((off + len) > PAGESIZE) {
201356567907SDavid Edmondson 			cmn_err(CE_WARN, "Rx packet overflows page "
201456567907SDavid Edmondson 			    "(offset %ld, length %ld) from domain %d",
201556567907SDavid Edmondson 			    off, len, xvdi_get_oeid(xnfp->xnf_devinfo));
201656567907SDavid Edmondson 		} else {
201756567907SDavid Edmondson 			xnf_buf_t *nbuf = NULL;
201856567907SDavid Edmondson 
201956567907SDavid Edmondson 			DTRACE_PROBE4(xnf_rx_packet, int, len,
202056567907SDavid Edmondson 			    char *, bdesc->buf, int, off,
202156567907SDavid Edmondson 			    char *, ((char *)bdesc->buf) + off);
202256567907SDavid Edmondson 
202356567907SDavid Edmondson 			ASSERT(off + len <= PAGEOFFSET);
202456567907SDavid Edmondson 
2025551bc2a6Smrj 			if (rxpkt->flags & NETRXF_data_validated)
2026551bc2a6Smrj 				hwcsum = B_TRUE;
2027551bc2a6Smrj 
2028551bc2a6Smrj 			/*
202956567907SDavid Edmondson 			 * If the packet is below a pre-determined
203056567907SDavid Edmondson 			 * size we will copy data out rather than
203156567907SDavid Edmondson 			 * replace it.
2032551bc2a6Smrj 			 */
203356567907SDavid Edmondson 			if (len > xnf_rx_copy_limit)
203456567907SDavid Edmondson 				nbuf = xnf_buf_get(xnfp, KM_NOSLEEP, B_FALSE);
203556567907SDavid Edmondson 
2036551bc2a6Smrj 			/*
203756567907SDavid Edmondson 			 * If we have a replacement buffer, attempt to
203856567907SDavid Edmondson 			 * wrap the existing one with an mblk_t in
203956567907SDavid Edmondson 			 * order that the upper layers of the stack
204056567907SDavid Edmondson 			 * might use it directly.
2041551bc2a6Smrj 			 */
204256567907SDavid Edmondson 			if (nbuf != NULL) {
204356567907SDavid Edmondson 				mp = desballoc((unsigned char *)bdesc->buf,
204456567907SDavid Edmondson 				    bdesc->len, 0, &bdesc->free_rtn);
204556567907SDavid Edmondson 				if (mp == NULL) {
204656567907SDavid Edmondson 					xnfp->xnf_stat_rx_desballoc_fail++;
2047551bc2a6Smrj 					xnfp->xnf_stat_norxbuf++;
204856567907SDavid Edmondson 
204956567907SDavid Edmondson 					xnf_buf_put(xnfp, nbuf, B_FALSE);
205056567907SDavid Edmondson 					nbuf = NULL;
205156567907SDavid Edmondson 				} else {
205256567907SDavid Edmondson 					mp->b_rptr = mp->b_rptr + off;
205356567907SDavid Edmondson 					mp->b_wptr = mp->b_rptr + len;
205456567907SDavid Edmondson 
205556567907SDavid Edmondson 					/*
205656567907SDavid Edmondson 					 * Release the grant reference
205756567907SDavid Edmondson 					 * associated with this buffer
205856567907SDavid Edmondson 					 * - they are scarce and the
205956567907SDavid Edmondson 					 * upper layers of the stack
206056567907SDavid Edmondson 					 * don't need it.
206156567907SDavid Edmondson 					 */
206256567907SDavid Edmondson 					(void) gnttab_end_foreign_access_ref(
206356567907SDavid Edmondson 					    bdesc->grant_ref, 0);
206456567907SDavid Edmondson 					gref_put(xnfp, bdesc->grant_ref);
206556567907SDavid Edmondson 					bdesc->grant_ref = INVALID_GRANT_REF;
206656567907SDavid Edmondson 
206756567907SDavid Edmondson 					bdesc = nbuf;
206856567907SDavid Edmondson 				}
206956567907SDavid Edmondson 			}
207056567907SDavid Edmondson 
207156567907SDavid Edmondson 			if (nbuf == NULL) {
207256567907SDavid Edmondson 				/*
207356567907SDavid Edmondson 				 * No replacement buffer allocated -
207456567907SDavid Edmondson 				 * attempt to copy the data out and
207556567907SDavid Edmondson 				 * re-hang the existing buffer.
207656567907SDavid Edmondson 				 */
207756567907SDavid Edmondson 
207856567907SDavid Edmondson 				/* 4. */
207956567907SDavid Edmondson 				mp = allocb(len, BPRI_MED);
208056567907SDavid Edmondson 				if (mp == NULL) {
208156567907SDavid Edmondson 					xnfp->xnf_stat_rx_allocb_fail++;
208256567907SDavid Edmondson 					xnfp->xnf_stat_norxbuf++;
2083551bc2a6Smrj 				} else {
2084551bc2a6Smrj 					/* 5. */
2085551bc2a6Smrj 					bcopy(bdesc->buf + off, mp->b_wptr,
2086551bc2a6Smrj 					    len);
2087551bc2a6Smrj 					mp->b_wptr += len;
2088551bc2a6Smrj 				}
2089551bc2a6Smrj 			}
2090551bc2a6Smrj 		}
2091551bc2a6Smrj 
209256567907SDavid Edmondson 		/* Re-hang the buffer. */
209356567907SDavid Edmondson 		xnf_rxbuf_hang(xnfp, bdesc);
2094551bc2a6Smrj 
209556567907SDavid Edmondson 		if (mp != NULL) {
2096843e1988Sjohnlev 			if (hwcsum) {
2097843e1988Sjohnlev 				/*
2098843e1988Sjohnlev 				 * If the peer says that the data has
2099843e1988Sjohnlev 				 * been validated then we declare that
2100843e1988Sjohnlev 				 * the full checksum has been
2101843e1988Sjohnlev 				 * verified.
2102843e1988Sjohnlev 				 *
2103843e1988Sjohnlev 				 * We don't look at the "checksum
2104843e1988Sjohnlev 				 * blank" flag, and hence could have a
2105843e1988Sjohnlev 				 * packet here that we are asserting
2106843e1988Sjohnlev 				 * is good with a blank checksum.
2107843e1988Sjohnlev 				 */
21080dc2366fSVenugopal Iyer 				mac_hcksum_set(mp, 0, 0, 0, 0,
21090dc2366fSVenugopal Iyer 				    HCK_FULLCKSUM_OK);
2110551bc2a6Smrj 				xnfp->xnf_stat_rx_cksum_no_need++;
2111843e1988Sjohnlev 			}
2112843e1988Sjohnlev 			if (head == NULL) {
211356567907SDavid Edmondson 				ASSERT(tail == NULL);
211456567907SDavid Edmondson 
211556567907SDavid Edmondson 				head = mp;
2116843e1988Sjohnlev 			} else {
211756567907SDavid Edmondson 				ASSERT(tail != NULL);
211856567907SDavid Edmondson 
2119843e1988Sjohnlev 				tail->b_next = mp;
2120843e1988Sjohnlev 			}
212156567907SDavid Edmondson 			tail = mp;
2122843e1988Sjohnlev 
2123843e1988Sjohnlev 			ASSERT(mp->b_next == NULL);
2124843e1988Sjohnlev 
2125551bc2a6Smrj 			xnfp->xnf_stat_ipackets++;
2126551bc2a6Smrj 			xnfp->xnf_stat_rbytes += len;
2127843e1988Sjohnlev 		}
2128843e1988Sjohnlev 
2129551bc2a6Smrj 		xnfp->xnf_rx_ring.rsp_cons++;
2130843e1988Sjohnlev 	}
2131843e1988Sjohnlev 
2132843e1988Sjohnlev 	/*
213356567907SDavid Edmondson 	 * Store the mblks we have collected.
2134843e1988Sjohnlev 	 */
213556567907SDavid Edmondson 	if (head != NULL) {
213656567907SDavid Edmondson 		ASSERT(tail != NULL);
2137843e1988Sjohnlev 
213856567907SDavid Edmondson 		if (xnfp->xnf_rx_head == NULL) {
213956567907SDavid Edmondson 			ASSERT(xnfp->xnf_rx_tail == NULL);
2140843e1988Sjohnlev 
214156567907SDavid Edmondson 			xnfp->xnf_rx_head = head;
2142843e1988Sjohnlev 		} else {
214356567907SDavid Edmondson 			ASSERT(xnfp->xnf_rx_tail != NULL);
214456567907SDavid Edmondson 
214556567907SDavid Edmondson 			xnfp->xnf_rx_tail->b_next = head;
214656567907SDavid Edmondson 		}
214756567907SDavid Edmondson 		xnfp->xnf_rx_tail = tail;
2148843e1988Sjohnlev 	}
2149843e1988Sjohnlev }
2150843e1988Sjohnlev 
2151843e1988Sjohnlev /*
2152843e1988Sjohnlev  *  xnf_alloc_dma_resources() -- initialize the drivers structures
2153843e1988Sjohnlev  */
2154843e1988Sjohnlev static int
2155843e1988Sjohnlev xnf_alloc_dma_resources(xnf_t *xnfp)
2156843e1988Sjohnlev {
2157551bc2a6Smrj 	dev_info_t 		*devinfo = xnfp->xnf_devinfo;
2158843e1988Sjohnlev 	size_t			len;
2159843e1988Sjohnlev 	ddi_dma_cookie_t	dma_cookie;
2160843e1988Sjohnlev 	uint_t			ncookies;
2161843e1988Sjohnlev 	int			rc;
2162843e1988Sjohnlev 	caddr_t			rptr;
2163843e1988Sjohnlev 
2164843e1988Sjohnlev 	/*
2165843e1988Sjohnlev 	 * The code below allocates all the DMA data structures that
2166843e1988Sjohnlev 	 * need to be released when the driver is detached.
2167843e1988Sjohnlev 	 *
2168843e1988Sjohnlev 	 * Allocate page for the transmit descriptor ring.
2169843e1988Sjohnlev 	 */
2170843e1988Sjohnlev 	if (ddi_dma_alloc_handle(devinfo, &ringbuf_dma_attr,
2171551bc2a6Smrj 	    DDI_DMA_SLEEP, 0, &xnfp->xnf_tx_ring_dma_handle) != DDI_SUCCESS)
2172843e1988Sjohnlev 		goto alloc_error;
2173843e1988Sjohnlev 
2174551bc2a6Smrj 	if (ddi_dma_mem_alloc(xnfp->xnf_tx_ring_dma_handle,
2175843e1988Sjohnlev 	    PAGESIZE, &accattr, DDI_DMA_CONSISTENT,
2176843e1988Sjohnlev 	    DDI_DMA_SLEEP, 0, &rptr, &len,
2177551bc2a6Smrj 	    &xnfp->xnf_tx_ring_dma_acchandle) != DDI_SUCCESS) {
2178551bc2a6Smrj 		ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
2179551bc2a6Smrj 		xnfp->xnf_tx_ring_dma_handle = NULL;
2180843e1988Sjohnlev 		goto alloc_error;
2181843e1988Sjohnlev 	}
2182843e1988Sjohnlev 
2183551bc2a6Smrj 	if ((rc = ddi_dma_addr_bind_handle(xnfp->xnf_tx_ring_dma_handle, NULL,
2184843e1988Sjohnlev 	    rptr, PAGESIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
2185843e1988Sjohnlev 	    DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies)) != DDI_DMA_MAPPED) {
2186551bc2a6Smrj 		ddi_dma_mem_free(&xnfp->xnf_tx_ring_dma_acchandle);
2187551bc2a6Smrj 		ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
2188551bc2a6Smrj 		xnfp->xnf_tx_ring_dma_handle = NULL;
2189551bc2a6Smrj 		xnfp->xnf_tx_ring_dma_acchandle = NULL;
2190843e1988Sjohnlev 		if (rc == DDI_DMA_NORESOURCES)
2191843e1988Sjohnlev 			goto alloc_error;
2192843e1988Sjohnlev 		else
2193843e1988Sjohnlev 			goto error;
2194843e1988Sjohnlev 	}
2195843e1988Sjohnlev 
2196843e1988Sjohnlev 	ASSERT(ncookies == 1);
2197843e1988Sjohnlev 	bzero(rptr, PAGESIZE);
2198843e1988Sjohnlev 	/* LINTED: constant in conditional context */
2199843e1988Sjohnlev 	SHARED_RING_INIT((netif_tx_sring_t *)rptr);
2200843e1988Sjohnlev 	/* LINTED: constant in conditional context */
2201551bc2a6Smrj 	FRONT_RING_INIT(&xnfp->xnf_tx_ring, (netif_tx_sring_t *)rptr, PAGESIZE);
2202551bc2a6Smrj 	xnfp->xnf_tx_ring_phys_addr = dma_cookie.dmac_laddress;
2203843e1988Sjohnlev 
2204843e1988Sjohnlev 	/*
2205843e1988Sjohnlev 	 * Allocate page for the receive descriptor ring.
2206843e1988Sjohnlev 	 */
2207843e1988Sjohnlev 	if (ddi_dma_alloc_handle(devinfo, &ringbuf_dma_attr,
2208551bc2a6Smrj 	    DDI_DMA_SLEEP, 0, &xnfp->xnf_rx_ring_dma_handle) != DDI_SUCCESS)
2209843e1988Sjohnlev 		goto alloc_error;
2210843e1988Sjohnlev 
2211551bc2a6Smrj 	if (ddi_dma_mem_alloc(xnfp->xnf_rx_ring_dma_handle,
2212843e1988Sjohnlev 	    PAGESIZE, &accattr, DDI_DMA_CONSISTENT,
2213843e1988Sjohnlev 	    DDI_DMA_SLEEP, 0, &rptr, &len,
2214551bc2a6Smrj 	    &xnfp->xnf_rx_ring_dma_acchandle) != DDI_SUCCESS) {
2215551bc2a6Smrj 		ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
2216551bc2a6Smrj 		xnfp->xnf_rx_ring_dma_handle = NULL;
2217843e1988Sjohnlev 		goto alloc_error;
2218843e1988Sjohnlev 	}
2219843e1988Sjohnlev 
2220551bc2a6Smrj 	if ((rc = ddi_dma_addr_bind_handle(xnfp->xnf_rx_ring_dma_handle, NULL,
2221843e1988Sjohnlev 	    rptr, PAGESIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
2222843e1988Sjohnlev 	    DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies)) != DDI_DMA_MAPPED) {
2223551bc2a6Smrj 		ddi_dma_mem_free(&xnfp->xnf_rx_ring_dma_acchandle);
2224551bc2a6Smrj 		ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
2225551bc2a6Smrj 		xnfp->xnf_rx_ring_dma_handle = NULL;
2226551bc2a6Smrj 		xnfp->xnf_rx_ring_dma_acchandle = NULL;
2227843e1988Sjohnlev 		if (rc == DDI_DMA_NORESOURCES)
2228843e1988Sjohnlev 			goto alloc_error;
2229843e1988Sjohnlev 		else
2230843e1988Sjohnlev 			goto error;
2231843e1988Sjohnlev 	}
2232843e1988Sjohnlev 
2233843e1988Sjohnlev 	ASSERT(ncookies == 1);
2234843e1988Sjohnlev 	bzero(rptr, PAGESIZE);
2235843e1988Sjohnlev 	/* LINTED: constant in conditional context */
2236843e1988Sjohnlev 	SHARED_RING_INIT((netif_rx_sring_t *)rptr);
2237843e1988Sjohnlev 	/* LINTED: constant in conditional context */
2238551bc2a6Smrj 	FRONT_RING_INIT(&xnfp->xnf_rx_ring, (netif_rx_sring_t *)rptr, PAGESIZE);
2239551bc2a6Smrj 	xnfp->xnf_rx_ring_phys_addr = dma_cookie.dmac_laddress;
2240843e1988Sjohnlev 
2241843e1988Sjohnlev 	return (DDI_SUCCESS);
2242843e1988Sjohnlev 
2243843e1988Sjohnlev alloc_error:
2244843e1988Sjohnlev 	cmn_err(CE_WARN, "xnf%d: could not allocate enough DMA memory",
2245551bc2a6Smrj 	    ddi_get_instance(xnfp->xnf_devinfo));
2246843e1988Sjohnlev error:
2247843e1988Sjohnlev 	xnf_release_dma_resources(xnfp);
2248843e1988Sjohnlev 	return (DDI_FAILURE);
2249843e1988Sjohnlev }
2250843e1988Sjohnlev 
2251843e1988Sjohnlev /*
2252843e1988Sjohnlev  * Release all DMA resources in the opposite order from acquisition
2253843e1988Sjohnlev  */
2254843e1988Sjohnlev static void
2255843e1988Sjohnlev xnf_release_dma_resources(xnf_t *xnfp)
2256843e1988Sjohnlev {
2257843e1988Sjohnlev 	int i;
2258843e1988Sjohnlev 
2259843e1988Sjohnlev 	/*
2260843e1988Sjohnlev 	 * Free receive buffers which are currently associated with
226156567907SDavid Edmondson 	 * descriptors.
2262843e1988Sjohnlev 	 */
226356567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_rxlock);
226456567907SDavid Edmondson 	for (i = 0; i < NET_RX_RING_SIZE; i++) {
226556567907SDavid Edmondson 		xnf_buf_t *bp;
2266843e1988Sjohnlev 
226756567907SDavid Edmondson 		if ((bp = xnfp->xnf_rx_pkt_info[i]) == NULL)
2268843e1988Sjohnlev 			continue;
226956567907SDavid Edmondson 		xnfp->xnf_rx_pkt_info[i] = NULL;
227056567907SDavid Edmondson 		xnf_buf_put(xnfp, bp, B_FALSE);
2271843e1988Sjohnlev 	}
227256567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_rxlock);
2273843e1988Sjohnlev 
227456567907SDavid Edmondson 	/* Free the receive ring buffer. */
2275551bc2a6Smrj 	if (xnfp->xnf_rx_ring_dma_acchandle != NULL) {
2276551bc2a6Smrj 		(void) ddi_dma_unbind_handle(xnfp->xnf_rx_ring_dma_handle);
2277551bc2a6Smrj 		ddi_dma_mem_free(&xnfp->xnf_rx_ring_dma_acchandle);
2278551bc2a6Smrj 		ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
2279551bc2a6Smrj 		xnfp->xnf_rx_ring_dma_acchandle = NULL;
2280843e1988Sjohnlev 	}
228156567907SDavid Edmondson 	/* Free the transmit ring buffer. */
2282551bc2a6Smrj 	if (xnfp->xnf_tx_ring_dma_acchandle != NULL) {
2283551bc2a6Smrj 		(void) ddi_dma_unbind_handle(xnfp->xnf_tx_ring_dma_handle);
2284551bc2a6Smrj 		ddi_dma_mem_free(&xnfp->xnf_tx_ring_dma_acchandle);
2285551bc2a6Smrj 		ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
2286551bc2a6Smrj 		xnfp->xnf_tx_ring_dma_acchandle = NULL;
2287843e1988Sjohnlev 	}
2288a390c5f4Scz147101 
228956567907SDavid Edmondson }
229056567907SDavid Edmondson 
2291a390c5f4Scz147101 /*
229256567907SDavid Edmondson  * Release any packets and associated structures used by the TX ring.
2293a390c5f4Scz147101  */
2294843e1988Sjohnlev static void
2295843e1988Sjohnlev xnf_release_mblks(xnf_t *xnfp)
2296843e1988Sjohnlev {
229756567907SDavid Edmondson 	RING_IDX i;
229856567907SDavid Edmondson 	xnf_txid_t *tidp;
2299843e1988Sjohnlev 
230056567907SDavid Edmondson 	for (i = 0, tidp = &xnfp->xnf_tx_pkt_id[0];
230156567907SDavid Edmondson 	    i < NET_TX_RING_SIZE;
230256567907SDavid Edmondson 	    i++, tidp++) {
230356567907SDavid Edmondson 		xnf_txbuf_t *txp = tidp->txbuf;
230456567907SDavid Edmondson 
230556567907SDavid Edmondson 		if (txp != NULL) {
230656567907SDavid Edmondson 			ASSERT(txp->tx_mp != NULL);
230756567907SDavid Edmondson 			freemsg(txp->tx_mp);
230856567907SDavid Edmondson 
230956567907SDavid Edmondson 			txid_put(xnfp, tidp);
231056567907SDavid Edmondson 			kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
231156567907SDavid Edmondson 		}
2312843e1988Sjohnlev 	}
2313843e1988Sjohnlev }
2314843e1988Sjohnlev 
231556567907SDavid Edmondson static int
231656567907SDavid Edmondson xnf_buf_constructor(void *buf, void *arg, int kmflag)
2317843e1988Sjohnlev {
231856567907SDavid Edmondson 	int (*ddiflags)(caddr_t) = DDI_DMA_SLEEP;
231956567907SDavid Edmondson 	xnf_buf_t *bdesc = buf;
232056567907SDavid Edmondson 	xnf_t *xnfp = arg;
2321843e1988Sjohnlev 	ddi_dma_cookie_t dma_cookie;
232256567907SDavid Edmondson 	uint_t ncookies;
232356567907SDavid Edmondson 	size_t len;
2324843e1988Sjohnlev 
232556567907SDavid Edmondson 	if (kmflag & KM_NOSLEEP)
232656567907SDavid Edmondson 		ddiflags = DDI_DMA_DONTWAIT;
2327843e1988Sjohnlev 
232856567907SDavid Edmondson 	/* Allocate a DMA access handle for the buffer. */
232956567907SDavid Edmondson 	if (ddi_dma_alloc_handle(xnfp->xnf_devinfo, &buf_dma_attr,
233056567907SDavid Edmondson 	    ddiflags, 0, &bdesc->dma_handle) != DDI_SUCCESS)
2331843e1988Sjohnlev 		goto failure;
2332843e1988Sjohnlev 
233356567907SDavid Edmondson 	/* Allocate DMA-able memory for buffer. */
2334843e1988Sjohnlev 	if (ddi_dma_mem_alloc(bdesc->dma_handle,
233556567907SDavid Edmondson 	    PAGESIZE, &data_accattr, DDI_DMA_STREAMING, ddiflags, 0,
2336843e1988Sjohnlev 	    &bdesc->buf, &len, &bdesc->acc_handle) != DDI_SUCCESS)
2337551bc2a6Smrj 		goto failure_1;
2338843e1988Sjohnlev 
233956567907SDavid Edmondson 	/* Bind to virtual address of buffer to get physical address. */
2340843e1988Sjohnlev 	if (ddi_dma_addr_bind_handle(bdesc->dma_handle, NULL,
234156567907SDavid Edmondson 	    bdesc->buf, len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
234256567907SDavid Edmondson 	    ddiflags, 0, &dma_cookie, &ncookies) != DDI_DMA_MAPPED)
2343551bc2a6Smrj 		goto failure_2;
2344843e1988Sjohnlev 	ASSERT(ncookies == 1);
2345843e1988Sjohnlev 
234656567907SDavid Edmondson 	bdesc->free_rtn.free_func = xnf_buf_recycle;
234756567907SDavid Edmondson 	bdesc->free_rtn.free_arg = (caddr_t)bdesc;
234856567907SDavid Edmondson 	bdesc->xnfp = xnfp;
234956567907SDavid Edmondson 	bdesc->buf_phys = dma_cookie.dmac_laddress;
235056567907SDavid Edmondson 	bdesc->buf_mfn = pfn_to_mfn(xnf_btop(bdesc->buf_phys));
235156567907SDavid Edmondson 	bdesc->len = dma_cookie.dmac_size;
235256567907SDavid Edmondson 	bdesc->grant_ref = INVALID_GRANT_REF;
235356567907SDavid Edmondson 	bdesc->gen = xnfp->xnf_gen;
2354551bc2a6Smrj 
2355*1a5e258fSJosef 'Jeff' Sipek 	atomic_inc_64(&xnfp->xnf_stat_buf_allocated);
2356843e1988Sjohnlev 
235756567907SDavid Edmondson 	return (0);
2358843e1988Sjohnlev 
2359551bc2a6Smrj failure_2:
2360843e1988Sjohnlev 	ddi_dma_mem_free(&bdesc->acc_handle);
2361843e1988Sjohnlev 
2362551bc2a6Smrj failure_1:
2363843e1988Sjohnlev 	ddi_dma_free_handle(&bdesc->dma_handle);
2364843e1988Sjohnlev 
2365843e1988Sjohnlev failure:
236656567907SDavid Edmondson 
2367fd0939efSDavid Edmondson 	ASSERT(kmflag & KM_NOSLEEP); /* Cannot fail for KM_SLEEP. */
236856567907SDavid Edmondson 	return (-1);
236956567907SDavid Edmondson }
237056567907SDavid Edmondson 
237156567907SDavid Edmondson static void
237256567907SDavid Edmondson xnf_buf_destructor(void *buf, void *arg)
237356567907SDavid Edmondson {
237456567907SDavid Edmondson 	xnf_buf_t *bdesc = buf;
237556567907SDavid Edmondson 	xnf_t *xnfp = arg;
237656567907SDavid Edmondson 
237756567907SDavid Edmondson 	(void) ddi_dma_unbind_handle(bdesc->dma_handle);
237856567907SDavid Edmondson 	ddi_dma_mem_free(&bdesc->acc_handle);
237956567907SDavid Edmondson 	ddi_dma_free_handle(&bdesc->dma_handle);
238056567907SDavid Edmondson 
2381*1a5e258fSJosef 'Jeff' Sipek 	atomic_dec_64(&xnfp->xnf_stat_buf_allocated);
238256567907SDavid Edmondson }
238356567907SDavid Edmondson 
238456567907SDavid Edmondson static xnf_buf_t *
238556567907SDavid Edmondson xnf_buf_get(xnf_t *xnfp, int flags, boolean_t readonly)
238656567907SDavid Edmondson {
238756567907SDavid Edmondson 	grant_ref_t gref;
238856567907SDavid Edmondson 	xnf_buf_t *bufp;
238956567907SDavid Edmondson 
239056567907SDavid Edmondson 	/*
239156567907SDavid Edmondson 	 * Usually grant references are more scarce than memory, so we
239256567907SDavid Edmondson 	 * attempt to acquire a grant reference first.
239356567907SDavid Edmondson 	 */
239456567907SDavid Edmondson 	gref = gref_get(xnfp);
239556567907SDavid Edmondson 	if (gref == INVALID_GRANT_REF)
2396843e1988Sjohnlev 		return (NULL);
239756567907SDavid Edmondson 
239856567907SDavid Edmondson 	bufp = kmem_cache_alloc(xnfp->xnf_buf_cache, flags);
239956567907SDavid Edmondson 	if (bufp == NULL) {
240056567907SDavid Edmondson 		gref_put(xnfp, gref);
240156567907SDavid Edmondson 		return (NULL);
240256567907SDavid Edmondson 	}
240356567907SDavid Edmondson 
240456567907SDavid Edmondson 	ASSERT(bufp->grant_ref == INVALID_GRANT_REF);
240556567907SDavid Edmondson 
240656567907SDavid Edmondson 	bufp->grant_ref = gref;
240756567907SDavid Edmondson 
240856567907SDavid Edmondson 	if (bufp->gen != xnfp->xnf_gen)
240956567907SDavid Edmondson 		xnf_buf_refresh(bufp);
241056567907SDavid Edmondson 
241156567907SDavid Edmondson 	gnttab_grant_foreign_access_ref(bufp->grant_ref,
241256567907SDavid Edmondson 	    xvdi_get_oeid(bufp->xnfp->xnf_devinfo),
241356567907SDavid Edmondson 	    bufp->buf_mfn, readonly ? 1 : 0);
241456567907SDavid Edmondson 
2415*1a5e258fSJosef 'Jeff' Sipek 	atomic_inc_64(&xnfp->xnf_stat_buf_outstanding);
241656567907SDavid Edmondson 
241756567907SDavid Edmondson 	return (bufp);
241856567907SDavid Edmondson }
241956567907SDavid Edmondson 
242056567907SDavid Edmondson static void
242156567907SDavid Edmondson xnf_buf_put(xnf_t *xnfp, xnf_buf_t *bufp, boolean_t readonly)
242256567907SDavid Edmondson {
242356567907SDavid Edmondson 	if (bufp->grant_ref != INVALID_GRANT_REF) {
242456567907SDavid Edmondson 		(void) gnttab_end_foreign_access_ref(
242556567907SDavid Edmondson 		    bufp->grant_ref, readonly ? 1 : 0);
242656567907SDavid Edmondson 		gref_put(xnfp, bufp->grant_ref);
242756567907SDavid Edmondson 		bufp->grant_ref = INVALID_GRANT_REF;
242856567907SDavid Edmondson 	}
242956567907SDavid Edmondson 
243056567907SDavid Edmondson 	kmem_cache_free(xnfp->xnf_buf_cache, bufp);
243156567907SDavid Edmondson 
2432*1a5e258fSJosef 'Jeff' Sipek 	atomic_dec_64(&xnfp->xnf_stat_buf_outstanding);
243356567907SDavid Edmondson }
243456567907SDavid Edmondson 
243556567907SDavid Edmondson /*
243656567907SDavid Edmondson  * Refresh any cached data about a buffer after resume.
243756567907SDavid Edmondson  */
243856567907SDavid Edmondson static void
243956567907SDavid Edmondson xnf_buf_refresh(xnf_buf_t *bdesc)
244056567907SDavid Edmondson {
244156567907SDavid Edmondson 	bdesc->buf_mfn = pfn_to_mfn(xnf_btop(bdesc->buf_phys));
244256567907SDavid Edmondson 	bdesc->gen = bdesc->xnfp->xnf_gen;
244356567907SDavid Edmondson }
244456567907SDavid Edmondson 
244556567907SDavid Edmondson /*
244656567907SDavid Edmondson  * Streams `freeb' routine for `xnf_buf_t' when used as transmit
244756567907SDavid Edmondson  * look-aside buffers.
244856567907SDavid Edmondson  */
244956567907SDavid Edmondson static void
245056567907SDavid Edmondson xnf_buf_recycle(xnf_buf_t *bdesc)
245156567907SDavid Edmondson {
245256567907SDavid Edmondson 	xnf_t *xnfp = bdesc->xnfp;
245356567907SDavid Edmondson 
245456567907SDavid Edmondson 	xnf_buf_put(xnfp, bdesc, B_TRUE);
245556567907SDavid Edmondson }
245656567907SDavid Edmondson 
245756567907SDavid Edmondson static int
245856567907SDavid Edmondson xnf_tx_buf_constructor(void *buf, void *arg, int kmflag)
245956567907SDavid Edmondson {
2460fd0939efSDavid Edmondson 	int (*ddiflags)(caddr_t) = DDI_DMA_SLEEP;
246156567907SDavid Edmondson 	xnf_txbuf_t *txp = buf;
246256567907SDavid Edmondson 	xnf_t *xnfp = arg;
246356567907SDavid Edmondson 
2464fd0939efSDavid Edmondson 	if (kmflag & KM_NOSLEEP)
2465fd0939efSDavid Edmondson 		ddiflags = DDI_DMA_DONTWAIT;
2466fd0939efSDavid Edmondson 
246756567907SDavid Edmondson 	if (ddi_dma_alloc_handle(xnfp->xnf_devinfo, &buf_dma_attr,
2468fd0939efSDavid Edmondson 	    ddiflags, 0, &txp->tx_dma_handle) != DDI_SUCCESS) {
2469fd0939efSDavid Edmondson 		ASSERT(kmflag & KM_NOSLEEP); /* Cannot fail for KM_SLEEP. */
247056567907SDavid Edmondson 		return (-1);
2471fd0939efSDavid Edmondson 	}
247256567907SDavid Edmondson 
247356567907SDavid Edmondson 	return (0);
247456567907SDavid Edmondson }
247556567907SDavid Edmondson 
247656567907SDavid Edmondson static void
247756567907SDavid Edmondson xnf_tx_buf_destructor(void *buf, void *arg)
247856567907SDavid Edmondson {
247956567907SDavid Edmondson 	_NOTE(ARGUNUSED(arg));
248056567907SDavid Edmondson 	xnf_txbuf_t *txp = buf;
248156567907SDavid Edmondson 
248256567907SDavid Edmondson 	ddi_dma_free_handle(&txp->tx_dma_handle);
2483843e1988Sjohnlev }
2484843e1988Sjohnlev 
2485551bc2a6Smrj /*
2486551bc2a6Smrj  * Statistics.
2487551bc2a6Smrj  */
2488551bc2a6Smrj static char *xnf_aux_statistics[] = {
2489551bc2a6Smrj 	"tx_cksum_deferred",
2490551bc2a6Smrj 	"rx_cksum_no_need",
2491551bc2a6Smrj 	"interrupts",
2492551bc2a6Smrj 	"unclaimed_interrupts",
2493551bc2a6Smrj 	"tx_pullup",
2494551bc2a6Smrj 	"tx_pagebndry",
2495551bc2a6Smrj 	"tx_attempt",
249656567907SDavid Edmondson 	"buf_allocated",
249756567907SDavid Edmondson 	"buf_outstanding",
249856567907SDavid Edmondson 	"gref_outstanding",
249956567907SDavid Edmondson 	"gref_failure",
250056567907SDavid Edmondson 	"gref_peak",
250156567907SDavid Edmondson 	"rx_allocb_fail",
250256567907SDavid Edmondson 	"rx_desballoc_fail",
2503551bc2a6Smrj };
2504551bc2a6Smrj 
2505551bc2a6Smrj static int
2506551bc2a6Smrj xnf_kstat_aux_update(kstat_t *ksp, int flag)
2507551bc2a6Smrj {
2508551bc2a6Smrj 	xnf_t *xnfp;
2509551bc2a6Smrj 	kstat_named_t *knp;
2510551bc2a6Smrj 
2511551bc2a6Smrj 	if (flag != KSTAT_READ)
2512551bc2a6Smrj 		return (EACCES);
2513551bc2a6Smrj 
2514551bc2a6Smrj 	xnfp = ksp->ks_private;
2515551bc2a6Smrj 	knp = ksp->ks_data;
2516551bc2a6Smrj 
2517551bc2a6Smrj 	/*
2518551bc2a6Smrj 	 * Assignment order must match that of the names in
2519551bc2a6Smrj 	 * xnf_aux_statistics.
2520551bc2a6Smrj 	 */
2521551bc2a6Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_tx_cksum_deferred;
2522551bc2a6Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_rx_cksum_no_need;
2523551bc2a6Smrj 
2524551bc2a6Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_interrupts;
2525551bc2a6Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_unclaimed_interrupts;
2526551bc2a6Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_tx_pullup;
2527551bc2a6Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_tx_pagebndry;
2528551bc2a6Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_tx_attempt;
2529551bc2a6Smrj 
253056567907SDavid Edmondson 	(knp++)->value.ui64 = xnfp->xnf_stat_buf_allocated;
253156567907SDavid Edmondson 	(knp++)->value.ui64 = xnfp->xnf_stat_buf_outstanding;
253256567907SDavid Edmondson 	(knp++)->value.ui64 = xnfp->xnf_stat_gref_outstanding;
253356567907SDavid Edmondson 	(knp++)->value.ui64 = xnfp->xnf_stat_gref_failure;
253456567907SDavid Edmondson 	(knp++)->value.ui64 = xnfp->xnf_stat_gref_peak;
253556567907SDavid Edmondson 	(knp++)->value.ui64 = xnfp->xnf_stat_rx_allocb_fail;
253656567907SDavid Edmondson 	(knp++)->value.ui64 = xnfp->xnf_stat_rx_desballoc_fail;
2537551bc2a6Smrj 
2538551bc2a6Smrj 	return (0);
2539551bc2a6Smrj }
2540551bc2a6Smrj 
2541551bc2a6Smrj static boolean_t
2542551bc2a6Smrj xnf_kstat_init(xnf_t *xnfp)
2543551bc2a6Smrj {
2544551bc2a6Smrj 	int nstat = sizeof (xnf_aux_statistics) /
2545551bc2a6Smrj 	    sizeof (xnf_aux_statistics[0]);
2546551bc2a6Smrj 	char **cp = xnf_aux_statistics;
2547551bc2a6Smrj 	kstat_named_t *knp;
2548551bc2a6Smrj 
2549551bc2a6Smrj 	/*
2550551bc2a6Smrj 	 * Create and initialise kstats.
2551551bc2a6Smrj 	 */
2552551bc2a6Smrj 	if ((xnfp->xnf_kstat_aux = kstat_create("xnf",
2553551bc2a6Smrj 	    ddi_get_instance(xnfp->xnf_devinfo),
2554551bc2a6Smrj 	    "aux_statistics", "net", KSTAT_TYPE_NAMED,
2555551bc2a6Smrj 	    nstat, 0)) == NULL)
2556551bc2a6Smrj 		return (B_FALSE);
2557551bc2a6Smrj 
2558551bc2a6Smrj 	xnfp->xnf_kstat_aux->ks_private = xnfp;
2559551bc2a6Smrj 	xnfp->xnf_kstat_aux->ks_update = xnf_kstat_aux_update;
2560551bc2a6Smrj 
2561551bc2a6Smrj 	knp = xnfp->xnf_kstat_aux->ks_data;
2562551bc2a6Smrj 	while (nstat > 0) {
2563551bc2a6Smrj 		kstat_named_init(knp, *cp, KSTAT_DATA_UINT64);
2564551bc2a6Smrj 
2565551bc2a6Smrj 		knp++;
2566551bc2a6Smrj 		cp++;
2567551bc2a6Smrj 		nstat--;
2568551bc2a6Smrj 	}
2569551bc2a6Smrj 
2570551bc2a6Smrj 	kstat_install(xnfp->xnf_kstat_aux);
2571551bc2a6Smrj 
2572551bc2a6Smrj 	return (B_TRUE);
2573551bc2a6Smrj }
2574551bc2a6Smrj 
2575843e1988Sjohnlev static int
2576843e1988Sjohnlev xnf_stat(void *arg, uint_t stat, uint64_t *val)
2577843e1988Sjohnlev {
2578843e1988Sjohnlev 	xnf_t *xnfp = arg;
2579843e1988Sjohnlev 
258056567907SDavid Edmondson 	mutex_enter(&xnfp->xnf_rxlock);
2581551bc2a6Smrj 	mutex_enter(&xnfp->xnf_txlock);
2582843e1988Sjohnlev 
2583551bc2a6Smrj #define	mac_stat(q, r)				\
2584843e1988Sjohnlev 	case (MAC_STAT_##q):			\
2585551bc2a6Smrj 		*val = xnfp->xnf_stat_##r;	\
2586551bc2a6Smrj 		break
2587551bc2a6Smrj 
2588551bc2a6Smrj #define	ether_stat(q, r)			\
2589551bc2a6Smrj 	case (ETHER_STAT_##q):			\
2590551bc2a6Smrj 		*val = xnfp->xnf_stat_##r;	\
2591843e1988Sjohnlev 		break
2592843e1988Sjohnlev 
2593843e1988Sjohnlev 	switch (stat) {
2594843e1988Sjohnlev 
2595551bc2a6Smrj 	mac_stat(IPACKETS, ipackets);
2596551bc2a6Smrj 	mac_stat(OPACKETS, opackets);
2597551bc2a6Smrj 	mac_stat(RBYTES, rbytes);
2598551bc2a6Smrj 	mac_stat(OBYTES, obytes);
2599551bc2a6Smrj 	mac_stat(NORCVBUF, norxbuf);
2600551bc2a6Smrj 	mac_stat(IERRORS, errrx);
2601551bc2a6Smrj 	mac_stat(NOXMTBUF, tx_defer);
2602551bc2a6Smrj 
2603551bc2a6Smrj 	ether_stat(MACRCV_ERRORS, mac_rcv_error);
2604551bc2a6Smrj 	ether_stat(TOOSHORT_ERRORS, runt);
2605843e1988Sjohnlev 
26064bae950fSMax zhen 	/* always claim to be in full duplex mode */
26074bae950fSMax zhen 	case ETHER_STAT_LINK_DUPLEX:
26084bae950fSMax zhen 		*val = LINK_DUPLEX_FULL;
26094bae950fSMax zhen 		break;
26104bae950fSMax zhen 
26114bae950fSMax zhen 	/* always claim to be at 1Gb/s link speed */
26124bae950fSMax zhen 	case MAC_STAT_IFSPEED:
26134bae950fSMax zhen 		*val = 1000000000ull;
26144bae950fSMax zhen 		break;
26154bae950fSMax zhen 
2616843e1988Sjohnlev 	default:
2617551bc2a6Smrj 		mutex_exit(&xnfp->xnf_txlock);
261856567907SDavid Edmondson 		mutex_exit(&xnfp->xnf_rxlock);
2619843e1988Sjohnlev 
2620843e1988Sjohnlev 		return (ENOTSUP);
2621843e1988Sjohnlev 	}
2622843e1988Sjohnlev 
2623551bc2a6Smrj #undef mac_stat
2624551bc2a6Smrj #undef ether_stat
2625843e1988Sjohnlev 
2626551bc2a6Smrj 	mutex_exit(&xnfp->xnf_txlock);
262756567907SDavid Edmondson 	mutex_exit(&xnfp->xnf_rxlock);
2628843e1988Sjohnlev 
2629843e1988Sjohnlev 	return (0);
2630843e1988Sjohnlev }
2631843e1988Sjohnlev 
2632843e1988Sjohnlev static boolean_t
2633843e1988Sjohnlev xnf_getcapab(void *arg, mac_capab_t cap, void *cap_data)
2634843e1988Sjohnlev {
263556567907SDavid Edmondson 	_NOTE(ARGUNUSED(arg));
2636843e1988Sjohnlev 
2637843e1988Sjohnlev 	switch (cap) {
2638843e1988Sjohnlev 	case MAC_CAPAB_HCKSUM: {
2639843e1988Sjohnlev 		uint32_t *capab = cap_data;
2640843e1988Sjohnlev 
2641568a765bSdme 		/*
2642a859da42SDavid Edmondson 		 * Whilst the flag used to communicate with the IO
2643a859da42SDavid Edmondson 		 * domain is called "NETTXF_csum_blank", the checksum
2644a859da42SDavid Edmondson 		 * in the packet must contain the pseudo-header
2645a859da42SDavid Edmondson 		 * checksum and not zero.
2646568a765bSdme 		 *
2647a859da42SDavid Edmondson 		 * To help out the IO domain, we might use
2648a859da42SDavid Edmondson 		 * HCKSUM_INET_PARTIAL. Unfortunately our stack will
2649a859da42SDavid Edmondson 		 * then use checksum offload for IPv6 packets, which
2650a859da42SDavid Edmondson 		 * the IO domain can't handle.
2651a859da42SDavid Edmondson 		 *
2652a859da42SDavid Edmondson 		 * As a result, we declare outselves capable of
2653a859da42SDavid Edmondson 		 * HCKSUM_INET_FULL_V4. This means that we receive
2654a859da42SDavid Edmondson 		 * IPv4 packets from the stack with a blank checksum
2655a859da42SDavid Edmondson 		 * field and must insert the pseudo-header checksum
2656a859da42SDavid Edmondson 		 * before passing the packet to the IO domain.
2657568a765bSdme 		 */
2658a859da42SDavid Edmondson 		*capab = HCKSUM_INET_FULL_V4;
2659843e1988Sjohnlev 		break;
2660843e1988Sjohnlev 	}
2661843e1988Sjohnlev 	default:
2662843e1988Sjohnlev 		return (B_FALSE);
2663843e1988Sjohnlev 	}
2664843e1988Sjohnlev 
2665843e1988Sjohnlev 	return (B_TRUE);
2666843e1988Sjohnlev }
2667843e1988Sjohnlev 
266856567907SDavid Edmondson /*
266956567907SDavid Edmondson  * The state of the peer has changed - react accordingly.
267056567907SDavid Edmondson  */
2671843e1988Sjohnlev static void
2672843e1988Sjohnlev oe_state_change(dev_info_t *dip, ddi_eventcookie_t id,
2673843e1988Sjohnlev     void *arg, void *impl_data)
2674843e1988Sjohnlev {
267556567907SDavid Edmondson 	_NOTE(ARGUNUSED(id, arg));
2676843e1988Sjohnlev 	xnf_t *xnfp = ddi_get_driver_private(dip);
2677843e1988Sjohnlev 	XenbusState new_state = *(XenbusState *)impl_data;
2678843e1988Sjohnlev 
2679843e1988Sjohnlev 	ASSERT(xnfp != NULL);
2680843e1988Sjohnlev 
2681843e1988Sjohnlev 	switch (new_state) {
268256567907SDavid Edmondson 	case XenbusStateUnknown:
268356567907SDavid Edmondson 	case XenbusStateInitialising:
268456567907SDavid Edmondson 	case XenbusStateInitialised:
268556567907SDavid Edmondson 	case XenbusStateClosing:
268656567907SDavid Edmondson 	case XenbusStateClosed:
268756567907SDavid Edmondson 	case XenbusStateReconfiguring:
268856567907SDavid Edmondson 	case XenbusStateReconfigured:
268956567907SDavid Edmondson 		break;
269056567907SDavid Edmondson 
269156567907SDavid Edmondson 	case XenbusStateInitWait:
269256567907SDavid Edmondson 		xnf_read_config(xnfp);
269356567907SDavid Edmondson 
269456567907SDavid Edmondson 		if (!xnfp->xnf_be_rx_copy) {
269556567907SDavid Edmondson 			cmn_err(CE_WARN,
269656567907SDavid Edmondson 			    "The xnf driver requires a dom0 that "
269756567907SDavid Edmondson 			    "supports 'feature-rx-copy'.");
269856567907SDavid Edmondson 			(void) xvdi_switch_state(xnfp->xnf_devinfo,
269956567907SDavid Edmondson 			    XBT_NULL, XenbusStateClosed);
270056567907SDavid Edmondson 			break;
270156567907SDavid Edmondson 		}
270256567907SDavid Edmondson 
270356567907SDavid Edmondson 		/*
270456567907SDavid Edmondson 		 * Connect to the backend.
270556567907SDavid Edmondson 		 */
270656567907SDavid Edmondson 		xnf_be_connect(xnfp);
270756567907SDavid Edmondson 
270856567907SDavid Edmondson 		/*
270956567907SDavid Edmondson 		 * Our MAC address as discovered by xnf_read_config().
271056567907SDavid Edmondson 		 */
271156567907SDavid Edmondson 		mac_unicst_update(xnfp->xnf_mh, xnfp->xnf_mac_addr);
271256567907SDavid Edmondson 
271356567907SDavid Edmondson 		break;
271456567907SDavid Edmondson 
2715843e1988Sjohnlev 	case XenbusStateConnected:
271656567907SDavid Edmondson 		mutex_enter(&xnfp->xnf_rxlock);
2717551bc2a6Smrj 		mutex_enter(&xnfp->xnf_txlock);
2718843e1988Sjohnlev 
2719551bc2a6Smrj 		xnfp->xnf_connected = B_TRUE;
2720a390c5f4Scz147101 		/*
272156567907SDavid Edmondson 		 * Wake up any threads waiting to send data to
272256567907SDavid Edmondson 		 * backend.
2723a390c5f4Scz147101 		 */
272456567907SDavid Edmondson 		cv_broadcast(&xnfp->xnf_cv_state);
2725843e1988Sjohnlev 
2726551bc2a6Smrj 		mutex_exit(&xnfp->xnf_txlock);
272756567907SDavid Edmondson 		mutex_exit(&xnfp->xnf_rxlock);
2728843e1988Sjohnlev 
2729a390c5f4Scz147101 		/*
273056567907SDavid Edmondson 		 * Kick the peer in case it missed any transmits
273156567907SDavid Edmondson 		 * request in the TX ring.
2732a390c5f4Scz147101 		 */
2733551bc2a6Smrj 		ec_notify_via_evtchn(xnfp->xnf_evtchn);
2734a390c5f4Scz147101 
2735a390c5f4Scz147101 		/*
273656567907SDavid Edmondson 		 * There may already be completed receive requests in
273756567907SDavid Edmondson 		 * the ring sent by backend after it gets connected
273856567907SDavid Edmondson 		 * but before we see its state change here, so we call
273956567907SDavid Edmondson 		 * xnf_intr() to handle them, if any.
2740a390c5f4Scz147101 		 */
2741a390c5f4Scz147101 		(void) xnf_intr((caddr_t)xnfp);
2742a390c5f4Scz147101 
274356567907SDavid Edmondson 		/*
274456567907SDavid Edmondson 		 * Mark the link up now that we are connected.
274556567907SDavid Edmondson 		 */
27464bae950fSMax zhen 		mac_link_update(xnfp->xnf_mh, LINK_STATE_UP);
27474bae950fSMax zhen 
274856567907SDavid Edmondson 		/*
274956567907SDavid Edmondson 		 * Tell the backend about the multicast addresses in
275056567907SDavid Edmondson 		 * which we are interested.
275156567907SDavid Edmondson 		 */
275256567907SDavid Edmondson 		mac_multicast_refresh(xnfp->xnf_mh, NULL, xnfp, B_TRUE);
275356567907SDavid Edmondson 
2754843e1988Sjohnlev 		break;
2755843e1988Sjohnlev 
2756843e1988Sjohnlev 	default:
2757843e1988Sjohnlev 		break;
2758843e1988Sjohnlev 	}
2759843e1988Sjohnlev }
2760