xref: /onnv-gate/usr/src/uts/common/xen/io/xnf.c (revision 11878:ac93462db6d7)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev 
225084Sjohnlev /*
2311588Sdavid.edmondson@sun.com  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
245084Sjohnlev  * Use is subject to license terms.
255084Sjohnlev  */
265084Sjohnlev 
275084Sjohnlev /*
285084Sjohnlev  *
295084Sjohnlev  * Copyright (c) 2004 Christian Limpach.
305084Sjohnlev  * All rights reserved.
315084Sjohnlev  *
325084Sjohnlev  * Redistribution and use in source and binary forms, with or without
335084Sjohnlev  * modification, are permitted provided that the following conditions
345084Sjohnlev  * are met:
355084Sjohnlev  * 1. Redistributions of source code must retain the above copyright
365084Sjohnlev  *    notice, this list of conditions and the following disclaimer.
375084Sjohnlev  * 2. Redistributions in binary form must reproduce the above copyright
385084Sjohnlev  *    notice, this list of conditions and the following disclaimer in the
395084Sjohnlev  *    documentation and/or other materials provided with the distribution.
405084Sjohnlev  * 3. This section intentionally left blank.
415084Sjohnlev  * 4. The name of the author may not be used to endorse or promote products
425084Sjohnlev  *    derived from this software without specific prior written permission.
435084Sjohnlev  *
445084Sjohnlev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
455084Sjohnlev  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
465084Sjohnlev  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
475084Sjohnlev  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
485084Sjohnlev  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
495084Sjohnlev  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
505084Sjohnlev  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
515084Sjohnlev  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
525084Sjohnlev  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
535084Sjohnlev  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
545084Sjohnlev  */
555084Sjohnlev /*
565084Sjohnlev  * Section 3 of the above license was updated in response to bug 6379571.
575084Sjohnlev  */
585084Sjohnlev 
595084Sjohnlev /*
6010958Sdme@sun.com  * xnf.c - GLDv3 network driver for domU.
6110958Sdme@sun.com  */
6210958Sdme@sun.com 
6310958Sdme@sun.com /*
6410958Sdme@sun.com  * This driver uses four per-instance locks:
6510958Sdme@sun.com  *
6610958Sdme@sun.com  * xnf_gref_lock:
6710958Sdme@sun.com  *
6810958Sdme@sun.com  *    Protects access to the grant reference list stored in
6910958Sdme@sun.com  *    xnf_gref_head. Grant references should be acquired and released
7010958Sdme@sun.com  *    using gref_get() and gref_put() respectively.
7110958Sdme@sun.com  *
7210958Sdme@sun.com  * xnf_schedlock:
7310958Sdme@sun.com  *
7410958Sdme@sun.com  *    Protects:
7510958Sdme@sun.com  *    xnf_need_sched - used to record that a previous transmit attempt
7610958Sdme@sun.com  *       failed (and consequently it will be necessary to call
7710958Sdme@sun.com  *       mac_tx_update() when transmit resources are available).
7810958Sdme@sun.com  *    xnf_pending_multicast - the number of multicast requests that
7910958Sdme@sun.com  *       have been submitted to the backend for which we have not
8010958Sdme@sun.com  *       processed responses.
8110958Sdme@sun.com  *
8210958Sdme@sun.com  * xnf_txlock:
8310958Sdme@sun.com  *
8410958Sdme@sun.com  *    Protects the transmit ring (xnf_tx_ring) and associated
8510958Sdme@sun.com  *    structures (notably xnf_tx_pkt_id and xnf_tx_pkt_id_head).
8610958Sdme@sun.com  *
8710958Sdme@sun.com  * xnf_rxlock:
8810958Sdme@sun.com  *
8910958Sdme@sun.com  *    Protects the receive ring (xnf_rx_ring) and associated
9010958Sdme@sun.com  *    structures (notably xnf_rx_pkt_info).
9110958Sdme@sun.com  *
9210958Sdme@sun.com  * If driver-global state that affects both the transmit and receive
9310958Sdme@sun.com  * rings is manipulated, both xnf_txlock and xnf_rxlock should be
9410958Sdme@sun.com  * held, in that order.
9510958Sdme@sun.com  *
9610958Sdme@sun.com  * xnf_schedlock is acquired both whilst holding xnf_txlock and
9710958Sdme@sun.com  * without. It should always be acquired after xnf_txlock if both are
9810958Sdme@sun.com  * held.
9910958Sdme@sun.com  *
10010958Sdme@sun.com  * Notes:
10110958Sdme@sun.com  * - atomic_add_64() is used to manipulate counters where we require
10210958Sdme@sun.com  *   accuracy. For counters intended only for observation by humans,
10310958Sdme@sun.com  *   post increment/decrement are used instead.
1045084Sjohnlev  */
1055084Sjohnlev 
1065084Sjohnlev #include <sys/types.h>
1075084Sjohnlev #include <sys/errno.h>
1085084Sjohnlev #include <sys/param.h>
1095084Sjohnlev #include <sys/sysmacros.h>
1105084Sjohnlev #include <sys/systm.h>
1115084Sjohnlev #include <sys/stream.h>
1125084Sjohnlev #include <sys/strsubr.h>
11310958Sdme@sun.com #include <sys/strsun.h>
1145084Sjohnlev #include <sys/conf.h>
1155084Sjohnlev #include <sys/ddi.h>
1165084Sjohnlev #include <sys/devops.h>
1175084Sjohnlev #include <sys/sunddi.h>
1185084Sjohnlev #include <sys/sunndi.h>
1195084Sjohnlev #include <sys/dlpi.h>
1205084Sjohnlev #include <sys/ethernet.h>
1215084Sjohnlev #include <sys/strsun.h>
1225084Sjohnlev #include <sys/pattr.h>
1235084Sjohnlev #include <inet/ip.h>
1247351Sdme@sun.com #include <inet/ip_impl.h>
1257351Sdme@sun.com #include <sys/gld.h>
1265084Sjohnlev #include <sys/modctl.h>
1278275SEric Cheng #include <sys/mac_provider.h>
1285084Sjohnlev #include <sys/mac_ether.h>
1295084Sjohnlev #include <sys/bootinfo.h>
1305084Sjohnlev #include <sys/mach_mmu.h>
1315741Smrj #ifdef	XPV_HVM_DRIVER
1325741Smrj #include <sys/xpv_support.h>
1335741Smrj #include <sys/hypervisor.h>
1345741Smrj #else
1355741Smrj #include <sys/hypervisor.h>
1365084Sjohnlev #include <sys/evtchn_impl.h>
1375084Sjohnlev #include <sys/balloon_impl.h>
1385741Smrj #endif
1395741Smrj #include <xen/public/io/netif.h>
1405741Smrj #include <sys/gnttab.h>
1415084Sjohnlev #include <xen/sys/xendev.h>
1425741Smrj #include <sys/sdt.h>
14310958Sdme@sun.com #include <sys/note.h>
14410958Sdme@sun.com #include <sys/debug.h>
1455741Smrj 
1465741Smrj #include <io/xnf.h>
1475741Smrj 
1485084Sjohnlev #if defined(DEBUG) || defined(__lint)
1495084Sjohnlev #define	XNF_DEBUG
15010958Sdme@sun.com #endif
15110958Sdme@sun.com 
15210958Sdme@sun.com #ifdef XNF_DEBUG
15310958Sdme@sun.com int xnf_debug = 0;
15410958Sdme@sun.com xnf_t *xnf_debug_instance = NULL;
1555084Sjohnlev #endif
1565084Sjohnlev 
1575084Sjohnlev /*
1585084Sjohnlev  * On a 32 bit PAE system physical and machine addresses are larger
1595084Sjohnlev  * than 32 bits.  ddi_btop() on such systems take an unsigned long
1605084Sjohnlev  * argument, and so addresses above 4G are truncated before ddi_btop()
1615084Sjohnlev  * gets to see them.  To avoid this, code the shift operation here.
1625084Sjohnlev  */
1635084Sjohnlev #define	xnf_btop(addr)	((addr) >> PAGESHIFT)
1645084Sjohnlev 
16510958Sdme@sun.com unsigned int	xnf_max_tx_frags = 1;
16610958Sdme@sun.com 
16710958Sdme@sun.com /*
16810958Sdme@sun.com  * Should we use the multicast control feature if the backend provides
16910958Sdme@sun.com  * it?
17010958Sdme@sun.com  */
17110958Sdme@sun.com boolean_t xnf_multicast_control = B_TRUE;
1725741Smrj 
1735084Sjohnlev /*
17410958Sdme@sun.com  * Received packets below this size are copied to a new streams buffer
17510958Sdme@sun.com  * rather than being desballoc'ed.
17610958Sdme@sun.com  *
17710958Sdme@sun.com  * This value is chosen to accommodate traffic where there are a large
17810958Sdme@sun.com  * number of small packets. For data showing a typical distribution,
17910958Sdme@sun.com  * see:
18010958Sdme@sun.com  *
18110958Sdme@sun.com  * Sinha07a:
18210958Sdme@sun.com  *	Rishi Sinha, Christos Papadopoulos, and John
18310958Sdme@sun.com  *	Heidemann. Internet Packet Size Distributions: Some
18410958Sdme@sun.com  *	Observations. Technical Report ISI-TR-2007-643,
18510958Sdme@sun.com  *	USC/Information Sciences Institute, May, 2007. Orignally
18610958Sdme@sun.com  *	released October 2005 as web page
18710958Sdme@sun.com  *	http://netweb.usc.edu/~sinha/pkt-sizes/.
18810958Sdme@sun.com  *	<http://www.isi.edu/~johnh/PAPERS/Sinha07a.html>.
1895084Sjohnlev  */
19010958Sdme@sun.com size_t xnf_rx_copy_limit = 64;
19110958Sdme@sun.com 
19210958Sdme@sun.com #define	INVALID_GRANT_HANDLE	((grant_handle_t)-1)
19310958Sdme@sun.com #define	INVALID_GRANT_REF	((grant_ref_t)-1)
19410958Sdme@sun.com #define	INVALID_TX_ID		((uint16_t)-1)
19510958Sdme@sun.com 
19610958Sdme@sun.com #define	TX_ID_TO_TXID(p, id) (&((p)->xnf_tx_pkt_id[(id)]))
19710958Sdme@sun.com #define	TX_ID_VALID(i) (((i) != INVALID_TX_ID) && ((i) < NET_TX_RING_SIZE))
1985084Sjohnlev 
1995084Sjohnlev /* Required system entry points */
2005084Sjohnlev static int	xnf_attach(dev_info_t *, ddi_attach_cmd_t);
2015084Sjohnlev static int	xnf_detach(dev_info_t *, ddi_detach_cmd_t);
2025084Sjohnlev 
2035084Sjohnlev /* Required driver entry points for Nemo */
2045084Sjohnlev static int	xnf_start(void *);
2055084Sjohnlev static void	xnf_stop(void *);
2065084Sjohnlev static int	xnf_set_mac_addr(void *, const uint8_t *);
2075084Sjohnlev static int	xnf_set_multicast(void *, boolean_t, const uint8_t *);
2085084Sjohnlev static int	xnf_set_promiscuous(void *, boolean_t);
2095084Sjohnlev static mblk_t	*xnf_send(void *, mblk_t *);
2105084Sjohnlev static uint_t	xnf_intr(caddr_t);
2115084Sjohnlev static int	xnf_stat(void *, uint_t, uint64_t *);
2125084Sjohnlev static boolean_t xnf_getcapab(void *, mac_capab_t, void *);
2135084Sjohnlev 
2145084Sjohnlev /* Driver private functions */
2155084Sjohnlev static int xnf_alloc_dma_resources(xnf_t *);
2165084Sjohnlev static void xnf_release_dma_resources(xnf_t *);
2175084Sjohnlev static void xnf_release_mblks(xnf_t *);
21810958Sdme@sun.com 
21910958Sdme@sun.com static int xnf_buf_constructor(void *, void *, int);
22010958Sdme@sun.com static void xnf_buf_destructor(void *, void *);
22110958Sdme@sun.com static xnf_buf_t *xnf_buf_get(xnf_t *, int, boolean_t);
22210958Sdme@sun.com #pragma inline(xnf_buf_get)
22310958Sdme@sun.com static void xnf_buf_put(xnf_t *, xnf_buf_t *, boolean_t);
22410958Sdme@sun.com #pragma inline(xnf_buf_put)
22510958Sdme@sun.com static void xnf_buf_refresh(xnf_buf_t *);
22610958Sdme@sun.com #pragma inline(xnf_buf_refresh)
22710958Sdme@sun.com static void xnf_buf_recycle(xnf_buf_t *);
22810958Sdme@sun.com 
22910958Sdme@sun.com static int xnf_tx_buf_constructor(void *, void *, int);
23010958Sdme@sun.com static void xnf_tx_buf_destructor(void *, void *);
23110958Sdme@sun.com 
23210958Sdme@sun.com static grant_ref_t gref_get(xnf_t *);
23310958Sdme@sun.com #pragma inline(gref_get)
23410958Sdme@sun.com static void gref_put(xnf_t *, grant_ref_t);
23510958Sdme@sun.com #pragma inline(gref_put)
23610958Sdme@sun.com 
23710958Sdme@sun.com static xnf_txid_t *txid_get(xnf_t *);
23810958Sdme@sun.com #pragma inline(txid_get)
23910958Sdme@sun.com static void txid_put(xnf_t *, xnf_txid_t *);
24010958Sdme@sun.com #pragma inline(txid_put)
24110958Sdme@sun.com 
2425084Sjohnlev void xnf_send_driver_status(int, int);
24310958Sdme@sun.com static void xnf_rxbuf_hang(xnf_t *, xnf_buf_t *);
24410958Sdme@sun.com static int xnf_tx_clean_ring(xnf_t  *);
2455084Sjohnlev static void oe_state_change(dev_info_t *, ddi_eventcookie_t,
2465084Sjohnlev     void *, void *);
24710958Sdme@sun.com static boolean_t xnf_kstat_init(xnf_t *);
24810958Sdme@sun.com static void xnf_rx_collect(xnf_t *);
24910958Sdme@sun.com 
2505084Sjohnlev static mac_callbacks_t xnf_callbacks = {
25110958Sdme@sun.com 	MC_GETCAPAB,
2525084Sjohnlev 	xnf_stat,
2535084Sjohnlev 	xnf_start,
2545084Sjohnlev 	xnf_stop,
2555084Sjohnlev 	xnf_set_promiscuous,
2565084Sjohnlev 	xnf_set_multicast,
2575084Sjohnlev 	xnf_set_mac_addr,
2585084Sjohnlev 	xnf_send,
25910958Sdme@sun.com 	NULL,
260*11878SVenu.Iyer@Sun.COM 	NULL,
2615084Sjohnlev 	xnf_getcapab
2625084Sjohnlev };
2635084Sjohnlev 
2645084Sjohnlev /* DMA attributes for network ring buffer */
2655084Sjohnlev static ddi_dma_attr_t ringbuf_dma_attr = {
2665084Sjohnlev 	DMA_ATTR_V0,		/* version of this structure */
2675084Sjohnlev 	0,			/* lowest usable address */
2685084Sjohnlev 	0xffffffffffffffffULL,	/* highest usable address */
2695084Sjohnlev 	0x7fffffff,		/* maximum DMAable byte count */
2705084Sjohnlev 	MMU_PAGESIZE,		/* alignment in bytes */
2715084Sjohnlev 	0x7ff,			/* bitmap of burst sizes */
2725084Sjohnlev 	1,			/* minimum transfer */
2735084Sjohnlev 	0xffffffffU,		/* maximum transfer */
2745084Sjohnlev 	0xffffffffffffffffULL,	/* maximum segment length */
2755084Sjohnlev 	1,			/* maximum number of segments */
2765084Sjohnlev 	1,			/* granularity */
2775084Sjohnlev 	0,			/* flags (reserved) */
2785084Sjohnlev };
2795084Sjohnlev 
28010958Sdme@sun.com /* DMA attributes for transmit and receive data */
28110958Sdme@sun.com static ddi_dma_attr_t buf_dma_attr = {
2825084Sjohnlev 	DMA_ATTR_V0,		/* version of this structure */
2835084Sjohnlev 	0,			/* lowest usable address */
2845084Sjohnlev 	0xffffffffffffffffULL,	/* highest usable address */
2855084Sjohnlev 	0x7fffffff,		/* maximum DMAable byte count */
2865084Sjohnlev 	MMU_PAGESIZE,		/* alignment in bytes */
2875084Sjohnlev 	0x7ff,			/* bitmap of burst sizes */
2885084Sjohnlev 	1,			/* minimum transfer */
2895084Sjohnlev 	0xffffffffU,		/* maximum transfer */
2905084Sjohnlev 	0xffffffffffffffffULL,	/* maximum segment length */
2915084Sjohnlev 	1,			/* maximum number of segments */
2925084Sjohnlev 	1,			/* granularity */
2935084Sjohnlev 	0,			/* flags (reserved) */
2945084Sjohnlev };
2955084Sjohnlev 
2965084Sjohnlev /* DMA access attributes for registers and descriptors */
2975084Sjohnlev static ddi_device_acc_attr_t accattr = {
2985084Sjohnlev 	DDI_DEVICE_ATTR_V0,
2995084Sjohnlev 	DDI_STRUCTURE_LE_ACC,	/* This is a little-endian device */
3005084Sjohnlev 	DDI_STRICTORDER_ACC
3015084Sjohnlev };
3025084Sjohnlev 
3035084Sjohnlev /* DMA access attributes for data: NOT to be byte swapped. */
3045084Sjohnlev static ddi_device_acc_attr_t data_accattr = {
3055084Sjohnlev 	DDI_DEVICE_ATTR_V0,
3065084Sjohnlev 	DDI_NEVERSWAP_ACC,
3075084Sjohnlev 	DDI_STRICTORDER_ACC
3085084Sjohnlev };
3095084Sjohnlev 
3105084Sjohnlev DDI_DEFINE_STREAM_OPS(xnf_dev_ops, nulldev, nulldev, xnf_attach, xnf_detach,
3117656SSherry.Moore@Sun.COM     nodev, NULL, D_MP, NULL, ddi_quiesce_not_supported);
3125084Sjohnlev 
3135084Sjohnlev static struct modldrv xnf_modldrv = {
3147351Sdme@sun.com 	&mod_driverops,
3157351Sdme@sun.com 	"Virtual Ethernet driver",
3167351Sdme@sun.com 	&xnf_dev_ops
3175084Sjohnlev };
3185084Sjohnlev 
3195084Sjohnlev static struct modlinkage modlinkage = {
3205084Sjohnlev 	MODREV_1, &xnf_modldrv, NULL
3215084Sjohnlev };
3225084Sjohnlev 
3235084Sjohnlev int
_init(void)3245084Sjohnlev _init(void)
3255084Sjohnlev {
3265084Sjohnlev 	int r;
3275084Sjohnlev 
3285084Sjohnlev 	mac_init_ops(&xnf_dev_ops, "xnf");
3295084Sjohnlev 	r = mod_install(&modlinkage);
3305084Sjohnlev 	if (r != DDI_SUCCESS)
3315084Sjohnlev 		mac_fini_ops(&xnf_dev_ops);
3325084Sjohnlev 
3335084Sjohnlev 	return (r);
3345084Sjohnlev }
3355084Sjohnlev 
3365084Sjohnlev int
_fini(void)3375084Sjohnlev _fini(void)
3385084Sjohnlev {
33910958Sdme@sun.com 	return (EBUSY); /* XXPV should be removable */
3405084Sjohnlev }
3415084Sjohnlev 
3425084Sjohnlev int
_info(struct modinfo * modinfop)3435084Sjohnlev _info(struct modinfo *modinfop)
3445084Sjohnlev {
3455084Sjohnlev 	return (mod_info(&modlinkage, modinfop));
3465084Sjohnlev }
3475084Sjohnlev 
34810958Sdme@sun.com /*
34910958Sdme@sun.com  * Acquire a grant reference.
35010958Sdme@sun.com  */
35110958Sdme@sun.com static grant_ref_t
gref_get(xnf_t * xnfp)35210958Sdme@sun.com gref_get(xnf_t *xnfp)
35310958Sdme@sun.com {
35410958Sdme@sun.com 	grant_ref_t gref;
35510958Sdme@sun.com 
35610958Sdme@sun.com 	mutex_enter(&xnfp->xnf_gref_lock);
35710958Sdme@sun.com 
35810958Sdme@sun.com 	do {
35910958Sdme@sun.com 		gref = gnttab_claim_grant_reference(&xnfp->xnf_gref_head);
36010958Sdme@sun.com 
36110958Sdme@sun.com 	} while ((gref == INVALID_GRANT_REF) &&
36210958Sdme@sun.com 	    (gnttab_alloc_grant_references(16, &xnfp->xnf_gref_head) == 0));
36310958Sdme@sun.com 
36410958Sdme@sun.com 	mutex_exit(&xnfp->xnf_gref_lock);
36510958Sdme@sun.com 
36610958Sdme@sun.com 	if (gref == INVALID_GRANT_REF) {
36710958Sdme@sun.com 		xnfp->xnf_stat_gref_failure++;
36810958Sdme@sun.com 	} else {
36910958Sdme@sun.com 		atomic_add_64(&xnfp->xnf_stat_gref_outstanding, 1);
37010958Sdme@sun.com 		if (xnfp->xnf_stat_gref_outstanding > xnfp->xnf_stat_gref_peak)
37110958Sdme@sun.com 			xnfp->xnf_stat_gref_peak =
37210958Sdme@sun.com 			    xnfp->xnf_stat_gref_outstanding;
37310958Sdme@sun.com 	}
37410958Sdme@sun.com 
37510958Sdme@sun.com 	return (gref);
37610958Sdme@sun.com }
37710958Sdme@sun.com 
37810958Sdme@sun.com /*
37910958Sdme@sun.com  * Release a grant reference.
38010958Sdme@sun.com  */
38110958Sdme@sun.com static void
gref_put(xnf_t * xnfp,grant_ref_t gref)38210958Sdme@sun.com gref_put(xnf_t *xnfp, grant_ref_t gref)
38310958Sdme@sun.com {
38410958Sdme@sun.com 	ASSERT(gref != INVALID_GRANT_REF);
38510958Sdme@sun.com 
38610958Sdme@sun.com 	mutex_enter(&xnfp->xnf_gref_lock);
38710958Sdme@sun.com 	gnttab_release_grant_reference(&xnfp->xnf_gref_head, gref);
38810958Sdme@sun.com 	mutex_exit(&xnfp->xnf_gref_lock);
38910958Sdme@sun.com 
39010958Sdme@sun.com 	atomic_add_64(&xnfp->xnf_stat_gref_outstanding, -1);
39110958Sdme@sun.com }
39210958Sdme@sun.com 
39310958Sdme@sun.com /*
39410958Sdme@sun.com  * Acquire a transmit id.
39510958Sdme@sun.com  */
39610958Sdme@sun.com static xnf_txid_t *
txid_get(xnf_t * xnfp)39710958Sdme@sun.com txid_get(xnf_t *xnfp)
39810958Sdme@sun.com {
39910958Sdme@sun.com 	xnf_txid_t *tidp;
40010958Sdme@sun.com 
40110958Sdme@sun.com 	ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
40210958Sdme@sun.com 
40310958Sdme@sun.com 	if (xnfp->xnf_tx_pkt_id_head == INVALID_TX_ID)
40410958Sdme@sun.com 		return (NULL);
40510958Sdme@sun.com 
40610958Sdme@sun.com 	ASSERT(TX_ID_VALID(xnfp->xnf_tx_pkt_id_head));
40710958Sdme@sun.com 
40810958Sdme@sun.com 	tidp = TX_ID_TO_TXID(xnfp, xnfp->xnf_tx_pkt_id_head);
40910958Sdme@sun.com 	xnfp->xnf_tx_pkt_id_head = tidp->next;
41010958Sdme@sun.com 	tidp->next = INVALID_TX_ID;
41110958Sdme@sun.com 
41210958Sdme@sun.com 	ASSERT(tidp->txbuf == NULL);
41310958Sdme@sun.com 
41410958Sdme@sun.com 	return (tidp);
41510958Sdme@sun.com }
41610958Sdme@sun.com 
41710958Sdme@sun.com /*
41810958Sdme@sun.com  * Release a transmit id.
41910958Sdme@sun.com  */
42010958Sdme@sun.com static void
txid_put(xnf_t * xnfp,xnf_txid_t * tidp)42110958Sdme@sun.com txid_put(xnf_t *xnfp, xnf_txid_t *tidp)
42210958Sdme@sun.com {
42310958Sdme@sun.com 	ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
42410958Sdme@sun.com 	ASSERT(TX_ID_VALID(tidp->id));
42510958Sdme@sun.com 	ASSERT(tidp->next == INVALID_TX_ID);
42610958Sdme@sun.com 
42710958Sdme@sun.com 	tidp->txbuf = NULL;
42810958Sdme@sun.com 	tidp->next = xnfp->xnf_tx_pkt_id_head;
42910958Sdme@sun.com 	xnfp->xnf_tx_pkt_id_head = tidp->id;
43010958Sdme@sun.com }
43110958Sdme@sun.com 
43210958Sdme@sun.com /*
43310958Sdme@sun.com  * Get `wanted' slots in the transmit ring, waiting for at least that
43410958Sdme@sun.com  * number if `wait' is B_TRUE. Force the ring to be cleaned by setting
43510958Sdme@sun.com  * `wanted' to zero.
43610958Sdme@sun.com  *
43710958Sdme@sun.com  * Return the number of slots available.
43810958Sdme@sun.com  */
43910958Sdme@sun.com static int
tx_slots_get(xnf_t * xnfp,int wanted,boolean_t wait)44010958Sdme@sun.com tx_slots_get(xnf_t *xnfp, int wanted, boolean_t wait)
44110958Sdme@sun.com {
44210958Sdme@sun.com 	int slotsfree;
44310958Sdme@sun.com 	boolean_t forced_clean = (wanted == 0);
44410958Sdme@sun.com 
44510958Sdme@sun.com 	ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
44610958Sdme@sun.com 
44710958Sdme@sun.com 	/* LINTED: constant in conditional context */
44810958Sdme@sun.com 	while (B_TRUE) {
44910958Sdme@sun.com 		slotsfree = RING_FREE_REQUESTS(&xnfp->xnf_tx_ring);
45010958Sdme@sun.com 
45110958Sdme@sun.com 		if ((slotsfree < wanted) || forced_clean)
45210958Sdme@sun.com 			slotsfree = xnf_tx_clean_ring(xnfp);
45310958Sdme@sun.com 
45410958Sdme@sun.com 		/*
45510958Sdme@sun.com 		 * If there are more than we need free, tell other
45610958Sdme@sun.com 		 * people to come looking again. We hold txlock, so we
45710958Sdme@sun.com 		 * are able to take our slots before anyone else runs.
45810958Sdme@sun.com 		 */
45910958Sdme@sun.com 		if (slotsfree > wanted)
46010958Sdme@sun.com 			cv_broadcast(&xnfp->xnf_cv_tx_slots);
46110958Sdme@sun.com 
46210958Sdme@sun.com 		if (slotsfree >= wanted)
46310958Sdme@sun.com 			break;
46410958Sdme@sun.com 
46510958Sdme@sun.com 		if (!wait)
46610958Sdme@sun.com 			break;
46710958Sdme@sun.com 
46810958Sdme@sun.com 		cv_wait(&xnfp->xnf_cv_tx_slots, &xnfp->xnf_txlock);
46910958Sdme@sun.com 	}
47010958Sdme@sun.com 
47110958Sdme@sun.com 	ASSERT(slotsfree <= RING_SIZE(&(xnfp->xnf_tx_ring)));
47210958Sdme@sun.com 
47310958Sdme@sun.com 	return (slotsfree);
47410958Sdme@sun.com }
47510958Sdme@sun.com 
4765084Sjohnlev static int
xnf_setup_rings(xnf_t * xnfp)4775084Sjohnlev xnf_setup_rings(xnf_t *xnfp)
4785084Sjohnlev {
47910958Sdme@sun.com 	domid_t			oeid;
48010958Sdme@sun.com 	struct xenbus_device	*xsd;
4815084Sjohnlev 	RING_IDX		i;
48210958Sdme@sun.com 	int			err;
48310958Sdme@sun.com 	xnf_txid_t		*tidp;
48410958Sdme@sun.com 	xnf_buf_t **bdescp;
4855084Sjohnlev 
4865741Smrj 	oeid = xvdi_get_oeid(xnfp->xnf_devinfo);
4875741Smrj 	xsd = xvdi_get_xsd(xnfp->xnf_devinfo);
4885084Sjohnlev 
48910958Sdme@sun.com 	if (xnfp->xnf_tx_ring_ref != INVALID_GRANT_REF)
4905741Smrj 		gnttab_end_foreign_access(xnfp->xnf_tx_ring_ref, 0, 0);
4915084Sjohnlev 
4925084Sjohnlev 	err = gnttab_grant_foreign_access(oeid,
4935741Smrj 	    xnf_btop(pa_to_ma(xnfp->xnf_tx_ring_phys_addr)), 0);
4945084Sjohnlev 	if (err <= 0) {
4955084Sjohnlev 		err = -err;
4965084Sjohnlev 		xenbus_dev_error(xsd, err, "granting access to tx ring page");
4975084Sjohnlev 		goto out;
4985084Sjohnlev 	}
4995741Smrj 	xnfp->xnf_tx_ring_ref = (grant_ref_t)err;
5005084Sjohnlev 
50110958Sdme@sun.com 	if (xnfp->xnf_rx_ring_ref != INVALID_GRANT_REF)
5025741Smrj 		gnttab_end_foreign_access(xnfp->xnf_rx_ring_ref, 0, 0);
5035084Sjohnlev 
5045084Sjohnlev 	err = gnttab_grant_foreign_access(oeid,
5055741Smrj 	    xnf_btop(pa_to_ma(xnfp->xnf_rx_ring_phys_addr)), 0);
5065084Sjohnlev 	if (err <= 0) {
5075084Sjohnlev 		err = -err;
5085084Sjohnlev 		xenbus_dev_error(xsd, err, "granting access to rx ring page");
5095084Sjohnlev 		goto out;
5105084Sjohnlev 	}
5115741Smrj 	xnfp->xnf_rx_ring_ref = (grant_ref_t)err;
5125084Sjohnlev 
51310958Sdme@sun.com 	mutex_enter(&xnfp->xnf_txlock);
5145084Sjohnlev 
5155084Sjohnlev 	/*
51610958Sdme@sun.com 	 * Setup/cleanup the TX ring.  Note that this can lose packets
51710958Sdme@sun.com 	 * after a resume, but we expect to stagger on.
5185084Sjohnlev 	 */
51910958Sdme@sun.com 	xnfp->xnf_tx_pkt_id_head = INVALID_TX_ID; /* I.e. emtpy list. */
52010958Sdme@sun.com 	for (i = 0, tidp = &xnfp->xnf_tx_pkt_id[0];
52110958Sdme@sun.com 	    i < NET_TX_RING_SIZE;
52210958Sdme@sun.com 	    i++, tidp++) {
52310958Sdme@sun.com 		xnf_txbuf_t *txp;
52410958Sdme@sun.com 
52510958Sdme@sun.com 		tidp->id = i;
52610958Sdme@sun.com 
52710958Sdme@sun.com 		txp = tidp->txbuf;
52810958Sdme@sun.com 		if (txp == NULL) {
52910958Sdme@sun.com 			tidp->next = INVALID_TX_ID; /* Appease txid_put(). */
53010958Sdme@sun.com 			txid_put(xnfp, tidp);
5315084Sjohnlev 			continue;
5325084Sjohnlev 		}
5335084Sjohnlev 
53410958Sdme@sun.com 		ASSERT(txp->tx_txreq.gref != INVALID_GRANT_REF);
53510958Sdme@sun.com 		ASSERT(txp->tx_mp != NULL);
53610958Sdme@sun.com 
53710958Sdme@sun.com 		switch (txp->tx_type) {
53810958Sdme@sun.com 		case TX_DATA:
53910958Sdme@sun.com 			VERIFY(gnttab_query_foreign_access(txp->tx_txreq.gref)
54010958Sdme@sun.com 			    == 0);
54110958Sdme@sun.com 
54210958Sdme@sun.com 			if (txp->tx_bdesc == NULL) {
54310958Sdme@sun.com 				(void) gnttab_end_foreign_access_ref(
54410958Sdme@sun.com 				    txp->tx_txreq.gref, 1);
54510958Sdme@sun.com 				gref_put(xnfp, txp->tx_txreq.gref);
54610958Sdme@sun.com 				(void) ddi_dma_unbind_handle(
54710958Sdme@sun.com 				    txp->tx_dma_handle);
54810958Sdme@sun.com 			} else {
54910958Sdme@sun.com 				xnf_buf_put(xnfp, txp->tx_bdesc, B_TRUE);
55010958Sdme@sun.com 			}
55110958Sdme@sun.com 
55210958Sdme@sun.com 			freemsg(txp->tx_mp);
55310958Sdme@sun.com 			txid_put(xnfp, tidp);
55410958Sdme@sun.com 			kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
55510958Sdme@sun.com 
55610958Sdme@sun.com 			break;
55710958Sdme@sun.com 
55810958Sdme@sun.com 		case TX_MCAST_REQ:
55910958Sdme@sun.com 			txp->tx_type = TX_MCAST_RSP;
56010958Sdme@sun.com 			txp->tx_status = NETIF_RSP_DROPPED;
56110958Sdme@sun.com 			cv_broadcast(&xnfp->xnf_cv_multicast);
56210958Sdme@sun.com 
56310958Sdme@sun.com 			/*
56410958Sdme@sun.com 			 * The request consumed two slots in the ring,
56510958Sdme@sun.com 			 * yet only a single xnf_txid_t is used. Step
56610958Sdme@sun.com 			 * over the empty slot.
56710958Sdme@sun.com 			 */
56810958Sdme@sun.com 			i++;
56910958Sdme@sun.com 			ASSERT(i < NET_TX_RING_SIZE);
57010958Sdme@sun.com 
57110958Sdme@sun.com 			break;
57210958Sdme@sun.com 
57310958Sdme@sun.com 		case TX_MCAST_RSP:
57410958Sdme@sun.com 			break;
5755084Sjohnlev 		}
5765084Sjohnlev 	}
5775084Sjohnlev 
5787351Sdme@sun.com 	/* LINTED: constant in conditional context */
5797351Sdme@sun.com 	SHARED_RING_INIT(xnfp->xnf_tx_ring.sring);
58010958Sdme@sun.com 	/* LINTED: constant in conditional context */
58110958Sdme@sun.com 	FRONT_RING_INIT(&xnfp->xnf_tx_ring,
58210958Sdme@sun.com 	    xnfp->xnf_tx_ring.sring, PAGESIZE);
5835084Sjohnlev 
5845741Smrj 	mutex_exit(&xnfp->xnf_txlock);
5855084Sjohnlev 
58610958Sdme@sun.com 	mutex_enter(&xnfp->xnf_rxlock);
58710958Sdme@sun.com 
5885084Sjohnlev 	/*
58910958Sdme@sun.com 	 * Clean out any buffers currently posted to the receive ring
59010958Sdme@sun.com 	 * before we reset it.
5915084Sjohnlev 	 */
59210958Sdme@sun.com 	for (i = 0, bdescp = &xnfp->xnf_rx_pkt_info[0];
59310958Sdme@sun.com 	    i < NET_RX_RING_SIZE;
59410958Sdme@sun.com 	    i++, bdescp++) {
59510958Sdme@sun.com 		if (*bdescp != NULL) {
59610958Sdme@sun.com 			xnf_buf_put(xnfp, *bdescp, B_FALSE);
59710958Sdme@sun.com 			*bdescp = NULL;
5985084Sjohnlev 		}
5995084Sjohnlev 	}
6005741Smrj 
6017351Sdme@sun.com 	/* LINTED: constant in conditional context */
6027351Sdme@sun.com 	SHARED_RING_INIT(xnfp->xnf_rx_ring.sring);
60310958Sdme@sun.com 	/* LINTED: constant in conditional context */
60410958Sdme@sun.com 	FRONT_RING_INIT(&xnfp->xnf_rx_ring,
60510958Sdme@sun.com 	    xnfp->xnf_rx_ring.sring, PAGESIZE);
60610958Sdme@sun.com 
60710958Sdme@sun.com 	/*
60810958Sdme@sun.com 	 * Fill the ring with buffers.
60910958Sdme@sun.com 	 */
6105084Sjohnlev 	for (i = 0; i < NET_RX_RING_SIZE; i++) {
61110958Sdme@sun.com 		xnf_buf_t *bdesc;
61210958Sdme@sun.com 
61310958Sdme@sun.com 		bdesc = xnf_buf_get(xnfp, KM_SLEEP, B_FALSE);
61410958Sdme@sun.com 		VERIFY(bdesc != NULL);
61510958Sdme@sun.com 		xnf_rxbuf_hang(xnfp, bdesc);
6165084Sjohnlev 	}
61710958Sdme@sun.com 
6185084Sjohnlev 	/* LINTED: constant in conditional context */
6195741Smrj 	RING_PUSH_REQUESTS(&xnfp->xnf_rx_ring);
6205084Sjohnlev 
62110958Sdme@sun.com 	mutex_exit(&xnfp->xnf_rxlock);
6225084Sjohnlev 
6235084Sjohnlev 	return (0);
6245084Sjohnlev 
6255084Sjohnlev out:
62610958Sdme@sun.com 	if (xnfp->xnf_tx_ring_ref != INVALID_GRANT_REF)
6275741Smrj 		gnttab_end_foreign_access(xnfp->xnf_tx_ring_ref, 0, 0);
62810958Sdme@sun.com 	xnfp->xnf_tx_ring_ref = INVALID_GRANT_REF;
62910958Sdme@sun.com 
63010958Sdme@sun.com 	if (xnfp->xnf_rx_ring_ref != INVALID_GRANT_REF)
6315741Smrj 		gnttab_end_foreign_access(xnfp->xnf_rx_ring_ref, 0, 0);
63210958Sdme@sun.com 	xnfp->xnf_rx_ring_ref = INVALID_GRANT_REF;
6335084Sjohnlev 
6345084Sjohnlev 	return (err);
6355084Sjohnlev }
6365084Sjohnlev 
6375084Sjohnlev /*
6385084Sjohnlev  * Connect driver to back end, called to set up communication with
6395084Sjohnlev  * back end driver both initially and on resume after restore/migrate.
6405084Sjohnlev  */
6415084Sjohnlev void
xnf_be_connect(xnf_t * xnfp)6425084Sjohnlev xnf_be_connect(xnf_t *xnfp)
6435084Sjohnlev {
6445084Sjohnlev 	const char	*message;
6455084Sjohnlev 	xenbus_transaction_t xbt;
6465741Smrj 	struct		xenbus_device *xsd;
6475084Sjohnlev 	char		*xsname;
6486899Scz147101 	int		err;
6495084Sjohnlev 
6505741Smrj 	ASSERT(!xnfp->xnf_connected);
6515084Sjohnlev 
6525741Smrj 	xsd = xvdi_get_xsd(xnfp->xnf_devinfo);
6535741Smrj 	xsname = xvdi_get_xsname(xnfp->xnf_devinfo);
6545084Sjohnlev 
6555084Sjohnlev 	err = xnf_setup_rings(xnfp);
6565084Sjohnlev 	if (err != 0) {
6575084Sjohnlev 		cmn_err(CE_WARN, "failed to set up tx/rx rings");
6585084Sjohnlev 		xenbus_dev_error(xsd, err, "setting up ring");
6595084Sjohnlev 		return;
6605084Sjohnlev 	}
6615084Sjohnlev 
6625084Sjohnlev again:
6635084Sjohnlev 	err = xenbus_transaction_start(&xbt);
6645084Sjohnlev 	if (err != 0) {
6655084Sjohnlev 		xenbus_dev_error(xsd, EIO, "starting transaction");
6665084Sjohnlev 		return;
6675084Sjohnlev 	}
6685084Sjohnlev 
6695084Sjohnlev 	err = xenbus_printf(xbt, xsname, "tx-ring-ref", "%u",
6705741Smrj 	    xnfp->xnf_tx_ring_ref);
6715084Sjohnlev 	if (err != 0) {
6725084Sjohnlev 		message = "writing tx ring-ref";
6735084Sjohnlev 		goto abort_transaction;
6745084Sjohnlev 	}
6755084Sjohnlev 
6765084Sjohnlev 	err = xenbus_printf(xbt, xsname, "rx-ring-ref", "%u",
6775741Smrj 	    xnfp->xnf_rx_ring_ref);
6785084Sjohnlev 	if (err != 0) {
6795084Sjohnlev 		message = "writing rx ring-ref";
6805084Sjohnlev 		goto abort_transaction;
6815084Sjohnlev 	}
6825084Sjohnlev 
6835741Smrj 	err = xenbus_printf(xbt, xsname, "event-channel", "%u",
6845741Smrj 	    xnfp->xnf_evtchn);
6855084Sjohnlev 	if (err != 0) {
6865084Sjohnlev 		message = "writing event-channel";
6875084Sjohnlev 		goto abort_transaction;
6885084Sjohnlev 	}
6895084Sjohnlev 
6905084Sjohnlev 	err = xenbus_printf(xbt, xsname, "feature-rx-notify", "%d", 1);
6915084Sjohnlev 	if (err != 0) {
6925084Sjohnlev 		message = "writing feature-rx-notify";
6935084Sjohnlev 		goto abort_transaction;
6945084Sjohnlev 	}
6955084Sjohnlev 
69610958Sdme@sun.com 	err = xenbus_printf(xbt, xsname, "request-rx-copy", "%d", 1);
6975741Smrj 	if (err != 0) {
6985741Smrj 		message = "writing request-rx-copy";
6995741Smrj 		goto abort_transaction;
7005741Smrj 	}
7015084Sjohnlev 
70210958Sdme@sun.com 	if (xnfp->xnf_be_mcast_control) {
70310958Sdme@sun.com 		err = xenbus_printf(xbt, xsname, "request-multicast-control",
70410958Sdme@sun.com 		    "%d", 1);
70510958Sdme@sun.com 		if (err != 0) {
70610958Sdme@sun.com 			message = "writing request-multicast-control";
70710958Sdme@sun.com 			goto abort_transaction;
70810958Sdme@sun.com 		}
70910958Sdme@sun.com 	}
71010958Sdme@sun.com 
71110958Sdme@sun.com 	err = xvdi_switch_state(xnfp->xnf_devinfo, xbt, XenbusStateConnected);
7125084Sjohnlev 	if (err != 0) {
71310958Sdme@sun.com 		message = "switching state to XenbusStateConnected";
7145084Sjohnlev 		goto abort_transaction;
7155084Sjohnlev 	}
7165084Sjohnlev 
7175084Sjohnlev 	err = xenbus_transaction_end(xbt, 0);
7185084Sjohnlev 	if (err != 0) {
7195084Sjohnlev 		if (err == EAGAIN)
7205084Sjohnlev 			goto again;
7215084Sjohnlev 		xenbus_dev_error(xsd, err, "completing transaction");
7225084Sjohnlev 	}
7235084Sjohnlev 
7245084Sjohnlev 	return;
7255084Sjohnlev 
7265084Sjohnlev abort_transaction:
7275084Sjohnlev 	(void) xenbus_transaction_end(xbt, 1);
7285084Sjohnlev 	xenbus_dev_error(xsd, err, "%s", message);
7295084Sjohnlev }
7305084Sjohnlev 
7315084Sjohnlev /*
73210958Sdme@sun.com  * Read configuration information from xenstore.
7336899Scz147101  */
7346899Scz147101 void
xnf_read_config(xnf_t * xnfp)7356899Scz147101 xnf_read_config(xnf_t *xnfp)
7366899Scz147101 {
73710958Sdme@sun.com 	int err, be_cap;
73810958Sdme@sun.com 	char mac[ETHERADDRL * 3];
73910958Sdme@sun.com 	char *oename = xvdi_get_oename(xnfp->xnf_devinfo);
74010958Sdme@sun.com 
74110958Sdme@sun.com 	err = xenbus_scanf(XBT_NULL, oename, "mac",
7426899Scz147101 	    "%s", (char *)&mac[0]);
7436899Scz147101 	if (err != 0) {
7446899Scz147101 		/*
7456899Scz147101 		 * bad: we're supposed to be set up with a proper mac
7466899Scz147101 		 * addr. at this point
7476899Scz147101 		 */
7486899Scz147101 		cmn_err(CE_WARN, "%s%d: no mac address",
7496899Scz147101 		    ddi_driver_name(xnfp->xnf_devinfo),
7506899Scz147101 		    ddi_get_instance(xnfp->xnf_devinfo));
7516899Scz147101 			return;
7526899Scz147101 	}
7536899Scz147101 	if (ether_aton(mac, xnfp->xnf_mac_addr) != ETHERADDRL) {
7546899Scz147101 		err = ENOENT;
7556899Scz147101 		xenbus_dev_error(xvdi_get_xsd(xnfp->xnf_devinfo), ENOENT,
7566899Scz147101 		    "parsing %s/mac", xvdi_get_xsname(xnfp->xnf_devinfo));
7576899Scz147101 		return;
7586899Scz147101 	}
7596899Scz147101 
76010958Sdme@sun.com 	err = xenbus_scanf(XBT_NULL, oename,
76110958Sdme@sun.com 	    "feature-rx-copy", "%d", &be_cap);
7626899Scz147101 	/*
7636899Scz147101 	 * If we fail to read the store we assume that the key is
7646899Scz147101 	 * absent, implying an older domain at the far end.  Older
76510958Sdme@sun.com 	 * domains cannot do HV copy.
7666899Scz147101 	 */
7676899Scz147101 	if (err != 0)
76810958Sdme@sun.com 		be_cap = 0;
76910958Sdme@sun.com 	xnfp->xnf_be_rx_copy = (be_cap != 0);
77010958Sdme@sun.com 
77110958Sdme@sun.com 	err = xenbus_scanf(XBT_NULL, oename,
77210958Sdme@sun.com 	    "feature-multicast-control", "%d", &be_cap);
7736899Scz147101 	/*
77410958Sdme@sun.com 	 * If we fail to read the store we assume that the key is
77510958Sdme@sun.com 	 * absent, implying an older domain at the far end.  Older
77610958Sdme@sun.com 	 * domains do not support multicast control.
7776899Scz147101 	 */
77810958Sdme@sun.com 	if (err != 0)
77910958Sdme@sun.com 		be_cap = 0;
78010958Sdme@sun.com 	xnfp->xnf_be_mcast_control = (be_cap != 0) && xnf_multicast_control;
7816899Scz147101 }
7826899Scz147101 
7836899Scz147101 /*
7845084Sjohnlev  *  attach(9E) -- Attach a device to the system
7855084Sjohnlev  */
7865084Sjohnlev static int
xnf_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)7875084Sjohnlev xnf_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
7885084Sjohnlev {
7895084Sjohnlev 	mac_register_t *macp;
7905084Sjohnlev 	xnf_t *xnfp;
7915084Sjohnlev 	int err;
79210958Sdme@sun.com 	char cachename[32];
7935084Sjohnlev 
7945084Sjohnlev #ifdef XNF_DEBUG
79510958Sdme@sun.com 	if (xnf_debug & XNF_DEBUG_DDI)
7965084Sjohnlev 		printf("xnf%d: attach(0x%p)\n", ddi_get_instance(devinfo),
7975084Sjohnlev 		    (void *)devinfo);
7985084Sjohnlev #endif
7995084Sjohnlev 
8005084Sjohnlev 	switch (cmd) {
8015084Sjohnlev 	case DDI_RESUME:
8025084Sjohnlev 		xnfp = ddi_get_driver_private(devinfo);
80310958Sdme@sun.com 		xnfp->xnf_gen++;
8045084Sjohnlev 
8055084Sjohnlev 		(void) xvdi_resume(devinfo);
8065084Sjohnlev 		(void) xvdi_alloc_evtchn(devinfo);
8075741Smrj 		xnfp->xnf_evtchn = xvdi_get_evtchn(devinfo);
8085741Smrj #ifdef XPV_HVM_DRIVER
8095741Smrj 		ec_bind_evtchn_to_handler(xnfp->xnf_evtchn, IPL_VIF, xnf_intr,
8105741Smrj 		    xnfp);
8115741Smrj #else
8125084Sjohnlev 		(void) ddi_add_intr(devinfo, 0, NULL, NULL, xnf_intr,
8135084Sjohnlev 		    (caddr_t)xnfp);
8145741Smrj #endif
8155084Sjohnlev 		return (DDI_SUCCESS);
8165084Sjohnlev 
8175084Sjohnlev 	case DDI_ATTACH:
8185084Sjohnlev 		break;
8195084Sjohnlev 
8205084Sjohnlev 	default:
8215084Sjohnlev 		return (DDI_FAILURE);
8225084Sjohnlev 	}
8235084Sjohnlev 
8245084Sjohnlev 	/*
8255084Sjohnlev 	 *  Allocate gld_mac_info_t and xnf_instance structures
8265084Sjohnlev 	 */
8275084Sjohnlev 	macp = mac_alloc(MAC_VERSION);
8285084Sjohnlev 	if (macp == NULL)
8295084Sjohnlev 		return (DDI_FAILURE);
8305084Sjohnlev 	xnfp = kmem_zalloc(sizeof (*xnfp), KM_SLEEP);
8315084Sjohnlev 
8325084Sjohnlev 	macp->m_dip = devinfo;
8335084Sjohnlev 	macp->m_driver = xnfp;
8345741Smrj 	xnfp->xnf_devinfo = devinfo;
8355084Sjohnlev 
8365084Sjohnlev 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
8375741Smrj 	macp->m_src_addr = xnfp->xnf_mac_addr;
8385084Sjohnlev 	macp->m_callbacks = &xnf_callbacks;
8395084Sjohnlev 	macp->m_min_sdu = 0;
8405084Sjohnlev 	macp->m_max_sdu = XNF_MAXPKT;
8415084Sjohnlev 
8425741Smrj 	xnfp->xnf_running = B_FALSE;
8435741Smrj 	xnfp->xnf_connected = B_FALSE;
84410958Sdme@sun.com 	xnfp->xnf_be_rx_copy = B_FALSE;
84510958Sdme@sun.com 	xnfp->xnf_be_mcast_control = B_FALSE;
8467521Sdme@sun.com 	xnfp->xnf_need_sched = B_FALSE;
8475741Smrj 
84810958Sdme@sun.com 	xnfp->xnf_rx_head = NULL;
84910958Sdme@sun.com 	xnfp->xnf_rx_tail = NULL;
85010958Sdme@sun.com 	xnfp->xnf_rx_new_buffers_posted = B_FALSE;
85110958Sdme@sun.com 
8525741Smrj #ifdef XPV_HVM_DRIVER
8536450Srab 	/*
8546450Srab 	 * Report our version to dom0.
8556450Srab 	 */
85610175SStuart.Maybee@Sun.COM 	if (xenbus_printf(XBT_NULL, "guest/xnf", "version", "%d",
8576450Srab 	    HVMPV_XNF_VERS))
8586450Srab 		cmn_err(CE_WARN, "xnf: couldn't write version\n");
8595741Smrj #endif
8605084Sjohnlev 
8615084Sjohnlev 	/*
8625084Sjohnlev 	 * Get the iblock cookie with which to initialize the mutexes.
8635084Sjohnlev 	 */
8645741Smrj 	if (ddi_get_iblock_cookie(devinfo, 0, &xnfp->xnf_icookie)
8655084Sjohnlev 	    != DDI_SUCCESS)
8665084Sjohnlev 		goto failure;
86710958Sdme@sun.com 
8685741Smrj 	mutex_init(&xnfp->xnf_txlock,
8695741Smrj 	    NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87010958Sdme@sun.com 	mutex_init(&xnfp->xnf_rxlock,
87110958Sdme@sun.com 	    NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87210958Sdme@sun.com 	mutex_init(&xnfp->xnf_schedlock,
87310958Sdme@sun.com 	    NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87410958Sdme@sun.com 	mutex_init(&xnfp->xnf_gref_lock,
8755741Smrj 	    NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87610958Sdme@sun.com 
87710958Sdme@sun.com 	cv_init(&xnfp->xnf_cv_state, NULL, CV_DEFAULT, NULL);
87810958Sdme@sun.com 	cv_init(&xnfp->xnf_cv_multicast, NULL, CV_DEFAULT, NULL);
87910958Sdme@sun.com 	cv_init(&xnfp->xnf_cv_tx_slots, NULL, CV_DEFAULT, NULL);
88010958Sdme@sun.com 
88110958Sdme@sun.com 	(void) sprintf(cachename, "xnf_buf_cache_%d",
88210958Sdme@sun.com 	    ddi_get_instance(devinfo));
88310958Sdme@sun.com 	xnfp->xnf_buf_cache = kmem_cache_create(cachename,
88410958Sdme@sun.com 	    sizeof (xnf_buf_t), 0,
88510958Sdme@sun.com 	    xnf_buf_constructor, xnf_buf_destructor,
88610958Sdme@sun.com 	    NULL, xnfp, NULL, 0);
88710958Sdme@sun.com 	if (xnfp->xnf_buf_cache == NULL)
88810958Sdme@sun.com 		goto failure_0;
88910958Sdme@sun.com 
89010958Sdme@sun.com 	(void) sprintf(cachename, "xnf_tx_buf_cache_%d",
89110958Sdme@sun.com 	    ddi_get_instance(devinfo));
89210958Sdme@sun.com 	xnfp->xnf_tx_buf_cache = kmem_cache_create(cachename,
89310958Sdme@sun.com 	    sizeof (xnf_txbuf_t), 0,
89410958Sdme@sun.com 	    xnf_tx_buf_constructor, xnf_tx_buf_destructor,
89510958Sdme@sun.com 	    NULL, xnfp, NULL, 0);
89610958Sdme@sun.com 	if (xnfp->xnf_tx_buf_cache == NULL)
8975741Smrj 		goto failure_1;
89810958Sdme@sun.com 
89910958Sdme@sun.com 	xnfp->xnf_gref_head = INVALID_GRANT_REF;
90010958Sdme@sun.com 
9015084Sjohnlev 	if (xnf_alloc_dma_resources(xnfp) == DDI_FAILURE) {
9025084Sjohnlev 		cmn_err(CE_WARN, "xnf%d: failed to allocate and initialize "
9035741Smrj 		    "driver data structures",
9045741Smrj 		    ddi_get_instance(xnfp->xnf_devinfo));
90510958Sdme@sun.com 		goto failure_2;
9065084Sjohnlev 	}
9075084Sjohnlev 
9085741Smrj 	xnfp->xnf_rx_ring.sring->rsp_event =
9095741Smrj 	    xnfp->xnf_tx_ring.sring->rsp_event = 1;
9105084Sjohnlev 
91110958Sdme@sun.com 	xnfp->xnf_tx_ring_ref = INVALID_GRANT_REF;
91210958Sdme@sun.com 	xnfp->xnf_rx_ring_ref = INVALID_GRANT_REF;
9135084Sjohnlev 
9145084Sjohnlev 	/* set driver private pointer now */
9155084Sjohnlev 	ddi_set_driver_private(devinfo, xnfp);
9165084Sjohnlev 
9175084Sjohnlev 	if (!xnf_kstat_init(xnfp))
91810958Sdme@sun.com 		goto failure_3;
9195084Sjohnlev 
9205084Sjohnlev 	/*
9215084Sjohnlev 	 * Allocate an event channel, add the interrupt handler and
9225084Sjohnlev 	 * bind it to the event channel.
9235084Sjohnlev 	 */
9245084Sjohnlev 	(void) xvdi_alloc_evtchn(devinfo);
9255741Smrj 	xnfp->xnf_evtchn = xvdi_get_evtchn(devinfo);
9265741Smrj #ifdef XPV_HVM_DRIVER
9275741Smrj 	ec_bind_evtchn_to_handler(xnfp->xnf_evtchn, IPL_VIF, xnf_intr, xnfp);
9285741Smrj #else
9295084Sjohnlev 	(void) ddi_add_intr(devinfo, 0, NULL, NULL, xnf_intr, (caddr_t)xnfp);
9305741Smrj #endif
9315084Sjohnlev 
9325741Smrj 	err = mac_register(macp, &xnfp->xnf_mh);
9335084Sjohnlev 	mac_free(macp);
9345084Sjohnlev 	macp = NULL;
9355084Sjohnlev 	if (err != 0)
93610958Sdme@sun.com 		goto failure_4;
93710958Sdme@sun.com 
93810958Sdme@sun.com 	if (xvdi_add_event_handler(devinfo, XS_OE_STATE, oe_state_change, NULL)
93910958Sdme@sun.com 	    != DDI_SUCCESS)
94010958Sdme@sun.com 		goto failure_5;
9415084Sjohnlev 
9426873Sfvdl #ifdef XPV_HVM_DRIVER
9436873Sfvdl 	/*
9446873Sfvdl 	 * In the HVM case, this driver essentially replaces a driver for
9456873Sfvdl 	 * a 'real' PCI NIC. Without the "model" property set to
9466873Sfvdl 	 * "Ethernet controller", like the PCI code does, netbooting does
9476873Sfvdl 	 * not work correctly, as strplumb_get_netdev_path() will not find
9486873Sfvdl 	 * this interface.
9496873Sfvdl 	 */
9506873Sfvdl 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, devinfo, "model",
9516873Sfvdl 	    "Ethernet controller");
9526873Sfvdl #endif
9536873Sfvdl 
95410958Sdme@sun.com #ifdef XNF_DEBUG
95510958Sdme@sun.com 	if (xnf_debug_instance == NULL)
95610958Sdme@sun.com 		xnf_debug_instance = xnfp;
95710958Sdme@sun.com #endif
9586899Scz147101 
9595084Sjohnlev 	return (DDI_SUCCESS);
9605084Sjohnlev 
96110958Sdme@sun.com failure_5:
96210981Sdme@sun.com 	(void) mac_unregister(xnfp->xnf_mh);
96310958Sdme@sun.com 
96410958Sdme@sun.com failure_4:
9655741Smrj #ifdef XPV_HVM_DRIVER
9665741Smrj 	ec_unbind_evtchn(xnfp->xnf_evtchn);
9676431Ssmaybe 	xvdi_free_evtchn(devinfo);
9685741Smrj #else
9695741Smrj 	ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
9705741Smrj #endif
9715741Smrj 	xnfp->xnf_evtchn = INVALID_EVTCHN;
97210958Sdme@sun.com 	kstat_delete(xnfp->xnf_kstat_aux);
97310958Sdme@sun.com 
97410958Sdme@sun.com failure_3:
97510958Sdme@sun.com 	xnf_release_dma_resources(xnfp);
9765084Sjohnlev 
9776899Scz147101 failure_2:
97810958Sdme@sun.com 	kmem_cache_destroy(xnfp->xnf_tx_buf_cache);
9796899Scz147101 
9805741Smrj failure_1:
98110958Sdme@sun.com 	kmem_cache_destroy(xnfp->xnf_buf_cache);
98210958Sdme@sun.com 
98310958Sdme@sun.com failure_0:
98410958Sdme@sun.com 	cv_destroy(&xnfp->xnf_cv_tx_slots);
98510958Sdme@sun.com 	cv_destroy(&xnfp->xnf_cv_multicast);
98610958Sdme@sun.com 	cv_destroy(&xnfp->xnf_cv_state);
98710958Sdme@sun.com 
98810958Sdme@sun.com 	mutex_destroy(&xnfp->xnf_gref_lock);
98910958Sdme@sun.com 	mutex_destroy(&xnfp->xnf_schedlock);
99010958Sdme@sun.com 	mutex_destroy(&xnfp->xnf_rxlock);
9915741Smrj 	mutex_destroy(&xnfp->xnf_txlock);
9925084Sjohnlev 
9935084Sjohnlev failure:
9945084Sjohnlev 	kmem_free(xnfp, sizeof (*xnfp));
9955084Sjohnlev 	if (macp != NULL)
9965084Sjohnlev 		mac_free(macp);
9975084Sjohnlev 
9985084Sjohnlev 	return (DDI_FAILURE);
9995084Sjohnlev }
10005084Sjohnlev 
10015084Sjohnlev /*  detach(9E) -- Detach a device from the system */
10025084Sjohnlev static int
xnf_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)10035084Sjohnlev xnf_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
10045084Sjohnlev {
10055084Sjohnlev 	xnf_t *xnfp;		/* Our private device info */
10065084Sjohnlev 
10075084Sjohnlev #ifdef XNF_DEBUG
100810958Sdme@sun.com 	if (xnf_debug & XNF_DEBUG_DDI)
10095084Sjohnlev 		printf("xnf_detach(0x%p)\n", (void *)devinfo);
10105084Sjohnlev #endif
10115084Sjohnlev 
10125084Sjohnlev 	xnfp = ddi_get_driver_private(devinfo);
10135084Sjohnlev 
10145084Sjohnlev 	switch (cmd) {
10155084Sjohnlev 	case DDI_SUSPEND:
10165741Smrj #ifdef XPV_HVM_DRIVER
10175741Smrj 		ec_unbind_evtchn(xnfp->xnf_evtchn);
10186431Ssmaybe 		xvdi_free_evtchn(devinfo);
10195741Smrj #else
10205741Smrj 		ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
10215741Smrj #endif
10225084Sjohnlev 
10235084Sjohnlev 		xvdi_suspend(devinfo);
10245084Sjohnlev 
102510958Sdme@sun.com 		mutex_enter(&xnfp->xnf_rxlock);
10265741Smrj 		mutex_enter(&xnfp->xnf_txlock);
10275084Sjohnlev 
10285741Smrj 		xnfp->xnf_evtchn = INVALID_EVTCHN;
10295741Smrj 		xnfp->xnf_connected = B_FALSE;
10305741Smrj 		mutex_exit(&xnfp->xnf_txlock);
103110958Sdme@sun.com 		mutex_exit(&xnfp->xnf_rxlock);
10327397SMax.Zhen@Sun.COM 
10337397SMax.Zhen@Sun.COM 		/* claim link to be down after disconnect */
10347397SMax.Zhen@Sun.COM 		mac_link_update(xnfp->xnf_mh, LINK_STATE_DOWN);
10355084Sjohnlev 		return (DDI_SUCCESS);
10365084Sjohnlev 
10375084Sjohnlev 	case DDI_DETACH:
10385084Sjohnlev 		break;
10395084Sjohnlev 
10405084Sjohnlev 	default:
10415084Sjohnlev 		return (DDI_FAILURE);
10425084Sjohnlev 	}
10435084Sjohnlev 
10445741Smrj 	if (xnfp->xnf_connected)
10455084Sjohnlev 		return (DDI_FAILURE);
10465084Sjohnlev 
104710958Sdme@sun.com 	/*
104810958Sdme@sun.com 	 * Cannot detach if we have xnf_buf_t outstanding.
104910958Sdme@sun.com 	 */
105010958Sdme@sun.com 	if (xnfp->xnf_stat_buf_allocated > 0)
105110958Sdme@sun.com 		return (DDI_FAILURE);
10525084Sjohnlev 
10535741Smrj 	if (mac_unregister(xnfp->xnf_mh) != 0)
10545084Sjohnlev 		return (DDI_FAILURE);
10555084Sjohnlev 
10566899Scz147101 	kstat_delete(xnfp->xnf_kstat_aux);
10576899Scz147101 
10585084Sjohnlev 	/* Stop the receiver */
10595084Sjohnlev 	xnf_stop(xnfp);
10605084Sjohnlev 
10615084Sjohnlev 	xvdi_remove_event_handler(devinfo, XS_OE_STATE);
10625084Sjohnlev 
10635084Sjohnlev 	/* Remove the interrupt */
10645741Smrj #ifdef XPV_HVM_DRIVER
10655741Smrj 	ec_unbind_evtchn(xnfp->xnf_evtchn);
10666431Ssmaybe 	xvdi_free_evtchn(devinfo);
10675741Smrj #else
10685741Smrj 	ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
10695741Smrj #endif
10705084Sjohnlev 
10715084Sjohnlev 	/* Release any pending xmit mblks */
10725084Sjohnlev 	xnf_release_mblks(xnfp);
10735084Sjohnlev 
10745084Sjohnlev 	/* Release all DMA resources */
10755084Sjohnlev 	xnf_release_dma_resources(xnfp);
10765084Sjohnlev 
107710958Sdme@sun.com 	cv_destroy(&xnfp->xnf_cv_tx_slots);
107810958Sdme@sun.com 	cv_destroy(&xnfp->xnf_cv_multicast);
107910958Sdme@sun.com 	cv_destroy(&xnfp->xnf_cv_state);
108010958Sdme@sun.com 
108110958Sdme@sun.com 	kmem_cache_destroy(xnfp->xnf_tx_buf_cache);
108210958Sdme@sun.com 	kmem_cache_destroy(xnfp->xnf_buf_cache);
108310958Sdme@sun.com 
108410958Sdme@sun.com 	mutex_destroy(&xnfp->xnf_gref_lock);
108510958Sdme@sun.com 	mutex_destroy(&xnfp->xnf_schedlock);
108610958Sdme@sun.com 	mutex_destroy(&xnfp->xnf_rxlock);
10875741Smrj 	mutex_destroy(&xnfp->xnf_txlock);
10885084Sjohnlev 
10895084Sjohnlev 	kmem_free(xnfp, sizeof (*xnfp));
10905084Sjohnlev 
10915084Sjohnlev 	return (DDI_SUCCESS);
10925084Sjohnlev }
10935084Sjohnlev 
10945084Sjohnlev /*
10955084Sjohnlev  *  xnf_set_mac_addr() -- set the physical network address on the board.
10965084Sjohnlev  */
10975084Sjohnlev static int
xnf_set_mac_addr(void * arg,const uint8_t * macaddr)10985084Sjohnlev xnf_set_mac_addr(void *arg, const uint8_t *macaddr)
10995084Sjohnlev {
110010958Sdme@sun.com 	_NOTE(ARGUNUSED(arg, macaddr));
110110958Sdme@sun.com 
11025084Sjohnlev 	/*
11035084Sjohnlev 	 * We can't set our macaddr.
11045084Sjohnlev 	 */
11055084Sjohnlev 	return (ENOTSUP);
11065084Sjohnlev }
11075084Sjohnlev 
11085084Sjohnlev /*
11095084Sjohnlev  *  xnf_set_multicast() -- set (enable) or disable a multicast address.
11105084Sjohnlev  *
11115084Sjohnlev  *  Program the hardware to enable/disable the multicast address
111210958Sdme@sun.com  *  in "mca".  Enable if "add" is true, disable if false.
11135084Sjohnlev  */
11145084Sjohnlev static int
xnf_set_multicast(void * arg,boolean_t add,const uint8_t * mca)11155084Sjohnlev xnf_set_multicast(void *arg, boolean_t add, const uint8_t *mca)
11165084Sjohnlev {
11175084Sjohnlev 	xnf_t *xnfp = arg;
111810958Sdme@sun.com 	xnf_txbuf_t *txp;
111910958Sdme@sun.com 	int n_slots;
112010958Sdme@sun.com 	RING_IDX slot;
112110958Sdme@sun.com 	xnf_txid_t *tidp;
112210958Sdme@sun.com 	netif_tx_request_t *txrp;
112310958Sdme@sun.com 	struct netif_extra_info *erp;
112410958Sdme@sun.com 	boolean_t notify, result;
112510958Sdme@sun.com 
112610958Sdme@sun.com 	/*
112710958Sdme@sun.com 	 * If the backend does not support multicast control then we
112810958Sdme@sun.com 	 * must assume that the right packets will just arrive.
112910958Sdme@sun.com 	 */
113010958Sdme@sun.com 	if (!xnfp->xnf_be_mcast_control)
113110958Sdme@sun.com 		return (0);
113210958Sdme@sun.com 
113310958Sdme@sun.com 	txp = kmem_cache_alloc(xnfp->xnf_tx_buf_cache, KM_SLEEP);
113410958Sdme@sun.com 
113510958Sdme@sun.com 	mutex_enter(&xnfp->xnf_txlock);
113610958Sdme@sun.com 
113710958Sdme@sun.com 	/*
113810958Sdme@sun.com 	 * If we're not yet connected then claim success. This is
113910958Sdme@sun.com 	 * acceptable because we refresh the entire set of multicast
114010958Sdme@sun.com 	 * addresses when we get connected.
114110958Sdme@sun.com 	 *
114210958Sdme@sun.com 	 * We can't wait around here because the MAC layer expects
114310958Sdme@sun.com 	 * this to be a non-blocking operation - waiting ends up
114410958Sdme@sun.com 	 * causing a deadlock during resume.
114510958Sdme@sun.com 	 */
114610958Sdme@sun.com 	if (!xnfp->xnf_connected) {
114710958Sdme@sun.com 		mutex_exit(&xnfp->xnf_txlock);
114810958Sdme@sun.com 		return (0);
114910958Sdme@sun.com 	}
11505084Sjohnlev 
11515084Sjohnlev 	/*
115210958Sdme@sun.com 	 * 1. Acquire two slots in the ring.
115310958Sdme@sun.com 	 * 2. Fill in the slots.
115410958Sdme@sun.com 	 * 3. Request notification when the operation is done.
115510958Sdme@sun.com 	 * 4. Kick the peer.
115610958Sdme@sun.com 	 * 5. Wait for the response via xnf_tx_clean_ring().
11575084Sjohnlev 	 */
115810958Sdme@sun.com 
115910958Sdme@sun.com 	n_slots = tx_slots_get(xnfp, 2, B_TRUE);
116010958Sdme@sun.com 	ASSERT(n_slots >= 2);
116110958Sdme@sun.com 
116210958Sdme@sun.com 	slot = xnfp->xnf_tx_ring.req_prod_pvt;
116310958Sdme@sun.com 	tidp = txid_get(xnfp);
116410958Sdme@sun.com 	VERIFY(tidp != NULL);
116510958Sdme@sun.com 
116610958Sdme@sun.com 	txp->tx_type = TX_MCAST_REQ;
116710958Sdme@sun.com 	txp->tx_slot = slot;
116810958Sdme@sun.com 
116910958Sdme@sun.com 	txrp = RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot);
117010958Sdme@sun.com 	erp = (struct netif_extra_info *)
117110958Sdme@sun.com 	    RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot + 1);
117210958Sdme@sun.com 
117310958Sdme@sun.com 	txrp->gref = 0;
117410958Sdme@sun.com 	txrp->size = 0;
117510958Sdme@sun.com 	txrp->offset = 0;
117610958Sdme@sun.com 	/* Set tx_txreq.id to appease xnf_tx_clean_ring(). */
117710958Sdme@sun.com 	txrp->id = txp->tx_txreq.id = tidp->id;
117810958Sdme@sun.com 	txrp->flags = NETTXF_extra_info;
117910958Sdme@sun.com 
118010958Sdme@sun.com 	erp->type = add ? XEN_NETIF_EXTRA_TYPE_MCAST_ADD :
118110958Sdme@sun.com 	    XEN_NETIF_EXTRA_TYPE_MCAST_DEL;
118210958Sdme@sun.com 	bcopy((void *)mca, &erp->u.mcast.addr, ETHERADDRL);
118310958Sdme@sun.com 
118410958Sdme@sun.com 	tidp->txbuf = txp;
118510958Sdme@sun.com 
118610958Sdme@sun.com 	xnfp->xnf_tx_ring.req_prod_pvt = slot + 2;
118710958Sdme@sun.com 
118810958Sdme@sun.com 	mutex_enter(&xnfp->xnf_schedlock);
118910958Sdme@sun.com 	xnfp->xnf_pending_multicast++;
119010958Sdme@sun.com 	mutex_exit(&xnfp->xnf_schedlock);
119110958Sdme@sun.com 
119210958Sdme@sun.com 	/* LINTED: constant in conditional context */
119310958Sdme@sun.com 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_tx_ring,
119410958Sdme@sun.com 	    notify);
119510958Sdme@sun.com 	if (notify)
119610958Sdme@sun.com 		ec_notify_via_evtchn(xnfp->xnf_evtchn);
119710958Sdme@sun.com 
119810958Sdme@sun.com 	while (txp->tx_type == TX_MCAST_REQ)
119910958Sdme@sun.com 		cv_wait(&xnfp->xnf_cv_multicast,
120010958Sdme@sun.com 		    &xnfp->xnf_txlock);
120110958Sdme@sun.com 
120210958Sdme@sun.com 	ASSERT(txp->tx_type == TX_MCAST_RSP);
120310958Sdme@sun.com 
120410958Sdme@sun.com 	mutex_enter(&xnfp->xnf_schedlock);
120510958Sdme@sun.com 	xnfp->xnf_pending_multicast--;
120610958Sdme@sun.com 	mutex_exit(&xnfp->xnf_schedlock);
120710958Sdme@sun.com 
120810958Sdme@sun.com 	result = (txp->tx_status == NETIF_RSP_OKAY);
120910958Sdme@sun.com 
121010958Sdme@sun.com 	txid_put(xnfp, tidp);
121110958Sdme@sun.com 
121210958Sdme@sun.com 	mutex_exit(&xnfp->xnf_txlock);
121310958Sdme@sun.com 
121410958Sdme@sun.com 	kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
121510958Sdme@sun.com 
121610958Sdme@sun.com 	return (result ? 0 : 1);
12175084Sjohnlev }
12185084Sjohnlev 
12195084Sjohnlev /*
12205084Sjohnlev  * xnf_set_promiscuous() -- set or reset promiscuous mode on the board
12215084Sjohnlev  *
12225084Sjohnlev  *  Program the hardware to enable/disable promiscuous mode.
12235084Sjohnlev  */
12245084Sjohnlev static int
xnf_set_promiscuous(void * arg,boolean_t on)12255084Sjohnlev xnf_set_promiscuous(void *arg, boolean_t on)
12265084Sjohnlev {
122710958Sdme@sun.com 	_NOTE(ARGUNUSED(arg, on));
122810958Sdme@sun.com 
12295084Sjohnlev 	/*
12305084Sjohnlev 	 * We can't really do this, but we pretend that we can in
12315084Sjohnlev 	 * order that snoop will work.
12325084Sjohnlev 	 */
12335084Sjohnlev 	return (0);
12345084Sjohnlev }
12355084Sjohnlev 
12365084Sjohnlev /*
12375084Sjohnlev  * Clean buffers that we have responses for from the transmit ring.
12385084Sjohnlev  */
12395084Sjohnlev static int
xnf_tx_clean_ring(xnf_t * xnfp)124010958Sdme@sun.com xnf_tx_clean_ring(xnf_t *xnfp)
12415084Sjohnlev {
124210958Sdme@sun.com 	boolean_t work_to_do;
12435084Sjohnlev 
12445741Smrj 	ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
12455084Sjohnlev 
12466899Scz147101 loop:
12475990Sschuster 	while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_tx_ring)) {
124810958Sdme@sun.com 		RING_IDX cons, prod, i;
124910958Sdme@sun.com 
125010958Sdme@sun.com 		cons = xnfp->xnf_tx_ring.rsp_cons;
125110958Sdme@sun.com 		prod = xnfp->xnf_tx_ring.sring->rsp_prod;
12525084Sjohnlev 		membar_consumer();
12535084Sjohnlev 		/*
125410958Sdme@sun.com 		 * Clean tx requests from ring that we have responses
125510958Sdme@sun.com 		 * for.
12565084Sjohnlev 		 */
125710958Sdme@sun.com 		DTRACE_PROBE2(xnf_tx_clean_range, int, cons, int, prod);
125810958Sdme@sun.com 		for (i = cons; i != prod; i++) {
125910958Sdme@sun.com 			netif_tx_response_t *trp;
126010958Sdme@sun.com 			xnf_txid_t *tidp;
126110958Sdme@sun.com 			xnf_txbuf_t *txp;
126210958Sdme@sun.com 
126310958Sdme@sun.com 			trp = RING_GET_RESPONSE(&xnfp->xnf_tx_ring, i);
126410958Sdme@sun.com 			ASSERT(TX_ID_VALID(trp->id));
126510958Sdme@sun.com 
126610958Sdme@sun.com 			tidp = TX_ID_TO_TXID(xnfp, trp->id);
126710958Sdme@sun.com 			ASSERT(tidp->id == trp->id);
126810958Sdme@sun.com 			ASSERT(tidp->next == INVALID_TX_ID);
126910958Sdme@sun.com 
127010958Sdme@sun.com 			txp = tidp->txbuf;
127110958Sdme@sun.com 			ASSERT(txp != NULL);
127210958Sdme@sun.com 			ASSERT(txp->tx_txreq.id == trp->id);
127310958Sdme@sun.com 
127410958Sdme@sun.com 			switch (txp->tx_type) {
127510958Sdme@sun.com 			case TX_DATA:
127610958Sdme@sun.com 				if (gnttab_query_foreign_access(
127710958Sdme@sun.com 				    txp->tx_txreq.gref) != 0)
127810958Sdme@sun.com 					cmn_err(CE_PANIC,
127910958Sdme@sun.com 					    "tx grant %d still in use by "
128010958Sdme@sun.com 					    "backend domain",
128110958Sdme@sun.com 					    txp->tx_txreq.gref);
128210958Sdme@sun.com 
128310958Sdme@sun.com 				if (txp->tx_bdesc == NULL) {
128410958Sdme@sun.com 					(void) gnttab_end_foreign_access_ref(
128510958Sdme@sun.com 					    txp->tx_txreq.gref, 1);
128610958Sdme@sun.com 					gref_put(xnfp, txp->tx_txreq.gref);
128710958Sdme@sun.com 					(void) ddi_dma_unbind_handle(
128810958Sdme@sun.com 					    txp->tx_dma_handle);
128910958Sdme@sun.com 				} else {
129010958Sdme@sun.com 					xnf_buf_put(xnfp, txp->tx_bdesc,
129110958Sdme@sun.com 					    B_TRUE);
129210958Sdme@sun.com 				}
129310958Sdme@sun.com 
129410958Sdme@sun.com 				freemsg(txp->tx_mp);
129510958Sdme@sun.com 				txid_put(xnfp, tidp);
129610958Sdme@sun.com 				kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
129710958Sdme@sun.com 
129810958Sdme@sun.com 				break;
129910958Sdme@sun.com 
130010958Sdme@sun.com 			case TX_MCAST_REQ:
130110958Sdme@sun.com 				txp->tx_type = TX_MCAST_RSP;
130210958Sdme@sun.com 				txp->tx_status = trp->status;
130310958Sdme@sun.com 				cv_broadcast(&xnfp->xnf_cv_multicast);
130410958Sdme@sun.com 
130510958Sdme@sun.com 				break;
130610958Sdme@sun.com 
130710958Sdme@sun.com 			case TX_MCAST_RSP:
130810958Sdme@sun.com 				break;
130910958Sdme@sun.com 
131010958Sdme@sun.com 			default:
131110958Sdme@sun.com 				cmn_err(CE_PANIC, "xnf_tx_clean_ring: "
131210958Sdme@sun.com 				    "invalid xnf_txbuf_t type: %d",
131310958Sdme@sun.com 				    txp->tx_type);
131410958Sdme@sun.com 				break;
131510958Sdme@sun.com 			}
13165084Sjohnlev 		}
131710958Sdme@sun.com 		/*
131810958Sdme@sun.com 		 * Record the last response we dealt with so that we
131910958Sdme@sun.com 		 * know where to start next time around.
132010958Sdme@sun.com 		 */
132110958Sdme@sun.com 		xnfp->xnf_tx_ring.rsp_cons = prod;
13225084Sjohnlev 		membar_enter();
13235990Sschuster 	}
13245990Sschuster 
13256899Scz147101 	/* LINTED: constant in conditional context */
13266899Scz147101 	RING_FINAL_CHECK_FOR_RESPONSES(&xnfp->xnf_tx_ring, work_to_do);
13276899Scz147101 	if (work_to_do)
13286899Scz147101 		goto loop;
13296899Scz147101 
13305990Sschuster 	return (RING_FREE_REQUESTS(&xnfp->xnf_tx_ring));
13315084Sjohnlev }
13325084Sjohnlev 
13335084Sjohnlev /*
133410958Sdme@sun.com  * Allocate and fill in a look-aside buffer for the packet `mp'. Used
133510958Sdme@sun.com  * to ensure that the packet is physically contiguous and contained
133610958Sdme@sun.com  * within a single page.
13375084Sjohnlev  */
133810958Sdme@sun.com static xnf_buf_t *
xnf_tx_pullup(xnf_t * xnfp,mblk_t * mp)133910958Sdme@sun.com xnf_tx_pullup(xnf_t *xnfp, mblk_t *mp)
13405084Sjohnlev {
134110958Sdme@sun.com 	xnf_buf_t *bd;
134210958Sdme@sun.com 	caddr_t bp;
134310958Sdme@sun.com 
134410958Sdme@sun.com 	bd = xnf_buf_get(xnfp, KM_SLEEP, B_TRUE);
134510958Sdme@sun.com 	if (bd == NULL)
134610958Sdme@sun.com 		return (NULL);
134710958Sdme@sun.com 
134810958Sdme@sun.com 	bp = bd->buf;
134910958Sdme@sun.com 	while (mp != NULL) {
135010958Sdme@sun.com 		size_t len = MBLKL(mp);
135110958Sdme@sun.com 
135210958Sdme@sun.com 		bcopy(mp->b_rptr, bp, len);
135310958Sdme@sun.com 		bp += len;
135410958Sdme@sun.com 
135510958Sdme@sun.com 		mp = mp->b_cont;
135610958Sdme@sun.com 	}
135710958Sdme@sun.com 
135810958Sdme@sun.com 	ASSERT((bp - bd->buf) <= PAGESIZE);
135910958Sdme@sun.com 
13605741Smrj 	xnfp->xnf_stat_tx_pullup++;
136110958Sdme@sun.com 
136210958Sdme@sun.com 	return (bd);
13635084Sjohnlev }
13645084Sjohnlev 
136510958Sdme@sun.com /*
136610958Sdme@sun.com  * Insert the pseudo-header checksum into the packet `buf'.
136710958Sdme@sun.com  */
13687351Sdme@sun.com void
xnf_pseudo_cksum(caddr_t buf,int length)13697351Sdme@sun.com xnf_pseudo_cksum(caddr_t buf, int length)
13707351Sdme@sun.com {
13717351Sdme@sun.com 	struct ether_header *ehp;
13727351Sdme@sun.com 	uint16_t sap, len, *stuff;
13737351Sdme@sun.com 	uint32_t cksum;
13747351Sdme@sun.com 	size_t offset;
13757351Sdme@sun.com 	ipha_t *ipha;
13767351Sdme@sun.com 	ipaddr_t src, dst;
13777351Sdme@sun.com 
13787351Sdme@sun.com 	ASSERT(length >= sizeof (*ehp));
13797351Sdme@sun.com 	ehp = (struct ether_header *)buf;
13807351Sdme@sun.com 
13817351Sdme@sun.com 	if (ntohs(ehp->ether_type) == VLAN_TPID) {
13827351Sdme@sun.com 		struct ether_vlan_header *evhp;
13837351Sdme@sun.com 
13847351Sdme@sun.com 		ASSERT(length >= sizeof (*evhp));
13857351Sdme@sun.com 		evhp = (struct ether_vlan_header *)buf;
13867351Sdme@sun.com 		sap = ntohs(evhp->ether_type);
13877351Sdme@sun.com 		offset = sizeof (*evhp);
13887351Sdme@sun.com 	} else {
13897351Sdme@sun.com 		sap = ntohs(ehp->ether_type);
13907351Sdme@sun.com 		offset = sizeof (*ehp);
13917351Sdme@sun.com 	}
13927351Sdme@sun.com 
13937351Sdme@sun.com 	ASSERT(sap == ETHERTYPE_IP);
13947351Sdme@sun.com 
13957351Sdme@sun.com 	/* Packet should have been pulled up by the caller. */
13967351Sdme@sun.com 	if ((offset + sizeof (ipha_t)) > length) {
13977351Sdme@sun.com 		cmn_err(CE_WARN, "xnf_pseudo_cksum: no room for checksum");
13987351Sdme@sun.com 		return;
13997351Sdme@sun.com 	}
14007351Sdme@sun.com 
14017351Sdme@sun.com 	ipha = (ipha_t *)(buf + offset);
14027351Sdme@sun.com 
14037351Sdme@sun.com 	ASSERT(IPH_HDR_LENGTH(ipha) == IP_SIMPLE_HDR_LENGTH);
14047351Sdme@sun.com 
14057351Sdme@sun.com 	len = ntohs(ipha->ipha_length) - IP_SIMPLE_HDR_LENGTH;
14067351Sdme@sun.com 
14077351Sdme@sun.com 	switch (ipha->ipha_protocol) {
14087351Sdme@sun.com 	case IPPROTO_TCP:
14097351Sdme@sun.com 		stuff = IPH_TCPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH);
14107351Sdme@sun.com 		cksum = IP_TCP_CSUM_COMP;
14117351Sdme@sun.com 		break;
14127351Sdme@sun.com 	case IPPROTO_UDP:
14137351Sdme@sun.com 		stuff = IPH_UDPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH);
14147351Sdme@sun.com 		cksum = IP_UDP_CSUM_COMP;
14157351Sdme@sun.com 		break;
14167351Sdme@sun.com 	default:
14177351Sdme@sun.com 		cmn_err(CE_WARN, "xnf_pseudo_cksum: unexpected protocol %d",
14187351Sdme@sun.com 		    ipha->ipha_protocol);
14197351Sdme@sun.com 		return;
14207351Sdme@sun.com 	}
14217351Sdme@sun.com 
14227351Sdme@sun.com 	src = ipha->ipha_src;
14237351Sdme@sun.com 	dst = ipha->ipha_dst;
14247351Sdme@sun.com 
14257351Sdme@sun.com 	cksum += (dst >> 16) + (dst & 0xFFFF);
14267351Sdme@sun.com 	cksum += (src >> 16) + (src & 0xFFFF);
14277351Sdme@sun.com 	cksum += htons(len);
14287351Sdme@sun.com 
14297351Sdme@sun.com 	cksum = (cksum >> 16) + (cksum & 0xFFFF);
14307351Sdme@sun.com 	cksum = (cksum >> 16) + (cksum & 0xFFFF);
14317351Sdme@sun.com 
14327351Sdme@sun.com 	ASSERT(cksum <= 0xFFFF);
14337351Sdme@sun.com 
14347351Sdme@sun.com 	*stuff = (uint16_t)(cksum ? cksum : ~cksum);
14357351Sdme@sun.com }
14367351Sdme@sun.com 
14375084Sjohnlev /*
143810958Sdme@sun.com  * Push a list of prepared packets (`txp') into the transmit ring.
14395084Sjohnlev  */
144010958Sdme@sun.com static xnf_txbuf_t *
tx_push_packets(xnf_t * xnfp,xnf_txbuf_t * txp)144110958Sdme@sun.com tx_push_packets(xnf_t *xnfp, xnf_txbuf_t *txp)
144210958Sdme@sun.com {
144310958Sdme@sun.com 	int slots_free;
144410958Sdme@sun.com 	RING_IDX slot;
144510958Sdme@sun.com 	boolean_t notify;
144610958Sdme@sun.com 
144710958Sdme@sun.com 	mutex_enter(&xnfp->xnf_txlock);
144810958Sdme@sun.com 
144910958Sdme@sun.com 	ASSERT(xnfp->xnf_running);
145010958Sdme@sun.com 
145110958Sdme@sun.com 	/*
145210958Sdme@sun.com 	 * Wait until we are connected to the backend.
145310958Sdme@sun.com 	 */
145410958Sdme@sun.com 	while (!xnfp->xnf_connected)
145510958Sdme@sun.com 		cv_wait(&xnfp->xnf_cv_state, &xnfp->xnf_txlock);
145610958Sdme@sun.com 
145710958Sdme@sun.com 	slots_free = tx_slots_get(xnfp, 1, B_FALSE);
145810958Sdme@sun.com 	DTRACE_PROBE1(xnf_send_slotsfree, int, slots_free);
145910958Sdme@sun.com 
146010958Sdme@sun.com 	slot = xnfp->xnf_tx_ring.req_prod_pvt;
146110958Sdme@sun.com 
146210958Sdme@sun.com 	while ((txp != NULL) && (slots_free > 0)) {
146310958Sdme@sun.com 		xnf_txid_t *tidp;
146410958Sdme@sun.com 		netif_tx_request_t *txrp;
146510958Sdme@sun.com 
146610958Sdme@sun.com 		tidp = txid_get(xnfp);
146710958Sdme@sun.com 		VERIFY(tidp != NULL);
146810958Sdme@sun.com 
146910958Sdme@sun.com 		txrp = RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot);
147010958Sdme@sun.com 
147110958Sdme@sun.com 		txp->tx_slot = slot;
147210958Sdme@sun.com 		txp->tx_txreq.id = tidp->id;
147310958Sdme@sun.com 		*txrp = txp->tx_txreq;
147410958Sdme@sun.com 
147510958Sdme@sun.com 		tidp->txbuf = txp;
147610958Sdme@sun.com 
147710958Sdme@sun.com 		xnfp->xnf_stat_opackets++;
147810958Sdme@sun.com 		xnfp->xnf_stat_obytes += txp->tx_txreq.size;
147910958Sdme@sun.com 
148010958Sdme@sun.com 		txp = txp->tx_next;
148110958Sdme@sun.com 		slots_free--;
148210958Sdme@sun.com 		slot++;
148310958Sdme@sun.com 
148410958Sdme@sun.com 	}
148510958Sdme@sun.com 
148610958Sdme@sun.com 	xnfp->xnf_tx_ring.req_prod_pvt = slot;
148710958Sdme@sun.com 
148810958Sdme@sun.com 	/*
148910958Sdme@sun.com 	 * Tell the peer that we sent something, if it cares.
149010958Sdme@sun.com 	 */
149110958Sdme@sun.com 	/* LINTED: constant in conditional context */
149210958Sdme@sun.com 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_tx_ring,
149310958Sdme@sun.com 	    notify);
149410958Sdme@sun.com 	if (notify)
149510958Sdme@sun.com 		ec_notify_via_evtchn(xnfp->xnf_evtchn);
149610958Sdme@sun.com 
149710958Sdme@sun.com 	mutex_exit(&xnfp->xnf_txlock);
149810958Sdme@sun.com 
149910958Sdme@sun.com 	return (txp);
150010958Sdme@sun.com }
150110958Sdme@sun.com 
150210958Sdme@sun.com /*
150310958Sdme@sun.com  * Send the chain of packets `mp'. Called by the MAC framework.
150410958Sdme@sun.com  */
150510958Sdme@sun.com static mblk_t *
xnf_send(void * arg,mblk_t * mp)150610958Sdme@sun.com xnf_send(void *arg, mblk_t *mp)
15075084Sjohnlev {
150810958Sdme@sun.com 	xnf_t *xnfp = arg;
150910958Sdme@sun.com 	domid_t oeid;
151010958Sdme@sun.com 	xnf_txbuf_t *head, *tail;
151110958Sdme@sun.com 	mblk_t *ml;
151210958Sdme@sun.com 	int prepared;
151310958Sdme@sun.com 
151410958Sdme@sun.com 	oeid = xvdi_get_oeid(xnfp->xnf_devinfo);
151510958Sdme@sun.com 
151610958Sdme@sun.com 	/*
151710958Sdme@sun.com 	 * Prepare packets for transmission.
151810958Sdme@sun.com 	 */
151910958Sdme@sun.com 	head = tail = NULL;
152010958Sdme@sun.com 	prepared = 0;
152110958Sdme@sun.com 	while (mp != NULL) {
152210958Sdme@sun.com 		xnf_txbuf_t *txp;
152310958Sdme@sun.com 		int n_chunks, length;
152410958Sdme@sun.com 		boolean_t page_oops;
152510958Sdme@sun.com 		uint32_t pflags;
152610958Sdme@sun.com 
152710958Sdme@sun.com 		for (ml = mp, n_chunks = length = 0, page_oops = B_FALSE;
152810958Sdme@sun.com 		    ml != NULL;
152910958Sdme@sun.com 		    ml = ml->b_cont, n_chunks++) {
153010958Sdme@sun.com 
153110958Sdme@sun.com 			/*
153210958Sdme@sun.com 			 * Test if this buffer includes a page
153310958Sdme@sun.com 			 * boundary. The test assumes that the range
153410958Sdme@sun.com 			 * b_rptr...b_wptr can include only a single
153510958Sdme@sun.com 			 * boundary.
153610958Sdme@sun.com 			 */
153710958Sdme@sun.com 			if (xnf_btop((size_t)ml->b_rptr) !=
153810958Sdme@sun.com 			    xnf_btop((size_t)ml->b_wptr)) {
153910958Sdme@sun.com 				xnfp->xnf_stat_tx_pagebndry++;
154010958Sdme@sun.com 				page_oops = B_TRUE;
154110958Sdme@sun.com 			}
154210958Sdme@sun.com 
154310958Sdme@sun.com 			length += MBLKL(ml);
154410958Sdme@sun.com 		}
154510958Sdme@sun.com 		DTRACE_PROBE1(xnf_send_b_cont, int, n_chunks);
154610958Sdme@sun.com 
154710958Sdme@sun.com 		/*
154810958Sdme@sun.com 		 * Make sure packet isn't too large.
154910958Sdme@sun.com 		 */
155010958Sdme@sun.com 		if (length > XNF_FRAMESIZE) {
155110958Sdme@sun.com 			cmn_err(CE_WARN,
155210958Sdme@sun.com 			    "xnf%d: oversized packet (%d bytes) dropped",
155310958Sdme@sun.com 			    ddi_get_instance(xnfp->xnf_devinfo), length);
155410958Sdme@sun.com 			freemsg(mp);
155510958Sdme@sun.com 			continue;
155610958Sdme@sun.com 		}
155710958Sdme@sun.com 
155810958Sdme@sun.com 		txp = kmem_cache_alloc(xnfp->xnf_tx_buf_cache, KM_SLEEP);
155910958Sdme@sun.com 
156010958Sdme@sun.com 		txp->tx_type = TX_DATA;
156110958Sdme@sun.com 
156210958Sdme@sun.com 		if ((n_chunks > xnf_max_tx_frags) || page_oops) {
156310958Sdme@sun.com 			/*
156410958Sdme@sun.com 			 * Loan a side buffer rather than the mblk
156510958Sdme@sun.com 			 * itself.
156610958Sdme@sun.com 			 */
156710958Sdme@sun.com 			txp->tx_bdesc = xnf_tx_pullup(xnfp, mp);
156810958Sdme@sun.com 			if (txp->tx_bdesc == NULL) {
156910958Sdme@sun.com 				kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
157010958Sdme@sun.com 				break;
157110958Sdme@sun.com 			}
157210958Sdme@sun.com 
157310958Sdme@sun.com 			txp->tx_bufp = txp->tx_bdesc->buf;
157410958Sdme@sun.com 			txp->tx_mfn = txp->tx_bdesc->buf_mfn;
157510958Sdme@sun.com 			txp->tx_txreq.gref = txp->tx_bdesc->grant_ref;
157610958Sdme@sun.com 
157710958Sdme@sun.com 		} else {
157810958Sdme@sun.com 			int rc;
157910958Sdme@sun.com 			ddi_dma_cookie_t dma_cookie;
158010958Sdme@sun.com 			uint_t ncookies;
158110958Sdme@sun.com 
158210958Sdme@sun.com 			rc = ddi_dma_addr_bind_handle(txp->tx_dma_handle,
158310958Sdme@sun.com 			    NULL, (char *)mp->b_rptr, length,
158410958Sdme@sun.com 			    DDI_DMA_WRITE | DDI_DMA_STREAMING,
158510958Sdme@sun.com 			    DDI_DMA_DONTWAIT, 0, &dma_cookie,
158610958Sdme@sun.com 			    &ncookies);
158710958Sdme@sun.com 			if (rc != DDI_DMA_MAPPED) {
158810958Sdme@sun.com 				ASSERT(rc != DDI_DMA_INUSE);
158910958Sdme@sun.com 				ASSERT(rc != DDI_DMA_PARTIAL_MAP);
15905084Sjohnlev 
15915084Sjohnlev #ifdef XNF_DEBUG
159210958Sdme@sun.com 				if (rc != DDI_DMA_NORESOURCES)
159310958Sdme@sun.com 					cmn_err(CE_WARN,
159410958Sdme@sun.com 					    "xnf%d: bind_handle failed (%x)",
159510958Sdme@sun.com 					    ddi_get_instance(xnfp->xnf_devinfo),
159610958Sdme@sun.com 					    rc);
15975084Sjohnlev #endif
159810958Sdme@sun.com 				kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
159910958Sdme@sun.com 				break;
160010958Sdme@sun.com 			}
160110958Sdme@sun.com 			ASSERT(ncookies == 1);
160210958Sdme@sun.com 
160310958Sdme@sun.com 			txp->tx_bdesc = NULL;
160410958Sdme@sun.com 			txp->tx_bufp = (caddr_t)mp->b_rptr;
160510958Sdme@sun.com 			txp->tx_mfn =
160610958Sdme@sun.com 			    xnf_btop(pa_to_ma(dma_cookie.dmac_laddress));
160710958Sdme@sun.com 			txp->tx_txreq.gref = gref_get(xnfp);
160810958Sdme@sun.com 			if (txp->tx_txreq.gref == INVALID_GRANT_REF) {
160910958Sdme@sun.com 				(void) ddi_dma_unbind_handle(
161010958Sdme@sun.com 				    txp->tx_dma_handle);
161110958Sdme@sun.com 				kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
161210958Sdme@sun.com 				break;
161310958Sdme@sun.com 			}
161410958Sdme@sun.com 			gnttab_grant_foreign_access_ref(txp->tx_txreq.gref,
161510958Sdme@sun.com 			    oeid, txp->tx_mfn, 1);
161610958Sdme@sun.com 		}
161710958Sdme@sun.com 
161810958Sdme@sun.com 		txp->tx_next = NULL;
161910958Sdme@sun.com 		txp->tx_mp = mp;
162010958Sdme@sun.com 		txp->tx_txreq.size = length;
162110958Sdme@sun.com 		txp->tx_txreq.offset = (uintptr_t)txp->tx_bufp & PAGEOFFSET;
162210958Sdme@sun.com 		txp->tx_txreq.flags = 0;
1623*11878SVenu.Iyer@Sun.COM 		mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &pflags);
162410958Sdme@sun.com 		if (pflags != 0) {
162510958Sdme@sun.com 			/*
162610958Sdme@sun.com 			 * If the local protocol stack requests checksum
162710958Sdme@sun.com 			 * offload we set the 'checksum blank' flag,
162810958Sdme@sun.com 			 * indicating to the peer that we need the checksum
162910958Sdme@sun.com 			 * calculated for us.
163010958Sdme@sun.com 			 *
163110958Sdme@sun.com 			 * We _don't_ set the validated flag, because we haven't
163210958Sdme@sun.com 			 * validated that the data and the checksum match.
163310958Sdme@sun.com 			 */
163410958Sdme@sun.com 			xnf_pseudo_cksum(txp->tx_bufp, length);
163510958Sdme@sun.com 			txp->tx_txreq.flags |= NETTXF_csum_blank;
163610958Sdme@sun.com 
163710958Sdme@sun.com 			xnfp->xnf_stat_tx_cksum_deferred++;
163810958Sdme@sun.com 		}
163910958Sdme@sun.com 
164010958Sdme@sun.com 		if (head == NULL) {
164110958Sdme@sun.com 			ASSERT(tail == NULL);
164210958Sdme@sun.com 
164310958Sdme@sun.com 			head = txp;
164410958Sdme@sun.com 		} else {
164510958Sdme@sun.com 			ASSERT(tail != NULL);
164610958Sdme@sun.com 
164710958Sdme@sun.com 			tail->tx_next = txp;
164810958Sdme@sun.com 		}
164910958Sdme@sun.com 		tail = txp;
165010958Sdme@sun.com 
165110958Sdme@sun.com 		mp = mp->b_next;
165210958Sdme@sun.com 		prepared++;
165310958Sdme@sun.com 
165410958Sdme@sun.com 		/*
165510958Sdme@sun.com 		 * There is no point in preparing more than
165610958Sdme@sun.com 		 * NET_TX_RING_SIZE, as we won't be able to push them
165710958Sdme@sun.com 		 * into the ring in one go and would hence have to
165810958Sdme@sun.com 		 * un-prepare the extra.
165910958Sdme@sun.com 		 */
166010958Sdme@sun.com 		if (prepared == NET_TX_RING_SIZE)
166110958Sdme@sun.com 			break;
16625084Sjohnlev 	}
16635084Sjohnlev 
166410958Sdme@sun.com 	DTRACE_PROBE1(xnf_send_prepared, int, prepared);
166510958Sdme@sun.com 
166610958Sdme@sun.com 	if (mp != NULL) {
166710958Sdme@sun.com #ifdef XNF_DEBUG
166810958Sdme@sun.com 		int notprepared = 0;
166910958Sdme@sun.com 		mblk_t *l = mp;
167010958Sdme@sun.com 
167110958Sdme@sun.com 		while (l != NULL) {
167210958Sdme@sun.com 			notprepared++;
167310958Sdme@sun.com 			l = l->b_next;
167410958Sdme@sun.com 		}
167510958Sdme@sun.com 
167610958Sdme@sun.com 		DTRACE_PROBE1(xnf_send_notprepared, int, notprepared);
167710958Sdme@sun.com #else /* !XNF_DEBUG */
167810958Sdme@sun.com 		DTRACE_PROBE1(xnf_send_notprepared, int, -1);
167910958Sdme@sun.com #endif /* XNF_DEBUG */
16805084Sjohnlev 	}
16815084Sjohnlev 
16825084Sjohnlev 	/*
168310958Sdme@sun.com 	 * Push the packets we have prepared into the ring. They may
168410958Sdme@sun.com 	 * not all go.
16855084Sjohnlev 	 */
168610958Sdme@sun.com 	if (head != NULL)
168710958Sdme@sun.com 		head = tx_push_packets(xnfp, head);
16885084Sjohnlev 
16895084Sjohnlev 	/*
169010958Sdme@sun.com 	 * If some packets that we prepared were not sent, unprepare
169110958Sdme@sun.com 	 * them and add them back to the head of those we didn't
169210958Sdme@sun.com 	 * prepare.
16935084Sjohnlev 	 */
169410958Sdme@sun.com 	{
169510958Sdme@sun.com 		xnf_txbuf_t *loop;
169610958Sdme@sun.com 		mblk_t *mp_head, *mp_tail;
169710958Sdme@sun.com 		int unprepared = 0;
169810958Sdme@sun.com 
169910958Sdme@sun.com 		mp_head = mp_tail = NULL;
170010958Sdme@sun.com 		loop = head;
170110958Sdme@sun.com 
170210958Sdme@sun.com 		while (loop != NULL) {
170310958Sdme@sun.com 			xnf_txbuf_t *next = loop->tx_next;
170410958Sdme@sun.com 
170510958Sdme@sun.com 			if (loop->tx_bdesc == NULL) {
170610958Sdme@sun.com 				(void) gnttab_end_foreign_access_ref(
170710958Sdme@sun.com 				    loop->tx_txreq.gref, 1);
170810958Sdme@sun.com 				gref_put(xnfp, loop->tx_txreq.gref);
170910958Sdme@sun.com 				(void) ddi_dma_unbind_handle(
171010958Sdme@sun.com 				    loop->tx_dma_handle);
171110958Sdme@sun.com 			} else {
171210958Sdme@sun.com 				xnf_buf_put(xnfp, loop->tx_bdesc, B_TRUE);
171310958Sdme@sun.com 			}
171410958Sdme@sun.com 
171510958Sdme@sun.com 			ASSERT(loop->tx_mp != NULL);
171610958Sdme@sun.com 			if (mp_head == NULL)
171710958Sdme@sun.com 				mp_head = loop->tx_mp;
171810958Sdme@sun.com 			mp_tail = loop->tx_mp;
171910958Sdme@sun.com 
172010958Sdme@sun.com 			kmem_cache_free(xnfp->xnf_tx_buf_cache, loop);
172110958Sdme@sun.com 			loop = next;
172210958Sdme@sun.com 			unprepared++;
17235084Sjohnlev 		}
172410958Sdme@sun.com 
172510958Sdme@sun.com 		if (mp_tail == NULL) {
172610958Sdme@sun.com 			ASSERT(mp_head == NULL);
172710958Sdme@sun.com 		} else {
172810958Sdme@sun.com 			ASSERT(mp_head != NULL);
172910958Sdme@sun.com 
173010958Sdme@sun.com 			mp_tail->b_next = mp;
173110958Sdme@sun.com 			mp = mp_head;
173210958Sdme@sun.com 		}
173310958Sdme@sun.com 
173410958Sdme@sun.com 		DTRACE_PROBE1(xnf_send_unprepared, int, unprepared);
17355084Sjohnlev 	}
17365084Sjohnlev 
17375084Sjohnlev 	/*
173810958Sdme@sun.com 	 * If any mblks are left then we have deferred for some reason
173910958Sdme@sun.com 	 * and need to ask for a re-schedule later. This is typically
174010958Sdme@sun.com 	 * due to the ring filling.
17415084Sjohnlev 	 */
174210958Sdme@sun.com 	if (mp != NULL) {
174310958Sdme@sun.com 		mutex_enter(&xnfp->xnf_schedlock);
174410958Sdme@sun.com 		xnfp->xnf_need_sched = B_TRUE;
174510958Sdme@sun.com 		mutex_exit(&xnfp->xnf_schedlock);
174610958Sdme@sun.com 
174710958Sdme@sun.com 		xnfp->xnf_stat_tx_defer++;
17485084Sjohnlev 	}
17495084Sjohnlev 
17505084Sjohnlev 	return (mp);
17515084Sjohnlev }
17525084Sjohnlev 
17535084Sjohnlev /*
175410958Sdme@sun.com  * Notification of RX packets. Currently no TX-complete interrupt is
175510958Sdme@sun.com  * used, as we clean the TX ring lazily.
17565084Sjohnlev  */
17575084Sjohnlev static uint_t
xnf_intr(caddr_t arg)17585084Sjohnlev xnf_intr(caddr_t arg)
17595084Sjohnlev {
17605084Sjohnlev 	xnf_t *xnfp = (xnf_t *)arg;
176110958Sdme@sun.com 	mblk_t *mp;
176210958Sdme@sun.com 	boolean_t need_sched, clean_ring;
176310958Sdme@sun.com 
176410958Sdme@sun.com 	mutex_enter(&xnfp->xnf_rxlock);
176510958Sdme@sun.com 
176610958Sdme@sun.com 	/*
176710958Sdme@sun.com 	 * Interrupts before we are connected are spurious.
176810958Sdme@sun.com 	 */
17696899Scz147101 	if (!xnfp->xnf_connected) {
177010958Sdme@sun.com 		mutex_exit(&xnfp->xnf_rxlock);
17715741Smrj 		xnfp->xnf_stat_unclaimed_interrupts++;
17725084Sjohnlev 		return (DDI_INTR_UNCLAIMED);
17735084Sjohnlev 	}
17745084Sjohnlev 
177510958Sdme@sun.com 	/*
177610958Sdme@sun.com 	 * Receive side processing.
177710958Sdme@sun.com 	 */
177810958Sdme@sun.com 	do {
177910958Sdme@sun.com 		/*
178010958Sdme@sun.com 		 * Collect buffers from the ring.
178110958Sdme@sun.com 		 */
178210958Sdme@sun.com 		xnf_rx_collect(xnfp);
178310958Sdme@sun.com 
178410958Sdme@sun.com 		/*
178510958Sdme@sun.com 		 * Interrupt me when the next receive buffer is consumed.
178610958Sdme@sun.com 		 */
178710958Sdme@sun.com 		xnfp->xnf_rx_ring.sring->rsp_event =
178810958Sdme@sun.com 		    xnfp->xnf_rx_ring.rsp_cons + 1;
178910958Sdme@sun.com 		xen_mb();
179010958Sdme@sun.com 
179110958Sdme@sun.com 	} while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_rx_ring));
179210958Sdme@sun.com 
179310958Sdme@sun.com 	if (xnfp->xnf_rx_new_buffers_posted) {
179410958Sdme@sun.com 		boolean_t notify;
179510958Sdme@sun.com 
179610958Sdme@sun.com 		/*
179710958Sdme@sun.com 		 * Indicate to the peer that we have re-filled the
179810958Sdme@sun.com 		 * receive ring, if it cares.
179910958Sdme@sun.com 		 */
180010958Sdme@sun.com 		/* LINTED: constant in conditional context */
180110958Sdme@sun.com 		RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_rx_ring, notify);
180210958Sdme@sun.com 		if (notify)
180310958Sdme@sun.com 			ec_notify_via_evtchn(xnfp->xnf_evtchn);
180410958Sdme@sun.com 		xnfp->xnf_rx_new_buffers_posted = B_FALSE;
18055084Sjohnlev 	}
18065084Sjohnlev 
180710958Sdme@sun.com 	mp = xnfp->xnf_rx_head;
180810958Sdme@sun.com 	xnfp->xnf_rx_head = xnfp->xnf_rx_tail = NULL;
180910958Sdme@sun.com 
18107521Sdme@sun.com 	xnfp->xnf_stat_interrupts++;
181110958Sdme@sun.com 	mutex_exit(&xnfp->xnf_rxlock);
181210958Sdme@sun.com 
181310958Sdme@sun.com 	if (mp != NULL)
181410958Sdme@sun.com 		mac_rx(xnfp->xnf_mh, NULL, mp);
18157521Sdme@sun.com 
18165084Sjohnlev 	/*
181710958Sdme@sun.com 	 * Transmit side processing.
181810958Sdme@sun.com 	 *
181910958Sdme@sun.com 	 * If a previous transmit attempt failed or we have pending
182010958Sdme@sun.com 	 * multicast requests, clean the ring.
182110958Sdme@sun.com 	 *
182210958Sdme@sun.com 	 * If we previously stalled transmission and cleaning produces
182310958Sdme@sun.com 	 * some free slots, tell upstream to attempt sending again.
182410958Sdme@sun.com 	 *
182510958Sdme@sun.com 	 * The odd style is to avoid acquiring xnf_txlock unless we
182610958Sdme@sun.com 	 * will actually look inside the tx machinery.
18275084Sjohnlev 	 */
182810958Sdme@sun.com 	mutex_enter(&xnfp->xnf_schedlock);
182910958Sdme@sun.com 	need_sched = xnfp->xnf_need_sched;
183010958Sdme@sun.com 	clean_ring = need_sched || (xnfp->xnf_pending_multicast > 0);
183110958Sdme@sun.com 	mutex_exit(&xnfp->xnf_schedlock);
183210958Sdme@sun.com 
183310958Sdme@sun.com 	if (clean_ring) {
183410958Sdme@sun.com 		int free_slots;
183510958Sdme@sun.com 
183610958Sdme@sun.com 		mutex_enter(&xnfp->xnf_txlock);
183710958Sdme@sun.com 		free_slots = tx_slots_get(xnfp, 0, B_FALSE);
183810958Sdme@sun.com 
183910958Sdme@sun.com 		if (need_sched && (free_slots > 0)) {
184010958Sdme@sun.com 			mutex_enter(&xnfp->xnf_schedlock);
184110958Sdme@sun.com 			xnfp->xnf_need_sched = B_FALSE;
184210958Sdme@sun.com 			mutex_exit(&xnfp->xnf_schedlock);
184310958Sdme@sun.com 
184410958Sdme@sun.com 			mac_tx_update(xnfp->xnf_mh);
184510958Sdme@sun.com 		}
184610958Sdme@sun.com 		mutex_exit(&xnfp->xnf_txlock);
18477521Sdme@sun.com 	}
18485084Sjohnlev 
18497521Sdme@sun.com 	return (DDI_INTR_CLAIMED);
18505084Sjohnlev }
18515084Sjohnlev 
18525084Sjohnlev /*
18535084Sjohnlev  *  xnf_start() -- start the board receiving and enable interrupts.
18545084Sjohnlev  */
18555084Sjohnlev static int
xnf_start(void * arg)18565084Sjohnlev xnf_start(void *arg)
18575084Sjohnlev {
18585084Sjohnlev 	xnf_t *xnfp = arg;
18595084Sjohnlev 
18605084Sjohnlev #ifdef XNF_DEBUG
186110958Sdme@sun.com 	if (xnf_debug & XNF_DEBUG_TRACE)
18625084Sjohnlev 		printf("xnf%d start(0x%p)\n",
18635741Smrj 		    ddi_get_instance(xnfp->xnf_devinfo), (void *)xnfp);
18645084Sjohnlev #endif
18655084Sjohnlev 
186610958Sdme@sun.com 	mutex_enter(&xnfp->xnf_rxlock);
18675741Smrj 	mutex_enter(&xnfp->xnf_txlock);
18685084Sjohnlev 
18695084Sjohnlev 	/* Accept packets from above. */
18705741Smrj 	xnfp->xnf_running = B_TRUE;
18715084Sjohnlev 
18725741Smrj 	mutex_exit(&xnfp->xnf_txlock);
187310958Sdme@sun.com 	mutex_exit(&xnfp->xnf_rxlock);
18745084Sjohnlev 
18755084Sjohnlev 	return (0);
18765084Sjohnlev }
18775084Sjohnlev 
18785084Sjohnlev /* xnf_stop() - disable hardware */
18795084Sjohnlev static void
xnf_stop(void * arg)18805084Sjohnlev xnf_stop(void *arg)
18815084Sjohnlev {
18825084Sjohnlev 	xnf_t *xnfp = arg;
18835084Sjohnlev 
18845084Sjohnlev #ifdef XNF_DEBUG
188510958Sdme@sun.com 	if (xnf_debug & XNF_DEBUG_TRACE)
18865084Sjohnlev 		printf("xnf%d stop(0x%p)\n",
18875741Smrj 		    ddi_get_instance(xnfp->xnf_devinfo), (void *)xnfp);
18885084Sjohnlev #endif
18895084Sjohnlev 
189010958Sdme@sun.com 	mutex_enter(&xnfp->xnf_rxlock);
18915741Smrj 	mutex_enter(&xnfp->xnf_txlock);
18925084Sjohnlev 
18935741Smrj 	xnfp->xnf_running = B_FALSE;
18945084Sjohnlev 
18955741Smrj 	mutex_exit(&xnfp->xnf_txlock);
189610958Sdme@sun.com 	mutex_exit(&xnfp->xnf_rxlock);
18975084Sjohnlev }
18985084Sjohnlev 
18995084Sjohnlev /*
190010958Sdme@sun.com  * Hang buffer `bdesc' on the RX ring.
19015084Sjohnlev  */
19025084Sjohnlev static void
xnf_rxbuf_hang(xnf_t * xnfp,xnf_buf_t * bdesc)190310958Sdme@sun.com xnf_rxbuf_hang(xnf_t *xnfp, xnf_buf_t *bdesc)
19045084Sjohnlev {
190510958Sdme@sun.com 	netif_rx_request_t *reqp;
190610958Sdme@sun.com 	RING_IDX hang_ix;
190710958Sdme@sun.com 
190810958Sdme@sun.com 	ASSERT(MUTEX_HELD(&xnfp->xnf_rxlock));
190910958Sdme@sun.com 
19105741Smrj 	reqp = RING_GET_REQUEST(&xnfp->xnf_rx_ring,
19115741Smrj 	    xnfp->xnf_rx_ring.req_prod_pvt);
19125741Smrj 	hang_ix = (RING_IDX) (reqp - RING_GET_REQUEST(&xnfp->xnf_rx_ring, 0));
191310958Sdme@sun.com 	ASSERT(xnfp->xnf_rx_pkt_info[hang_ix] == NULL);
191410958Sdme@sun.com 
191510958Sdme@sun.com 	reqp->id = bdesc->id = hang_ix;
19165084Sjohnlev 	reqp->gref = bdesc->grant_ref;
191710958Sdme@sun.com 
191810958Sdme@sun.com 	xnfp->xnf_rx_pkt_info[hang_ix] = bdesc;
19195741Smrj 	xnfp->xnf_rx_ring.req_prod_pvt++;
192010958Sdme@sun.com 
192110958Sdme@sun.com 	xnfp->xnf_rx_new_buffers_posted = B_TRUE;
19225084Sjohnlev }
19235084Sjohnlev 
192410958Sdme@sun.com /*
192510958Sdme@sun.com  * Collect packets from the RX ring, storing them in `xnfp' for later
192610958Sdme@sun.com  * use.
192710958Sdme@sun.com  */
192810958Sdme@sun.com static void
xnf_rx_collect(xnf_t * xnfp)192910958Sdme@sun.com xnf_rx_collect(xnf_t *xnfp)
19305741Smrj {
193110958Sdme@sun.com 	mblk_t *head, *tail;
193210958Sdme@sun.com 
193310958Sdme@sun.com 	ASSERT(MUTEX_HELD(&xnfp->xnf_rxlock));
19345741Smrj 
19355741Smrj 	/*
193610958Sdme@sun.com 	 * Loop over unconsumed responses:
19375741Smrj 	 * 1. get a response
19385741Smrj 	 * 2. take corresponding buffer off recv. ring
19395741Smrj 	 * 3. indicate this by setting slot to NULL
19405741Smrj 	 * 4. create a new message and
19415741Smrj 	 * 5. copy data in, adjust ptr
19425741Smrj 	 */
19435741Smrj 
19445741Smrj 	head = tail = NULL;
19455741Smrj 
19465741Smrj 	while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_rx_ring)) {
194710958Sdme@sun.com 		netif_rx_response_t *rxpkt;
194810958Sdme@sun.com 		xnf_buf_t *bdesc;
194910958Sdme@sun.com 		ssize_t len;
195010958Sdme@sun.com 		size_t off;
195110958Sdme@sun.com 		mblk_t *mp = NULL;
195210958Sdme@sun.com 		boolean_t hwcsum = B_FALSE;
195310958Sdme@sun.com 		grant_ref_t ref;
19545741Smrj 
19555741Smrj 		/* 1. */
19565741Smrj 		rxpkt = RING_GET_RESPONSE(&xnfp->xnf_rx_ring,
19575741Smrj 		    xnfp->xnf_rx_ring.rsp_cons);
19585741Smrj 
195910958Sdme@sun.com 		DTRACE_PROBE4(xnf_rx_got_rsp, int, (int)rxpkt->id,
196010958Sdme@sun.com 		    int, (int)rxpkt->offset,
196110958Sdme@sun.com 		    int, (int)rxpkt->flags,
196210958Sdme@sun.com 		    int, (int)rxpkt->status);
19635741Smrj 
19645741Smrj 		/*
19655741Smrj 		 * 2.
196610958Sdme@sun.com 		 */
196710958Sdme@sun.com 		bdesc = xnfp->xnf_rx_pkt_info[rxpkt->id];
196810958Sdme@sun.com 
196910958Sdme@sun.com 		/*
197010958Sdme@sun.com 		 * 3.
19715741Smrj 		 */
197210958Sdme@sun.com 		xnfp->xnf_rx_pkt_info[rxpkt->id] = NULL;
19735741Smrj 		ASSERT(bdesc->id == rxpkt->id);
197410958Sdme@sun.com 
197510958Sdme@sun.com 		ref = bdesc->grant_ref;
197610958Sdme@sun.com 		off = rxpkt->offset;
197710958Sdme@sun.com 		len = rxpkt->status;
197810958Sdme@sun.com 
19796899Scz147101 		if (!xnfp->xnf_running) {
198010958Sdme@sun.com 			DTRACE_PROBE4(xnf_rx_not_running,
198110958Sdme@sun.com 			    int, rxpkt->status,
19826899Scz147101 			    char *, bdesc->buf, int, rxpkt->offset,
19836899Scz147101 			    char *, ((char *)bdesc->buf) + rxpkt->offset);
198410958Sdme@sun.com 
19856899Scz147101 			xnfp->xnf_stat_drop++;
198610958Sdme@sun.com 
198710958Sdme@sun.com 		} else if (len <= 0) {
198810958Sdme@sun.com 			DTRACE_PROBE4(xnf_rx_pkt_status_negative,
198910958Sdme@sun.com 			    int, rxpkt->status,
19905741Smrj 			    char *, bdesc->buf, int, rxpkt->offset,
19915741Smrj 			    char *, ((char *)bdesc->buf) + rxpkt->offset);
199210958Sdme@sun.com 
19935741Smrj 			xnfp->xnf_stat_errrx++;
199410958Sdme@sun.com 
199510958Sdme@sun.com 			switch (len) {
199610958Sdme@sun.com 			case 0:
19975741Smrj 				xnfp->xnf_stat_runt++;
199810958Sdme@sun.com 				break;
199910958Sdme@sun.com 			case NETIF_RSP_ERROR:
20005741Smrj 				xnfp->xnf_stat_mac_rcv_error++;
200110958Sdme@sun.com 				break;
200210958Sdme@sun.com 			case NETIF_RSP_DROPPED:
20035741Smrj 				xnfp->xnf_stat_norxbuf++;
200410958Sdme@sun.com 				break;
200510958Sdme@sun.com 			}
200610958Sdme@sun.com 
200710958Sdme@sun.com 		} else if (bdesc->grant_ref == INVALID_GRANT_REF) {
200810958Sdme@sun.com 			cmn_err(CE_WARN, "Bad rx grant reference %d "
200910958Sdme@sun.com 			    "from domain %d", ref,
201010958Sdme@sun.com 			    xvdi_get_oeid(xnfp->xnf_devinfo));
201110958Sdme@sun.com 
201210958Sdme@sun.com 		} else if ((off + len) > PAGESIZE) {
201310958Sdme@sun.com 			cmn_err(CE_WARN, "Rx packet overflows page "
201410958Sdme@sun.com 			    "(offset %ld, length %ld) from domain %d",
201510958Sdme@sun.com 			    off, len, xvdi_get_oeid(xnfp->xnf_devinfo));
20165741Smrj 		} else {
201710958Sdme@sun.com 			xnf_buf_t *nbuf = NULL;
201810958Sdme@sun.com 
201910958Sdme@sun.com 			DTRACE_PROBE4(xnf_rx_packet, int, len,
202010958Sdme@sun.com 			    char *, bdesc->buf, int, off,
202110958Sdme@sun.com 			    char *, ((char *)bdesc->buf) + off);
202210958Sdme@sun.com 
20235741Smrj 			ASSERT(off + len <= PAGEOFFSET);
202410958Sdme@sun.com 
20255741Smrj 			if (rxpkt->flags & NETRXF_data_validated)
20265741Smrj 				hwcsum = B_TRUE;
20275741Smrj 
20285741Smrj 			/*
202910958Sdme@sun.com 			 * If the packet is below a pre-determined
203010958Sdme@sun.com 			 * size we will copy data out rather than
203110958Sdme@sun.com 			 * replace it.
203210958Sdme@sun.com 			 */
203310958Sdme@sun.com 			if (len > xnf_rx_copy_limit)
203410958Sdme@sun.com 				nbuf = xnf_buf_get(xnfp, KM_NOSLEEP, B_FALSE);
203510958Sdme@sun.com 
203610958Sdme@sun.com 			/*
203710958Sdme@sun.com 			 * If we have a replacement buffer, attempt to
203810958Sdme@sun.com 			 * wrap the existing one with an mblk_t in
203910958Sdme@sun.com 			 * order that the upper layers of the stack
204010958Sdme@sun.com 			 * might use it directly.
20415741Smrj 			 */
204210958Sdme@sun.com 			if (nbuf != NULL) {
204310958Sdme@sun.com 				mp = desballoc((unsigned char *)bdesc->buf,
204410958Sdme@sun.com 				    bdesc->len, 0, &bdesc->free_rtn);
204510958Sdme@sun.com 				if (mp == NULL) {
204610958Sdme@sun.com 					xnfp->xnf_stat_rx_desballoc_fail++;
204710958Sdme@sun.com 					xnfp->xnf_stat_norxbuf++;
204810958Sdme@sun.com 
204910958Sdme@sun.com 					xnf_buf_put(xnfp, nbuf, B_FALSE);
205010958Sdme@sun.com 					nbuf = NULL;
205110958Sdme@sun.com 				} else {
205210958Sdme@sun.com 					mp->b_rptr = mp->b_rptr + off;
205310958Sdme@sun.com 					mp->b_wptr = mp->b_rptr + len;
205410958Sdme@sun.com 
205510958Sdme@sun.com 					/*
205610958Sdme@sun.com 					 * Release the grant reference
205710958Sdme@sun.com 					 * associated with this buffer
205810958Sdme@sun.com 					 * - they are scarce and the
205910958Sdme@sun.com 					 * upper layers of the stack
206010958Sdme@sun.com 					 * don't need it.
206110958Sdme@sun.com 					 */
206210958Sdme@sun.com 					(void) gnttab_end_foreign_access_ref(
206310958Sdme@sun.com 					    bdesc->grant_ref, 0);
206410958Sdme@sun.com 					gref_put(xnfp, bdesc->grant_ref);
206510958Sdme@sun.com 					bdesc->grant_ref = INVALID_GRANT_REF;
206610958Sdme@sun.com 
206710958Sdme@sun.com 					bdesc = nbuf;
206810958Sdme@sun.com 				}
20695741Smrj 			}
20705741Smrj 
207110958Sdme@sun.com 			if (nbuf == NULL) {
20725084Sjohnlev 				/*
207310958Sdme@sun.com 				 * No replacement buffer allocated -
207410958Sdme@sun.com 				 * attempt to copy the data out and
207510958Sdme@sun.com 				 * re-hang the existing buffer.
20765084Sjohnlev 				 */
207710958Sdme@sun.com 
207810958Sdme@sun.com 				/* 4. */
207910958Sdme@sun.com 				mp = allocb(len, BPRI_MED);
208010958Sdme@sun.com 				if (mp == NULL) {
208110958Sdme@sun.com 					xnfp->xnf_stat_rx_allocb_fail++;
20825741Smrj 					xnfp->xnf_stat_norxbuf++;
20835084Sjohnlev 				} else {
208410958Sdme@sun.com 					/* 5. */
20855084Sjohnlev 					bcopy(bdesc->buf + off, mp->b_wptr,
20865084Sjohnlev 					    len);
208710958Sdme@sun.com 					mp->b_wptr += len;
20885084Sjohnlev 				}
20895084Sjohnlev 			}
20905084Sjohnlev 		}
209110958Sdme@sun.com 
209210958Sdme@sun.com 		/* Re-hang the buffer. */
209310958Sdme@sun.com 		xnf_rxbuf_hang(xnfp, bdesc);
209410958Sdme@sun.com 
209510958Sdme@sun.com 		if (mp != NULL) {
20965084Sjohnlev 			if (hwcsum) {
20975084Sjohnlev 				/*
20985084Sjohnlev 				 * If the peer says that the data has
20995084Sjohnlev 				 * been validated then we declare that
21005084Sjohnlev 				 * the full checksum has been
21015084Sjohnlev 				 * verified.
21025084Sjohnlev 				 *
21035084Sjohnlev 				 * We don't look at the "checksum
21045084Sjohnlev 				 * blank" flag, and hence could have a
21055084Sjohnlev 				 * packet here that we are asserting
21065084Sjohnlev 				 * is good with a blank checksum.
21075084Sjohnlev 				 */
2108*11878SVenu.Iyer@Sun.COM 				mac_hcksum_set(mp, 0, 0, 0, 0,
2109*11878SVenu.Iyer@Sun.COM 				    HCK_FULLCKSUM_OK);
21105741Smrj 				xnfp->xnf_stat_rx_cksum_no_need++;
21115084Sjohnlev 			}
21125084Sjohnlev 			if (head == NULL) {
211310958Sdme@sun.com 				ASSERT(tail == NULL);
211410958Sdme@sun.com 
211510958Sdme@sun.com 				head = mp;
21165084Sjohnlev 			} else {
211710958Sdme@sun.com 				ASSERT(tail != NULL);
211810958Sdme@sun.com 
21195084Sjohnlev 				tail->b_next = mp;
21205084Sjohnlev 			}
212110958Sdme@sun.com 			tail = mp;
21225084Sjohnlev 
21235084Sjohnlev 			ASSERT(mp->b_next == NULL);
21245084Sjohnlev 
21255741Smrj 			xnfp->xnf_stat_ipackets++;
21265741Smrj 			xnfp->xnf_stat_rbytes += len;
21275084Sjohnlev 		}
21285084Sjohnlev 
21295741Smrj 		xnfp->xnf_rx_ring.rsp_cons++;
21305084Sjohnlev 	}
21315084Sjohnlev 
21325084Sjohnlev 	/*
213310958Sdme@sun.com 	 * Store the mblks we have collected.
21345084Sjohnlev 	 */
213510958Sdme@sun.com 	if (head != NULL) {
213610958Sdme@sun.com 		ASSERT(tail != NULL);
213710958Sdme@sun.com 
213810958Sdme@sun.com 		if (xnfp->xnf_rx_head == NULL) {
213910958Sdme@sun.com 			ASSERT(xnfp->xnf_rx_tail == NULL);
214010958Sdme@sun.com 
214110958Sdme@sun.com 			xnfp->xnf_rx_head = head;
214210958Sdme@sun.com 		} else {
214310958Sdme@sun.com 			ASSERT(xnfp->xnf_rx_tail != NULL);
214410958Sdme@sun.com 
214510958Sdme@sun.com 			xnfp->xnf_rx_tail->b_next = head;
21465084Sjohnlev 		}
214710958Sdme@sun.com 		xnfp->xnf_rx_tail = tail;
21485084Sjohnlev 	}
21495084Sjohnlev }
21505084Sjohnlev 
21515084Sjohnlev /*
21525084Sjohnlev  *  xnf_alloc_dma_resources() -- initialize the drivers structures
21535084Sjohnlev  */
21545084Sjohnlev static int
xnf_alloc_dma_resources(xnf_t * xnfp)21555084Sjohnlev xnf_alloc_dma_resources(xnf_t *xnfp)
21565084Sjohnlev {
21575741Smrj 	dev_info_t 		*devinfo = xnfp->xnf_devinfo;
21585084Sjohnlev 	size_t			len;
21595084Sjohnlev 	ddi_dma_cookie_t	dma_cookie;
21605084Sjohnlev 	uint_t			ncookies;
21615084Sjohnlev 	int			rc;
21625084Sjohnlev 	caddr_t			rptr;
21635084Sjohnlev 
21645084Sjohnlev 	/*
21655084Sjohnlev 	 * The code below allocates all the DMA data structures that
21665084Sjohnlev 	 * need to be released when the driver is detached.
21675084Sjohnlev 	 *
21685084Sjohnlev 	 * Allocate page for the transmit descriptor ring.
21695084Sjohnlev 	 */
21705084Sjohnlev 	if (ddi_dma_alloc_handle(devinfo, &ringbuf_dma_attr,
21715741Smrj 	    DDI_DMA_SLEEP, 0, &xnfp->xnf_tx_ring_dma_handle) != DDI_SUCCESS)
21725084Sjohnlev 		goto alloc_error;
21735084Sjohnlev 
21745741Smrj 	if (ddi_dma_mem_alloc(xnfp->xnf_tx_ring_dma_handle,
21755084Sjohnlev 	    PAGESIZE, &accattr, DDI_DMA_CONSISTENT,
21765084Sjohnlev 	    DDI_DMA_SLEEP, 0, &rptr, &len,
21775741Smrj 	    &xnfp->xnf_tx_ring_dma_acchandle) != DDI_SUCCESS) {
21785741Smrj 		ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
21795741Smrj 		xnfp->xnf_tx_ring_dma_handle = NULL;
21805084Sjohnlev 		goto alloc_error;
21815084Sjohnlev 	}
21825084Sjohnlev 
21835741Smrj 	if ((rc = ddi_dma_addr_bind_handle(xnfp->xnf_tx_ring_dma_handle, NULL,
21845084Sjohnlev 	    rptr, PAGESIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
21855084Sjohnlev 	    DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies)) != DDI_DMA_MAPPED) {
21865741Smrj 		ddi_dma_mem_free(&xnfp->xnf_tx_ring_dma_acchandle);
21875741Smrj 		ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
21885741Smrj 		xnfp->xnf_tx_ring_dma_handle = NULL;
21895741Smrj 		xnfp->xnf_tx_ring_dma_acchandle = NULL;
21905084Sjohnlev 		if (rc == DDI_DMA_NORESOURCES)
21915084Sjohnlev 			goto alloc_error;
21925084Sjohnlev 		else
21935084Sjohnlev 			goto error;
21945084Sjohnlev 	}
21955084Sjohnlev 
21965084Sjohnlev 	ASSERT(ncookies == 1);
21975084Sjohnlev 	bzero(rptr, PAGESIZE);
21985084Sjohnlev 	/* LINTED: constant in conditional context */
21995084Sjohnlev 	SHARED_RING_INIT((netif_tx_sring_t *)rptr);
22005084Sjohnlev 	/* LINTED: constant in conditional context */
22015741Smrj 	FRONT_RING_INIT(&xnfp->xnf_tx_ring, (netif_tx_sring_t *)rptr, PAGESIZE);
22025741Smrj 	xnfp->xnf_tx_ring_phys_addr = dma_cookie.dmac_laddress;
22035084Sjohnlev 
22045084Sjohnlev 	/*
22055084Sjohnlev 	 * Allocate page for the receive descriptor ring.
22065084Sjohnlev 	 */
22075084Sjohnlev 	if (ddi_dma_alloc_handle(devinfo, &ringbuf_dma_attr,
22085741Smrj 	    DDI_DMA_SLEEP, 0, &xnfp->xnf_rx_ring_dma_handle) != DDI_SUCCESS)
22095084Sjohnlev 		goto alloc_error;
22105084Sjohnlev 
22115741Smrj 	if (ddi_dma_mem_alloc(xnfp->xnf_rx_ring_dma_handle,
22125084Sjohnlev 	    PAGESIZE, &accattr, DDI_DMA_CONSISTENT,
22135084Sjohnlev 	    DDI_DMA_SLEEP, 0, &rptr, &len,
22145741Smrj 	    &xnfp->xnf_rx_ring_dma_acchandle) != DDI_SUCCESS) {
22155741Smrj 		ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
22165741Smrj 		xnfp->xnf_rx_ring_dma_handle = NULL;
22175084Sjohnlev 		goto alloc_error;
22185084Sjohnlev 	}
22195084Sjohnlev 
22205741Smrj 	if ((rc = ddi_dma_addr_bind_handle(xnfp->xnf_rx_ring_dma_handle, NULL,
22215084Sjohnlev 	    rptr, PAGESIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
22225084Sjohnlev 	    DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies)) != DDI_DMA_MAPPED) {
22235741Smrj 		ddi_dma_mem_free(&xnfp->xnf_rx_ring_dma_acchandle);
22245741Smrj 		ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
22255741Smrj 		xnfp->xnf_rx_ring_dma_handle = NULL;
22265741Smrj 		xnfp->xnf_rx_ring_dma_acchandle = NULL;
22275084Sjohnlev 		if (rc == DDI_DMA_NORESOURCES)
22285084Sjohnlev 			goto alloc_error;
22295084Sjohnlev 		else
22305084Sjohnlev 			goto error;
22315084Sjohnlev 	}
22325084Sjohnlev 
22335084Sjohnlev 	ASSERT(ncookies == 1);
22345084Sjohnlev 	bzero(rptr, PAGESIZE);
22355084Sjohnlev 	/* LINTED: constant in conditional context */
22365084Sjohnlev 	SHARED_RING_INIT((netif_rx_sring_t *)rptr);
22375084Sjohnlev 	/* LINTED: constant in conditional context */
22385741Smrj 	FRONT_RING_INIT(&xnfp->xnf_rx_ring, (netif_rx_sring_t *)rptr, PAGESIZE);
22395741Smrj 	xnfp->xnf_rx_ring_phys_addr = dma_cookie.dmac_laddress;
22405084Sjohnlev 
22415084Sjohnlev 	return (DDI_SUCCESS);
22425084Sjohnlev 
22435084Sjohnlev alloc_error:
22445084Sjohnlev 	cmn_err(CE_WARN, "xnf%d: could not allocate enough DMA memory",
22455741Smrj 	    ddi_get_instance(xnfp->xnf_devinfo));
22465084Sjohnlev error:
22475084Sjohnlev 	xnf_release_dma_resources(xnfp);
22485084Sjohnlev 	return (DDI_FAILURE);
22495084Sjohnlev }
22505084Sjohnlev 
22515084Sjohnlev /*
22525084Sjohnlev  * Release all DMA resources in the opposite order from acquisition
22535084Sjohnlev  */
22545084Sjohnlev static void
xnf_release_dma_resources(xnf_t * xnfp)22555084Sjohnlev xnf_release_dma_resources(xnf_t *xnfp)
22565084Sjohnlev {
22575084Sjohnlev 	int i;
22585084Sjohnlev 
22595084Sjohnlev 	/*
22605084Sjohnlev 	 * Free receive buffers which are currently associated with
226110958Sdme@sun.com 	 * descriptors.
22625084Sjohnlev 	 */
226310958Sdme@sun.com 	mutex_enter(&xnfp->xnf_rxlock);
226410958Sdme@sun.com 	for (i = 0; i < NET_RX_RING_SIZE; i++) {
226510958Sdme@sun.com 		xnf_buf_t *bp;
226610958Sdme@sun.com 
226710958Sdme@sun.com 		if ((bp = xnfp->xnf_rx_pkt_info[i]) == NULL)
22685084Sjohnlev 			continue;
226910958Sdme@sun.com 		xnfp->xnf_rx_pkt_info[i] = NULL;
227010958Sdme@sun.com 		xnf_buf_put(xnfp, bp, B_FALSE);
22715084Sjohnlev 	}
227210958Sdme@sun.com 	mutex_exit(&xnfp->xnf_rxlock);
227310958Sdme@sun.com 
227410958Sdme@sun.com 	/* Free the receive ring buffer. */
22755741Smrj 	if (xnfp->xnf_rx_ring_dma_acchandle != NULL) {
22765741Smrj 		(void) ddi_dma_unbind_handle(xnfp->xnf_rx_ring_dma_handle);
22775741Smrj 		ddi_dma_mem_free(&xnfp->xnf_rx_ring_dma_acchandle);
22785741Smrj 		ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
22795741Smrj 		xnfp->xnf_rx_ring_dma_acchandle = NULL;
22805084Sjohnlev 	}
228110958Sdme@sun.com 	/* Free the transmit ring buffer. */
22825741Smrj 	if (xnfp->xnf_tx_ring_dma_acchandle != NULL) {
22835741Smrj 		(void) ddi_dma_unbind_handle(xnfp->xnf_tx_ring_dma_handle);
22845741Smrj 		ddi_dma_mem_free(&xnfp->xnf_tx_ring_dma_acchandle);
22855741Smrj 		ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
22865741Smrj 		xnfp->xnf_tx_ring_dma_acchandle = NULL;
22875084Sjohnlev 	}
22886899Scz147101 
22895084Sjohnlev }
22905084Sjohnlev 
22915084Sjohnlev /*
229210958Sdme@sun.com  * Release any packets and associated structures used by the TX ring.
22935084Sjohnlev  */
22945084Sjohnlev static void
xnf_release_mblks(xnf_t * xnfp)229510958Sdme@sun.com xnf_release_mblks(xnf_t *xnfp)
22965084Sjohnlev {
229710958Sdme@sun.com 	RING_IDX i;
229810958Sdme@sun.com 	xnf_txid_t *tidp;
229910958Sdme@sun.com 
230010958Sdme@sun.com 	for (i = 0, tidp = &xnfp->xnf_tx_pkt_id[0];
230110958Sdme@sun.com 	    i < NET_TX_RING_SIZE;
230210958Sdme@sun.com 	    i++, tidp++) {
230310958Sdme@sun.com 		xnf_txbuf_t *txp = tidp->txbuf;
230410958Sdme@sun.com 
230510958Sdme@sun.com 		if (txp != NULL) {
230610958Sdme@sun.com 			ASSERT(txp->tx_mp != NULL);
230710958Sdme@sun.com 			freemsg(txp->tx_mp);
230810958Sdme@sun.com 
230910958Sdme@sun.com 			txid_put(xnfp, tidp);
231010958Sdme@sun.com 			kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
231110958Sdme@sun.com 		}
23125741Smrj 	}
23135084Sjohnlev }
23145084Sjohnlev 
231510958Sdme@sun.com static int
xnf_buf_constructor(void * buf,void * arg,int kmflag)231610958Sdme@sun.com xnf_buf_constructor(void *buf, void *arg, int kmflag)
23175084Sjohnlev {
231810958Sdme@sun.com 	int (*ddiflags)(caddr_t) = DDI_DMA_SLEEP;
231910958Sdme@sun.com 	xnf_buf_t *bdesc = buf;
232010958Sdme@sun.com 	xnf_t *xnfp = arg;
232110958Sdme@sun.com 	ddi_dma_cookie_t dma_cookie;
232210958Sdme@sun.com 	uint_t ncookies;
23235084Sjohnlev 	size_t len;
23245084Sjohnlev 
232510958Sdme@sun.com 	if (kmflag & KM_NOSLEEP)
232610958Sdme@sun.com 		ddiflags = DDI_DMA_DONTWAIT;
232710958Sdme@sun.com 
232810958Sdme@sun.com 	/* Allocate a DMA access handle for the buffer. */
232910958Sdme@sun.com 	if (ddi_dma_alloc_handle(xnfp->xnf_devinfo, &buf_dma_attr,
233010958Sdme@sun.com 	    ddiflags, 0, &bdesc->dma_handle) != DDI_SUCCESS)
23315084Sjohnlev 		goto failure;
23325084Sjohnlev 
233310958Sdme@sun.com 	/* Allocate DMA-able memory for buffer. */
23345084Sjohnlev 	if (ddi_dma_mem_alloc(bdesc->dma_handle,
233510958Sdme@sun.com 	    PAGESIZE, &data_accattr, DDI_DMA_STREAMING, ddiflags, 0,
23365084Sjohnlev 	    &bdesc->buf, &len, &bdesc->acc_handle) != DDI_SUCCESS)
23375741Smrj 		goto failure_1;
23385084Sjohnlev 
233910958Sdme@sun.com 	/* Bind to virtual address of buffer to get physical address. */
234010958Sdme@sun.com 	if (ddi_dma_addr_bind_handle(bdesc->dma_handle, NULL,
234110958Sdme@sun.com 	    bdesc->buf, len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
234210958Sdme@sun.com 	    ddiflags, 0, &dma_cookie, &ncookies) != DDI_DMA_MAPPED)
234310958Sdme@sun.com 		goto failure_2;
234410958Sdme@sun.com 	ASSERT(ncookies == 1);
234510958Sdme@sun.com 
234610958Sdme@sun.com 	bdesc->free_rtn.free_func = xnf_buf_recycle;
234710958Sdme@sun.com 	bdesc->free_rtn.free_arg = (caddr_t)bdesc;
23485084Sjohnlev 	bdesc->xnfp = xnfp;
23495084Sjohnlev 	bdesc->buf_phys = dma_cookie.dmac_laddress;
235010958Sdme@sun.com 	bdesc->buf_mfn = pfn_to_mfn(xnf_btop(bdesc->buf_phys));
235110958Sdme@sun.com 	bdesc->len = dma_cookie.dmac_size;
235210958Sdme@sun.com 	bdesc->grant_ref = INVALID_GRANT_REF;
235310958Sdme@sun.com 	bdesc->gen = xnfp->xnf_gen;
235410958Sdme@sun.com 
235510958Sdme@sun.com 	atomic_add_64(&xnfp->xnf_stat_buf_allocated, 1);
235610958Sdme@sun.com 
235710958Sdme@sun.com 	return (0);
23585084Sjohnlev 
23595741Smrj failure_2:
23605084Sjohnlev 	ddi_dma_mem_free(&bdesc->acc_handle);
23615084Sjohnlev 
23625741Smrj failure_1:
23635084Sjohnlev 	ddi_dma_free_handle(&bdesc->dma_handle);
23645084Sjohnlev 
23655084Sjohnlev failure:
236610958Sdme@sun.com 
236711588Sdavid.edmondson@sun.com 	ASSERT(kmflag & KM_NOSLEEP); /* Cannot fail for KM_SLEEP. */
236810958Sdme@sun.com 	return (-1);
236910958Sdme@sun.com }
237010958Sdme@sun.com 
237110958Sdme@sun.com static void
xnf_buf_destructor(void * buf,void * arg)237210958Sdme@sun.com xnf_buf_destructor(void *buf, void *arg)
237310958Sdme@sun.com {
237410958Sdme@sun.com 	xnf_buf_t *bdesc = buf;
237510958Sdme@sun.com 	xnf_t *xnfp = arg;
237610958Sdme@sun.com 
237710958Sdme@sun.com 	(void) ddi_dma_unbind_handle(bdesc->dma_handle);
237810958Sdme@sun.com 	ddi_dma_mem_free(&bdesc->acc_handle);
237910958Sdme@sun.com 	ddi_dma_free_handle(&bdesc->dma_handle);
238010958Sdme@sun.com 
238110958Sdme@sun.com 	atomic_add_64(&xnfp->xnf_stat_buf_allocated, -1);
238210958Sdme@sun.com }
238310958Sdme@sun.com 
238410958Sdme@sun.com static xnf_buf_t *
xnf_buf_get(xnf_t * xnfp,int flags,boolean_t readonly)238510958Sdme@sun.com xnf_buf_get(xnf_t *xnfp, int flags, boolean_t readonly)
238610958Sdme@sun.com {
238710958Sdme@sun.com 	grant_ref_t gref;
238810958Sdme@sun.com 	xnf_buf_t *bufp;
238910958Sdme@sun.com 
239010958Sdme@sun.com 	/*
239110958Sdme@sun.com 	 * Usually grant references are more scarce than memory, so we
239210958Sdme@sun.com 	 * attempt to acquire a grant reference first.
239310958Sdme@sun.com 	 */
239410958Sdme@sun.com 	gref = gref_get(xnfp);
239510958Sdme@sun.com 	if (gref == INVALID_GRANT_REF)
239610958Sdme@sun.com 		return (NULL);
239710958Sdme@sun.com 
239810958Sdme@sun.com 	bufp = kmem_cache_alloc(xnfp->xnf_buf_cache, flags);
239910958Sdme@sun.com 	if (bufp == NULL) {
240010958Sdme@sun.com 		gref_put(xnfp, gref);
240110958Sdme@sun.com 		return (NULL);
240210958Sdme@sun.com 	}
240310958Sdme@sun.com 
240410958Sdme@sun.com 	ASSERT(bufp->grant_ref == INVALID_GRANT_REF);
240510958Sdme@sun.com 
240610958Sdme@sun.com 	bufp->grant_ref = gref;
240710958Sdme@sun.com 
240810958Sdme@sun.com 	if (bufp->gen != xnfp->xnf_gen)
240910958Sdme@sun.com 		xnf_buf_refresh(bufp);
241010958Sdme@sun.com 
241110958Sdme@sun.com 	gnttab_grant_foreign_access_ref(bufp->grant_ref,
241210958Sdme@sun.com 	    xvdi_get_oeid(bufp->xnfp->xnf_devinfo),
241310958Sdme@sun.com 	    bufp->buf_mfn, readonly ? 1 : 0);
241410958Sdme@sun.com 
241510958Sdme@sun.com 	atomic_add_64(&xnfp->xnf_stat_buf_outstanding, 1);
241610958Sdme@sun.com 
241710958Sdme@sun.com 	return (bufp);
241810958Sdme@sun.com }
241910958Sdme@sun.com 
242010958Sdme@sun.com static void
xnf_buf_put(xnf_t * xnfp,xnf_buf_t * bufp,boolean_t readonly)242110958Sdme@sun.com xnf_buf_put(xnf_t *xnfp, xnf_buf_t *bufp, boolean_t readonly)
242210958Sdme@sun.com {
242310958Sdme@sun.com 	if (bufp->grant_ref != INVALID_GRANT_REF) {
242410958Sdme@sun.com 		(void) gnttab_end_foreign_access_ref(
242510958Sdme@sun.com 		    bufp->grant_ref, readonly ? 1 : 0);
242610958Sdme@sun.com 		gref_put(xnfp, bufp->grant_ref);
242710958Sdme@sun.com 		bufp->grant_ref = INVALID_GRANT_REF;
242810958Sdme@sun.com 	}
242910958Sdme@sun.com 
243010958Sdme@sun.com 	kmem_cache_free(xnfp->xnf_buf_cache, bufp);
243110958Sdme@sun.com 
243210958Sdme@sun.com 	atomic_add_64(&xnfp->xnf_stat_buf_outstanding, -1);
243310958Sdme@sun.com }
243410958Sdme@sun.com 
243510958Sdme@sun.com /*
243610958Sdme@sun.com  * Refresh any cached data about a buffer after resume.
243710958Sdme@sun.com  */
243810958Sdme@sun.com static void
xnf_buf_refresh(xnf_buf_t * bdesc)243910958Sdme@sun.com xnf_buf_refresh(xnf_buf_t *bdesc)
244010958Sdme@sun.com {
244110958Sdme@sun.com 	bdesc->buf_mfn = pfn_to_mfn(xnf_btop(bdesc->buf_phys));
244210958Sdme@sun.com 	bdesc->gen = bdesc->xnfp->xnf_gen;
244310958Sdme@sun.com }
244410958Sdme@sun.com 
244510958Sdme@sun.com /*
244610958Sdme@sun.com  * Streams `freeb' routine for `xnf_buf_t' when used as transmit
244710958Sdme@sun.com  * look-aside buffers.
244810958Sdme@sun.com  */
244910958Sdme@sun.com static void
xnf_buf_recycle(xnf_buf_t * bdesc)245010958Sdme@sun.com xnf_buf_recycle(xnf_buf_t *bdesc)
245110958Sdme@sun.com {
245210958Sdme@sun.com 	xnf_t *xnfp = bdesc->xnfp;
245310958Sdme@sun.com 
245410958Sdme@sun.com 	xnf_buf_put(xnfp, bdesc, B_TRUE);
245510958Sdme@sun.com }
245610958Sdme@sun.com 
245710958Sdme@sun.com static int
xnf_tx_buf_constructor(void * buf,void * arg,int kmflag)245810958Sdme@sun.com xnf_tx_buf_constructor(void *buf, void *arg, int kmflag)
245910958Sdme@sun.com {
246011588Sdavid.edmondson@sun.com 	int (*ddiflags)(caddr_t) = DDI_DMA_SLEEP;
246110958Sdme@sun.com 	xnf_txbuf_t *txp = buf;
246210958Sdme@sun.com 	xnf_t *xnfp = arg;
246310958Sdme@sun.com 
246411588Sdavid.edmondson@sun.com 	if (kmflag & KM_NOSLEEP)
246511588Sdavid.edmondson@sun.com 		ddiflags = DDI_DMA_DONTWAIT;
246611588Sdavid.edmondson@sun.com 
246710958Sdme@sun.com 	if (ddi_dma_alloc_handle(xnfp->xnf_devinfo, &buf_dma_attr,
246811588Sdavid.edmondson@sun.com 	    ddiflags, 0, &txp->tx_dma_handle) != DDI_SUCCESS) {
246911588Sdavid.edmondson@sun.com 		ASSERT(kmflag & KM_NOSLEEP); /* Cannot fail for KM_SLEEP. */
247010958Sdme@sun.com 		return (-1);
247111588Sdavid.edmondson@sun.com 	}
247210958Sdme@sun.com 
247310958Sdme@sun.com 	return (0);
247410958Sdme@sun.com }
247510958Sdme@sun.com 
247610958Sdme@sun.com static void
xnf_tx_buf_destructor(void * buf,void * arg)247710958Sdme@sun.com xnf_tx_buf_destructor(void *buf, void *arg)
247810958Sdme@sun.com {
247910958Sdme@sun.com 	_NOTE(ARGUNUSED(arg));
248010958Sdme@sun.com 	xnf_txbuf_t *txp = buf;
248110958Sdme@sun.com 
248210958Sdme@sun.com 	ddi_dma_free_handle(&txp->tx_dma_handle);
24835084Sjohnlev }
24845084Sjohnlev 
24855741Smrj /*
24865741Smrj  * Statistics.
24875741Smrj  */
24885741Smrj static char *xnf_aux_statistics[] = {
24895741Smrj 	"tx_cksum_deferred",
24905741Smrj 	"rx_cksum_no_need",
24915741Smrj 	"interrupts",
24925741Smrj 	"unclaimed_interrupts",
24935741Smrj 	"tx_pullup",
24945741Smrj 	"tx_pagebndry",
24955741Smrj 	"tx_attempt",
249610958Sdme@sun.com 	"buf_allocated",
249710958Sdme@sun.com 	"buf_outstanding",
249810958Sdme@sun.com 	"gref_outstanding",
249910958Sdme@sun.com 	"gref_failure",
250010958Sdme@sun.com 	"gref_peak",
250110958Sdme@sun.com 	"rx_allocb_fail",
250210958Sdme@sun.com 	"rx_desballoc_fail",
25035741Smrj };
25045741Smrj 
25055741Smrj static int
xnf_kstat_aux_update(kstat_t * ksp,int flag)25065741Smrj xnf_kstat_aux_update(kstat_t *ksp, int flag)
25075741Smrj {
25085741Smrj 	xnf_t *xnfp;
25095741Smrj 	kstat_named_t *knp;
25105741Smrj 
25115741Smrj 	if (flag != KSTAT_READ)
25125741Smrj 		return (EACCES);
25135741Smrj 
25145741Smrj 	xnfp = ksp->ks_private;
25155741Smrj 	knp = ksp->ks_data;
25165741Smrj 
25175741Smrj 	/*
25185741Smrj 	 * Assignment order must match that of the names in
25195741Smrj 	 * xnf_aux_statistics.
25205741Smrj 	 */
25215741Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_tx_cksum_deferred;
25225741Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_rx_cksum_no_need;
25235741Smrj 
25245741Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_interrupts;
25255741Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_unclaimed_interrupts;
25265741Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_tx_pullup;
25275741Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_tx_pagebndry;
25285741Smrj 	(knp++)->value.ui64 = xnfp->xnf_stat_tx_attempt;
252910958Sdme@sun.com 
253010958Sdme@sun.com 	(knp++)->value.ui64 = xnfp->xnf_stat_buf_allocated;
253110958Sdme@sun.com 	(knp++)->value.ui64 = xnfp->xnf_stat_buf_outstanding;
253210958Sdme@sun.com 	(knp++)->value.ui64 = xnfp->xnf_stat_gref_outstanding;
253310958Sdme@sun.com 	(knp++)->value.ui64 = xnfp->xnf_stat_gref_failure;
253410958Sdme@sun.com 	(knp++)->value.ui64 = xnfp->xnf_stat_gref_peak;
253510958Sdme@sun.com 	(knp++)->value.ui64 = xnfp->xnf_stat_rx_allocb_fail;
253610958Sdme@sun.com 	(knp++)->value.ui64 = xnfp->xnf_stat_rx_desballoc_fail;
25375741Smrj 
25385741Smrj 	return (0);
25395741Smrj }
25405741Smrj 
25415741Smrj static boolean_t
xnf_kstat_init(xnf_t * xnfp)25425741Smrj xnf_kstat_init(xnf_t *xnfp)
25435741Smrj {
25445741Smrj 	int nstat = sizeof (xnf_aux_statistics) /
25455741Smrj 	    sizeof (xnf_aux_statistics[0]);
25465741Smrj 	char **cp = xnf_aux_statistics;
25475741Smrj 	kstat_named_t *knp;
25485741Smrj 
25495741Smrj 	/*
25505741Smrj 	 * Create and initialise kstats.
25515741Smrj 	 */
25525741Smrj 	if ((xnfp->xnf_kstat_aux = kstat_create("xnf",
25535741Smrj 	    ddi_get_instance(xnfp->xnf_devinfo),
25545741Smrj 	    "aux_statistics", "net", KSTAT_TYPE_NAMED,
25555741Smrj 	    nstat, 0)) == NULL)
25565741Smrj 		return (B_FALSE);
25575741Smrj 
25585741Smrj 	xnfp->xnf_kstat_aux->ks_private = xnfp;
25595741Smrj 	xnfp->xnf_kstat_aux->ks_update = xnf_kstat_aux_update;
25605741Smrj 
25615741Smrj 	knp = xnfp->xnf_kstat_aux->ks_data;
25625741Smrj 	while (nstat > 0) {
25635741Smrj 		kstat_named_init(knp, *cp, KSTAT_DATA_UINT64);
25645741Smrj 
25655741Smrj 		knp++;
25665741Smrj 		cp++;
25675741Smrj 		nstat--;
25685741Smrj 	}
25695741Smrj 
25705741Smrj 	kstat_install(xnfp->xnf_kstat_aux);
25715741Smrj 
25725741Smrj 	return (B_TRUE);
25735741Smrj }
25745741Smrj 
25755084Sjohnlev static int
xnf_stat(void * arg,uint_t stat,uint64_t * val)25765084Sjohnlev xnf_stat(void *arg, uint_t stat, uint64_t *val)
25775084Sjohnlev {
25785084Sjohnlev 	xnf_t *xnfp = arg;
25795084Sjohnlev 
258010958Sdme@sun.com 	mutex_enter(&xnfp->xnf_rxlock);
25815741Smrj 	mutex_enter(&xnfp->xnf_txlock);
25825084Sjohnlev 
25835741Smrj #define	mac_stat(q, r)				\
25845084Sjohnlev 	case (MAC_STAT_##q):			\
25855741Smrj 		*val = xnfp->xnf_stat_##r;	\
25865741Smrj 		break
25875741Smrj 
25885741Smrj #define	ether_stat(q, r)			\
25895741Smrj 	case (ETHER_STAT_##q):			\
25905741Smrj 		*val = xnfp->xnf_stat_##r;	\
25915084Sjohnlev 		break
25925084Sjohnlev 
25935084Sjohnlev 	switch (stat) {
25945084Sjohnlev 
25955741Smrj 	mac_stat(IPACKETS, ipackets);
25965741Smrj 	mac_stat(OPACKETS, opackets);
25975741Smrj 	mac_stat(RBYTES, rbytes);
25985741Smrj 	mac_stat(OBYTES, obytes);
25995741Smrj 	mac_stat(NORCVBUF, norxbuf);
26005741Smrj 	mac_stat(IERRORS, errrx);
26015741Smrj 	mac_stat(NOXMTBUF, tx_defer);
26025741Smrj 
26035741Smrj 	ether_stat(MACRCV_ERRORS, mac_rcv_error);
26045741Smrj 	ether_stat(TOOSHORT_ERRORS, runt);
26055084Sjohnlev 
26067397SMax.Zhen@Sun.COM 	/* always claim to be in full duplex mode */
26077397SMax.Zhen@Sun.COM 	case ETHER_STAT_LINK_DUPLEX:
26087397SMax.Zhen@Sun.COM 		*val = LINK_DUPLEX_FULL;
26097397SMax.Zhen@Sun.COM 		break;
26107397SMax.Zhen@Sun.COM 
26117397SMax.Zhen@Sun.COM 	/* always claim to be at 1Gb/s link speed */
26127397SMax.Zhen@Sun.COM 	case MAC_STAT_IFSPEED:
26137397SMax.Zhen@Sun.COM 		*val = 1000000000ull;
26147397SMax.Zhen@Sun.COM 		break;
26157397SMax.Zhen@Sun.COM 
26165084Sjohnlev 	default:
26175741Smrj 		mutex_exit(&xnfp->xnf_txlock);
261810958Sdme@sun.com 		mutex_exit(&xnfp->xnf_rxlock);
26195084Sjohnlev 
26205084Sjohnlev 		return (ENOTSUP);
26215084Sjohnlev 	}
26225084Sjohnlev 
26235741Smrj #undef mac_stat
26245741Smrj #undef ether_stat
26255084Sjohnlev 
26265741Smrj 	mutex_exit(&xnfp->xnf_txlock);
262710958Sdme@sun.com 	mutex_exit(&xnfp->xnf_rxlock);
26285084Sjohnlev 
26295084Sjohnlev 	return (0);
26305084Sjohnlev }
26315084Sjohnlev 
26325084Sjohnlev static boolean_t
xnf_getcapab(void * arg,mac_capab_t cap,void * cap_data)26335084Sjohnlev xnf_getcapab(void *arg, mac_capab_t cap, void *cap_data)
26345084Sjohnlev {
263510958Sdme@sun.com 	_NOTE(ARGUNUSED(arg));
26365084Sjohnlev 
26375084Sjohnlev 	switch (cap) {
26385084Sjohnlev 	case MAC_CAPAB_HCKSUM: {
26395084Sjohnlev 		uint32_t *capab = cap_data;
26405084Sjohnlev 
26415702Sdme 		/*
26427351Sdme@sun.com 		 * Whilst the flag used to communicate with the IO
26437351Sdme@sun.com 		 * domain is called "NETTXF_csum_blank", the checksum
26447351Sdme@sun.com 		 * in the packet must contain the pseudo-header
26457351Sdme@sun.com 		 * checksum and not zero.
26465702Sdme 		 *
26477351Sdme@sun.com 		 * To help out the IO domain, we might use
26487351Sdme@sun.com 		 * HCKSUM_INET_PARTIAL. Unfortunately our stack will
26497351Sdme@sun.com 		 * then use checksum offload for IPv6 packets, which
26507351Sdme@sun.com 		 * the IO domain can't handle.
26517351Sdme@sun.com 		 *
26527351Sdme@sun.com 		 * As a result, we declare outselves capable of
26537351Sdme@sun.com 		 * HCKSUM_INET_FULL_V4. This means that we receive
26547351Sdme@sun.com 		 * IPv4 packets from the stack with a blank checksum
26557351Sdme@sun.com 		 * field and must insert the pseudo-header checksum
26567351Sdme@sun.com 		 * before passing the packet to the IO domain.
26575702Sdme 		 */
265810958Sdme@sun.com 		*capab = HCKSUM_INET_FULL_V4;
26595084Sjohnlev 		break;
26605084Sjohnlev 	}
26615084Sjohnlev 	default:
26625084Sjohnlev 		return (B_FALSE);
26635084Sjohnlev 	}
26645084Sjohnlev 
26655084Sjohnlev 	return (B_TRUE);
26665084Sjohnlev }
26675084Sjohnlev 
266810958Sdme@sun.com /*
266910958Sdme@sun.com  * The state of the peer has changed - react accordingly.
267010958Sdme@sun.com  */
26715084Sjohnlev static void
oe_state_change(dev_info_t * dip,ddi_eventcookie_t id,void * arg,void * impl_data)26725084Sjohnlev oe_state_change(dev_info_t *dip, ddi_eventcookie_t id,
26735084Sjohnlev     void *arg, void *impl_data)
26745084Sjohnlev {
267510958Sdme@sun.com 	_NOTE(ARGUNUSED(id, arg));
26765084Sjohnlev 	xnf_t *xnfp = ddi_get_driver_private(dip);
26775084Sjohnlev 	XenbusState new_state = *(XenbusState *)impl_data;
26785084Sjohnlev 
26795084Sjohnlev 	ASSERT(xnfp != NULL);
26805084Sjohnlev 
26815084Sjohnlev 	switch (new_state) {
268210958Sdme@sun.com 	case XenbusStateUnknown:
268310958Sdme@sun.com 	case XenbusStateInitialising:
268410958Sdme@sun.com 	case XenbusStateInitialised:
268510958Sdme@sun.com 	case XenbusStateClosing:
268610958Sdme@sun.com 	case XenbusStateClosed:
268710958Sdme@sun.com 	case XenbusStateReconfiguring:
268810958Sdme@sun.com 	case XenbusStateReconfigured:
268910958Sdme@sun.com 		break;
269010958Sdme@sun.com 
269110958Sdme@sun.com 	case XenbusStateInitWait:
269210958Sdme@sun.com 		xnf_read_config(xnfp);
269310958Sdme@sun.com 
269410958Sdme@sun.com 		if (!xnfp->xnf_be_rx_copy) {
269510958Sdme@sun.com 			cmn_err(CE_WARN,
269610958Sdme@sun.com 			    "The xnf driver requires a dom0 that "
269710958Sdme@sun.com 			    "supports 'feature-rx-copy'.");
269810958Sdme@sun.com 			(void) xvdi_switch_state(xnfp->xnf_devinfo,
269910958Sdme@sun.com 			    XBT_NULL, XenbusStateClosed);
270010958Sdme@sun.com 			break;
270110958Sdme@sun.com 		}
270210958Sdme@sun.com 
270310958Sdme@sun.com 		/*
270410958Sdme@sun.com 		 * Connect to the backend.
270510958Sdme@sun.com 		 */
270610958Sdme@sun.com 		xnf_be_connect(xnfp);
270710958Sdme@sun.com 
270810958Sdme@sun.com 		/*
270910958Sdme@sun.com 		 * Our MAC address as discovered by xnf_read_config().
271010958Sdme@sun.com 		 */
271110958Sdme@sun.com 		mac_unicst_update(xnfp->xnf_mh, xnfp->xnf_mac_addr);
271210958Sdme@sun.com 
271310958Sdme@sun.com 		break;
271410958Sdme@sun.com 
27155084Sjohnlev 	case XenbusStateConnected:
271610958Sdme@sun.com 		mutex_enter(&xnfp->xnf_rxlock);
27175741Smrj 		mutex_enter(&xnfp->xnf_txlock);
27185084Sjohnlev 
27195741Smrj 		xnfp->xnf_connected = B_TRUE;
27206899Scz147101 		/*
272110958Sdme@sun.com 		 * Wake up any threads waiting to send data to
272210958Sdme@sun.com 		 * backend.
27236899Scz147101 		 */
272410958Sdme@sun.com 		cv_broadcast(&xnfp->xnf_cv_state);
27255084Sjohnlev 
27265741Smrj 		mutex_exit(&xnfp->xnf_txlock);
272710958Sdme@sun.com 		mutex_exit(&xnfp->xnf_rxlock);
27285084Sjohnlev 
27296899Scz147101 		/*
273010958Sdme@sun.com 		 * Kick the peer in case it missed any transmits
273110958Sdme@sun.com 		 * request in the TX ring.
27326899Scz147101 		 */
27335741Smrj 		ec_notify_via_evtchn(xnfp->xnf_evtchn);
27346899Scz147101 
27356899Scz147101 		/*
273610958Sdme@sun.com 		 * There may already be completed receive requests in
273710958Sdme@sun.com 		 * the ring sent by backend after it gets connected
273810958Sdme@sun.com 		 * but before we see its state change here, so we call
273910958Sdme@sun.com 		 * xnf_intr() to handle them, if any.
27406899Scz147101 		 */
27416899Scz147101 		(void) xnf_intr((caddr_t)xnfp);
27426899Scz147101 
274310958Sdme@sun.com 		/*
274410958Sdme@sun.com 		 * Mark the link up now that we are connected.
274510958Sdme@sun.com 		 */
27467397SMax.Zhen@Sun.COM 		mac_link_update(xnfp->xnf_mh, LINK_STATE_UP);
27477397SMax.Zhen@Sun.COM 
274810958Sdme@sun.com 		/*
274910958Sdme@sun.com 		 * Tell the backend about the multicast addresses in
275010958Sdme@sun.com 		 * which we are interested.
275110958Sdme@sun.com 		 */
275210958Sdme@sun.com 		mac_multicast_refresh(xnfp->xnf_mh, NULL, xnfp, B_TRUE);
275310958Sdme@sun.com 
27545084Sjohnlev 		break;
27555084Sjohnlev 
27565084Sjohnlev 	default:
27575084Sjohnlev 		break;
27585084Sjohnlev 	}
27595084Sjohnlev }
2760