xref: /illumos-gate/usr/src/uts/common/xen/os/xvdi.c (revision 7f0b8309)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev 
22843e1988Sjohnlev /*
23*7f0b8309SEdward Pilatowicz  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
27843e1988Sjohnlev /*
28843e1988Sjohnlev  * Xen virtual device driver interfaces
29843e1988Sjohnlev  */
30843e1988Sjohnlev 
31843e1988Sjohnlev /*
32843e1988Sjohnlev  * todo:
33843e1988Sjohnlev  * + name space clean up:
34843e1988Sjohnlev  *	xvdi_* - public xen interfaces, for use by all leaf drivers
35843e1988Sjohnlev  *	xd_* - public xen data structures
36843e1988Sjohnlev  *	i_xvdi_* - implementation private functions
37843e1988Sjohnlev  *	xendev_* - xendev driver interfaces, both internal and in cb_ops/bus_ops
38843e1988Sjohnlev  * + add mdb dcmds to dump ring status
39843e1988Sjohnlev  * + implement xvdi_xxx to wrap xenbus_xxx read/write function
40843e1988Sjohnlev  * + convert (xendev_ring_t *) into xvdi_ring_handle_t
41843e1988Sjohnlev  */
42843e1988Sjohnlev #include <sys/conf.h>
43843e1988Sjohnlev #include <sys/param.h>
44843e1988Sjohnlev #include <sys/kmem.h>
45843e1988Sjohnlev #include <vm/seg_kmem.h>
46843e1988Sjohnlev #include <sys/debug.h>
47843e1988Sjohnlev #include <sys/modctl.h>
48843e1988Sjohnlev #include <sys/autoconf.h>
49843e1988Sjohnlev #include <sys/ddi_impldefs.h>
50843e1988Sjohnlev #include <sys/ddi_subrdefs.h>
51843e1988Sjohnlev #include <sys/ddi.h>
52843e1988Sjohnlev #include <sys/sunddi.h>
53843e1988Sjohnlev #include <sys/sunndi.h>
54843e1988Sjohnlev #include <sys/sunldi.h>
55843e1988Sjohnlev #include <sys/fs/dv_node.h>
56843e1988Sjohnlev #include <sys/avintr.h>
57843e1988Sjohnlev #include <sys/psm.h>
58843e1988Sjohnlev #include <sys/spl.h>
59843e1988Sjohnlev #include <sys/promif.h>
60843e1988Sjohnlev #include <sys/list.h>
61843e1988Sjohnlev #include <sys/bootconf.h>
62843e1988Sjohnlev #include <sys/bootsvcs.h>
63843e1988Sjohnlev #include <sys/bootinfo.h>
64843e1988Sjohnlev #include <sys/note.h>
65*7f0b8309SEdward Pilatowicz #include <sys/sysmacros.h>
66551bc2a6Smrj #ifdef XPV_HVM_DRIVER
67551bc2a6Smrj #include <sys/xpv_support.h>
68551bc2a6Smrj #include <sys/hypervisor.h>
69551bc2a6Smrj #include <public/grant_table.h>
70551bc2a6Smrj #include <public/xen.h>
71551bc2a6Smrj #include <public/io/xenbus.h>
72551bc2a6Smrj #include <public/io/xs_wire.h>
73551bc2a6Smrj #include <public/event_channel.h>
74551bc2a6Smrj #include <public/io/xenbus.h>
75551bc2a6Smrj #else /* XPV_HVM_DRIVER */
76551bc2a6Smrj #include <sys/hypervisor.h>
77843e1988Sjohnlev #include <sys/xen_mmu.h>
78843e1988Sjohnlev #include <xen/sys/xenbus_impl.h>
79551bc2a6Smrj #include <sys/evtchn_impl.h>
80551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
81551bc2a6Smrj #include <sys/gnttab.h>
82843e1988Sjohnlev #include <xen/sys/xendev.h>
83843e1988Sjohnlev #include <vm/hat_i86.h>
84843e1988Sjohnlev #include <sys/scsi/generic/inquiry.h>
85843e1988Sjohnlev #include <util/sscanf.h>
86843e1988Sjohnlev #include <xen/public/io/xs_wire.h>
87843e1988Sjohnlev 
88843e1988Sjohnlev 
8906bbe1e0Sedp #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
9006bbe1e0Sedp #define	isxdigit(ch)	(isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
9106bbe1e0Sedp 			((ch) >= 'A' && (ch) <= 'F'))
9206bbe1e0Sedp 
93843e1988Sjohnlev static void xvdi_ring_init_sring(xendev_ring_t *);
94843e1988Sjohnlev static void xvdi_ring_init_front_ring(xendev_ring_t *, size_t, size_t);
95551bc2a6Smrj #ifndef XPV_HVM_DRIVER
96843e1988Sjohnlev static void xvdi_ring_init_back_ring(xendev_ring_t *, size_t, size_t);
97551bc2a6Smrj #endif
98843e1988Sjohnlev static void xvdi_reinit_ring(dev_info_t *, grant_ref_t *, xendev_ring_t *);
99843e1988Sjohnlev 
100843e1988Sjohnlev static int i_xvdi_add_watches(dev_info_t *);
101843e1988Sjohnlev static void i_xvdi_rem_watches(dev_info_t *);
102843e1988Sjohnlev 
103843e1988Sjohnlev static int i_xvdi_add_watch_oestate(dev_info_t *);
104843e1988Sjohnlev static void i_xvdi_rem_watch_oestate(dev_info_t *);
105843e1988Sjohnlev static void i_xvdi_oestate_cb(struct xenbus_device *, XenbusState);
106843e1988Sjohnlev static void i_xvdi_oestate_handler(void *);
107843e1988Sjohnlev 
108843e1988Sjohnlev static int i_xvdi_add_watch_hpstate(dev_info_t *);
109843e1988Sjohnlev static void i_xvdi_rem_watch_hpstate(dev_info_t *);
110843e1988Sjohnlev static void i_xvdi_hpstate_cb(struct xenbus_watch *, const char **,
111843e1988Sjohnlev     unsigned int);
112843e1988Sjohnlev static void i_xvdi_hpstate_handler(void *);
113843e1988Sjohnlev 
114843e1988Sjohnlev static int i_xvdi_add_watch_bepath(dev_info_t *);
115843e1988Sjohnlev static void i_xvdi_rem_watch_bepath(dev_info_t *);
116843e1988Sjohnlev static void i_xvdi_bepath_cb(struct xenbus_watch *, const char **,
117843e1988Sjohnlev     unsigned in);
118843e1988Sjohnlev 
119843e1988Sjohnlev static void xendev_offline_device(void *);
120843e1988Sjohnlev 
121843e1988Sjohnlev static void i_xvdi_probe_path_cb(struct xenbus_watch *, const char **,
122843e1988Sjohnlev     unsigned int);
123843e1988Sjohnlev static void i_xvdi_probe_path_handler(void *);
124843e1988Sjohnlev 
125eea6c6b9SMax zhen typedef struct oestate_evt {
126eea6c6b9SMax zhen 	dev_info_t *dip;
127eea6c6b9SMax zhen 	XenbusState state;
128eea6c6b9SMax zhen } i_oestate_evt_t;
129eea6c6b9SMax zhen 
130843e1988Sjohnlev typedef struct xd_cfg {
131843e1988Sjohnlev 	xendev_devclass_t devclass;
132843e1988Sjohnlev 	char *xsdev;
133843e1988Sjohnlev 	char *xs_path_fe;
134843e1988Sjohnlev 	char *xs_path_be;
135843e1988Sjohnlev 	char *node_fe;
136843e1988Sjohnlev 	char *node_be;
137843e1988Sjohnlev 	char *device_type;
138843e1988Sjohnlev 	int xd_ipl;
139843e1988Sjohnlev 	int flags;
140843e1988Sjohnlev } i_xd_cfg_t;
141843e1988Sjohnlev 
142843e1988Sjohnlev #define	XD_DOM_ZERO	0x01	/* dom0 only. */
143843e1988Sjohnlev #define	XD_DOM_GUEST	0x02	/* Guest domains (i.e. non-dom0). */
144843e1988Sjohnlev #define	XD_DOM_IO	0x04	/* IO domains. */
145843e1988Sjohnlev 
146843e1988Sjohnlev #define	XD_DOM_ALL	(XD_DOM_ZERO | XD_DOM_GUEST)
147843e1988Sjohnlev 
148843e1988Sjohnlev static i_xd_cfg_t xdci[] = {
149843e1988Sjohnlev 	{ XEN_CONSOLE, NULL, NULL, NULL, "xencons", NULL,
150843e1988Sjohnlev 	    "console", IPL_CONS, XD_DOM_ALL, },
151843e1988Sjohnlev 
152843e1988Sjohnlev 	{ XEN_VNET, "vif", "device/vif", "backend/vif", "xnf", "xnb",
153843e1988Sjohnlev 	    "network", IPL_VIF, XD_DOM_ALL, },
154843e1988Sjohnlev 
155843e1988Sjohnlev 	{ XEN_VBLK, "vbd", "device/vbd", "backend/vbd", "xdf", "xdb",
156843e1988Sjohnlev 	    "block", IPL_VBD, XD_DOM_ALL, },
157843e1988Sjohnlev 
1587eea693dSMark Johnson 	{ XEN_BLKTAP, "tap", NULL, "backend/tap", NULL, "xpvtap",
1597eea693dSMark Johnson 	    "block", IPL_VBD, XD_DOM_ALL, },
1607eea693dSMark Johnson 
161843e1988Sjohnlev 	{ XEN_XENBUS, NULL, NULL, NULL, "xenbus", NULL,
162843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
163843e1988Sjohnlev 
164843e1988Sjohnlev 	{ XEN_DOMCAPS, NULL, NULL, NULL, "domcaps", NULL,
165843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
166843e1988Sjohnlev 
167843e1988Sjohnlev 	{ XEN_BALLOON, NULL, NULL, NULL, "balloon", NULL,
168843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
169843e1988Sjohnlev 
170843e1988Sjohnlev 	{ XEN_EVTCHN, NULL, NULL, NULL, "evtchn", NULL,
171843e1988Sjohnlev 	    NULL, 0, XD_DOM_ZERO, },
172843e1988Sjohnlev 
173843e1988Sjohnlev 	{ XEN_PRIVCMD, NULL, NULL, NULL, "privcmd", NULL,
174843e1988Sjohnlev 	    NULL, 0, XD_DOM_ZERO, },
175843e1988Sjohnlev };
176843e1988Sjohnlev #define	NXDC	(sizeof (xdci) / sizeof (xdci[0]))
177843e1988Sjohnlev 
178843e1988Sjohnlev static void i_xvdi_enum_fe(dev_info_t *, i_xd_cfg_t *);
179843e1988Sjohnlev static void i_xvdi_enum_be(dev_info_t *, i_xd_cfg_t *);
180843e1988Sjohnlev static void i_xvdi_enum_worker(dev_info_t *, i_xd_cfg_t *, char *);
181843e1988Sjohnlev 
182843e1988Sjohnlev /*
183843e1988Sjohnlev  * Xen device channel device access and DMA attributes
184843e1988Sjohnlev  */
185843e1988Sjohnlev static ddi_device_acc_attr_t xendev_dc_accattr = {
186843e1988Sjohnlev 	DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC
187843e1988Sjohnlev };
188843e1988Sjohnlev 
189843e1988Sjohnlev static ddi_dma_attr_t xendev_dc_dmaattr = {
190843e1988Sjohnlev 	DMA_ATTR_V0,		/* version of this structure */
191843e1988Sjohnlev 	0,			/* lowest usable address */
192843e1988Sjohnlev 	0xffffffffffffffffULL,	/* highest usable address */
193843e1988Sjohnlev 	0x7fffffff,		/* maximum DMAable byte count */
194843e1988Sjohnlev 	MMU_PAGESIZE,		/* alignment in bytes */
195843e1988Sjohnlev 	0x7ff,			/* bitmap of burst sizes */
196843e1988Sjohnlev 	1,			/* minimum transfer */
197843e1988Sjohnlev 	0xffffffffU,		/* maximum transfer */
198843e1988Sjohnlev 	0xffffffffffffffffULL,	/* maximum segment length */
199843e1988Sjohnlev 	1,			/* maximum number of segments */
200843e1988Sjohnlev 	1,			/* granularity */
201843e1988Sjohnlev 	0,			/* flags (reserved) */
202843e1988Sjohnlev };
203843e1988Sjohnlev 
204843e1988Sjohnlev static dev_info_t *xendev_dip = NULL;
205843e1988Sjohnlev 
206843e1988Sjohnlev #define	XVDI_DBG_STATE	0x01
207843e1988Sjohnlev #define	XVDI_DBG_PROBE	0x02
208843e1988Sjohnlev 
209843e1988Sjohnlev #ifdef DEBUG
210ab4a9bebSjohnlev int i_xvdi_debug = 0;
211843e1988Sjohnlev 
212843e1988Sjohnlev #define	XVDI_DPRINTF(flag, format, ...)			\
213843e1988Sjohnlev {							\
214843e1988Sjohnlev 	if (i_xvdi_debug & (flag))			\
215843e1988Sjohnlev 		prom_printf((format), __VA_ARGS__);	\
216843e1988Sjohnlev }
217843e1988Sjohnlev #else
218843e1988Sjohnlev #define	XVDI_DPRINTF(flag, format, ...)
219843e1988Sjohnlev #endif /* DEBUG */
220843e1988Sjohnlev 
221843e1988Sjohnlev static i_xd_cfg_t *
222843e1988Sjohnlev i_xvdi_devclass2cfg(xendev_devclass_t devclass)
223843e1988Sjohnlev {
224843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
225843e1988Sjohnlev 	int i;
226843e1988Sjohnlev 
227843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++)
228843e1988Sjohnlev 		if (xdcp->devclass == devclass)
229843e1988Sjohnlev 			return (xdcp);
230843e1988Sjohnlev 
231843e1988Sjohnlev 	return (NULL);
232843e1988Sjohnlev }
233843e1988Sjohnlev 
234843e1988Sjohnlev int
235843e1988Sjohnlev xvdi_init_dev(dev_info_t *dip)
236843e1988Sjohnlev {
237843e1988Sjohnlev 	xendev_devclass_t devcls;
238843e1988Sjohnlev 	int vdevnum;
239843e1988Sjohnlev 	domid_t domid;
240843e1988Sjohnlev 	struct xendev_ppd *pdp;
241843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
242843e1988Sjohnlev 	boolean_t backend;
243843e1988Sjohnlev 	char xsnamebuf[TYPICALMAXPATHLEN];
244843e1988Sjohnlev 	char *xsname;
24597869ac5Sjhd 	void *prop_str;
2463de3be76Sjhd 	unsigned int prop_len;
24797869ac5Sjhd 	char unitaddr[8];
248843e1988Sjohnlev 
249843e1988Sjohnlev 	devcls = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
250843e1988Sjohnlev 	    DDI_PROP_DONTPASS, "devclass", XEN_INVAL);
251843e1988Sjohnlev 	vdevnum = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2521ca30e39Sjohnlev 	    DDI_PROP_DONTPASS, "vdev", VDEV_NOXS);
253843e1988Sjohnlev 	domid = (domid_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
254843e1988Sjohnlev 	    DDI_PROP_DONTPASS, "domain", DOMID_SELF);
255843e1988Sjohnlev 
256843e1988Sjohnlev 	backend = (domid != DOMID_SELF);
257843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devcls);
258843e1988Sjohnlev 	if (xdcp->device_type != NULL)
259843e1988Sjohnlev 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
260843e1988Sjohnlev 		    "device_type", xdcp->device_type);
261843e1988Sjohnlev 
262843e1988Sjohnlev 	pdp = kmem_zalloc(sizeof (*pdp), KM_SLEEP);
263843e1988Sjohnlev 	pdp->xd_domain = domid;
264843e1988Sjohnlev 	pdp->xd_vdevnum = vdevnum;
265843e1988Sjohnlev 	pdp->xd_devclass = devcls;
266843e1988Sjohnlev 	pdp->xd_evtchn = INVALID_EVTCHN;
267*7f0b8309SEdward Pilatowicz 	list_create(&pdp->xd_xb_watches, sizeof (xd_xb_watches_t),
268*7f0b8309SEdward Pilatowicz 	    offsetof(xd_xb_watches_t, xxw_list));
2697eea693dSMark Johnson 	mutex_init(&pdp->xd_evt_lk, NULL, MUTEX_DRIVER, NULL);
2707eea693dSMark Johnson 	mutex_init(&pdp->xd_ndi_lk, NULL, MUTEX_DRIVER, NULL);
271843e1988Sjohnlev 	ddi_set_parent_data(dip, pdp);
272843e1988Sjohnlev 
273843e1988Sjohnlev 	/*
274843e1988Sjohnlev 	 * devices that do not need to interact with xenstore
275843e1988Sjohnlev 	 */
2761ca30e39Sjohnlev 	if (vdevnum == VDEV_NOXS) {
277843e1988Sjohnlev 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
278843e1988Sjohnlev 		    "unit-address", "0");
279843e1988Sjohnlev 		if (devcls == XEN_CONSOLE)
280843e1988Sjohnlev 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
281843e1988Sjohnlev 			    "pm-hardware-state", "needs-suspend-resume");
282843e1988Sjohnlev 		return (DDI_SUCCESS);
283843e1988Sjohnlev 	}
284843e1988Sjohnlev 
285843e1988Sjohnlev 	/*
286843e1988Sjohnlev 	 * PV devices that need to probe xenstore
287843e1988Sjohnlev 	 */
288843e1988Sjohnlev 
289843e1988Sjohnlev 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
290843e1988Sjohnlev 	    "pm-hardware-state", "needs-suspend-resume");
291843e1988Sjohnlev 
292843e1988Sjohnlev 	xsname = xsnamebuf;
293843e1988Sjohnlev 	if (!backend)
294843e1988Sjohnlev 		(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
295843e1988Sjohnlev 		    "%s/%d", xdcp->xs_path_fe, vdevnum);
296843e1988Sjohnlev 	else
297843e1988Sjohnlev 		(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
298843e1988Sjohnlev 		    "%s/%d/%d", xdcp->xs_path_be, domid, vdevnum);
2991d03c31eSjohnlev 	if ((xenbus_read_driver_state(xsname) >= XenbusStateClosing)) {
3001d03c31eSjohnlev 		/* Don't try to init a dev that may be closing */
3017eea693dSMark Johnson 		mutex_destroy(&pdp->xd_ndi_lk);
3027eea693dSMark Johnson 		mutex_destroy(&pdp->xd_evt_lk);
3031d03c31eSjohnlev 		kmem_free(pdp, sizeof (*pdp));
3041d03c31eSjohnlev 		ddi_set_parent_data(dip, NULL);
3051d03c31eSjohnlev 		return (DDI_FAILURE);
3061d03c31eSjohnlev 	}
307843e1988Sjohnlev 
308843e1988Sjohnlev 	pdp->xd_xsdev.nodename = i_ddi_strdup(xsname, KM_SLEEP);
309843e1988Sjohnlev 	pdp->xd_xsdev.devicetype = xdcp->xsdev;
310843e1988Sjohnlev 	pdp->xd_xsdev.frontend = (backend ? 0 : 1);
311843e1988Sjohnlev 	pdp->xd_xsdev.data = dip;
312843e1988Sjohnlev 	pdp->xd_xsdev.otherend_id = (backend ? domid : -1);
313843e1988Sjohnlev 	if (i_xvdi_add_watches(dip) != DDI_SUCCESS) {
314843e1988Sjohnlev 		cmn_err(CE_WARN, "xvdi_init_dev: "
315843e1988Sjohnlev 		    "cannot add watches for %s", xsname);
316843e1988Sjohnlev 		xvdi_uninit_dev(dip);
317843e1988Sjohnlev 		return (DDI_FAILURE);
318843e1988Sjohnlev 	}
319843e1988Sjohnlev 
32097869ac5Sjhd 	if (backend)
32197869ac5Sjhd 		return (DDI_SUCCESS);
32297869ac5Sjhd 
323843e1988Sjohnlev 	/*
32497869ac5Sjhd 	 * The unit-address for frontend devices is the name of the
32597869ac5Sjhd 	 * of the xenstore node containing the device configuration
32697869ac5Sjhd 	 * and is contained in the 'vdev' property.
32797869ac5Sjhd 	 * VIF devices are named using an incrementing integer.
32897869ac5Sjhd 	 * VBD devices are either named using the 16-bit dev_t value
32997869ac5Sjhd 	 * for linux 'hd' and 'xvd' devices, or a simple integer value
33097869ac5Sjhd 	 * in the range 0..767.  768 is the base value of the linux
33197869ac5Sjhd 	 * dev_t namespace, the dev_t value for 'hda'.
332843e1988Sjohnlev 	 */
33397869ac5Sjhd 	(void) snprintf(unitaddr, sizeof (unitaddr), "%d", vdevnum);
33497869ac5Sjhd 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "unit-address",
33597869ac5Sjhd 	    unitaddr);
33697869ac5Sjhd 
33797869ac5Sjhd 	switch (devcls) {
33897869ac5Sjhd 	case XEN_VNET:
33997869ac5Sjhd 		if (xenbus_read(XBT_NULL, xsname, "mac", (void *)&prop_str,
34097869ac5Sjhd 		    &prop_len) != 0)
341843e1988Sjohnlev 			break;
34297869ac5Sjhd 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "mac",
34397869ac5Sjhd 		    prop_str);
34497869ac5Sjhd 		kmem_free(prop_str, prop_len);
34597869ac5Sjhd 		break;
34697869ac5Sjhd 	case XEN_VBLK:
34797869ac5Sjhd 		/*
34897869ac5Sjhd 		 * cache a copy of the otherend name
34997869ac5Sjhd 		 * for ease of observeability
35097869ac5Sjhd 		 */
35197869ac5Sjhd 		if (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend, "dev",
35297869ac5Sjhd 		    &prop_str, &prop_len) != 0)
353843e1988Sjohnlev 			break;
35497869ac5Sjhd 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
35597869ac5Sjhd 		    "dev-address", prop_str);
35697869ac5Sjhd 		kmem_free(prop_str, prop_len);
35797869ac5Sjhd 		break;
35897869ac5Sjhd 	default:
35997869ac5Sjhd 		break;
360843e1988Sjohnlev 	}
361843e1988Sjohnlev 
362843e1988Sjohnlev 	return (DDI_SUCCESS);
363843e1988Sjohnlev }
364843e1988Sjohnlev 
365843e1988Sjohnlev void
366843e1988Sjohnlev xvdi_uninit_dev(dev_info_t *dip)
367843e1988Sjohnlev {
368843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
369843e1988Sjohnlev 
370843e1988Sjohnlev 	if (pdp != NULL) {
371843e1988Sjohnlev 		/* Remove any registered callbacks. */
372843e1988Sjohnlev 		xvdi_remove_event_handler(dip, NULL);
373843e1988Sjohnlev 
374843e1988Sjohnlev 		/* Remove any registered watches. */
375843e1988Sjohnlev 		i_xvdi_rem_watches(dip);
376843e1988Sjohnlev 
3771d03c31eSjohnlev 		/* tell other end to close */
378551bc2a6Smrj 		if (pdp->xd_xsdev.otherend_id != (domid_t)-1)
379551bc2a6Smrj 			(void) xvdi_switch_state(dip, XBT_NULL,
380551bc2a6Smrj 			    XenbusStateClosed);
3811d03c31eSjohnlev 
382843e1988Sjohnlev 		if (pdp->xd_xsdev.nodename != NULL)
383843e1988Sjohnlev 			kmem_free((char *)(pdp->xd_xsdev.nodename),
384843e1988Sjohnlev 			    strlen(pdp->xd_xsdev.nodename) + 1);
385843e1988Sjohnlev 
386843e1988Sjohnlev 		ddi_set_parent_data(dip, NULL);
387843e1988Sjohnlev 
3887eea693dSMark Johnson 		mutex_destroy(&pdp->xd_ndi_lk);
3897eea693dSMark Johnson 		mutex_destroy(&pdp->xd_evt_lk);
390843e1988Sjohnlev 		kmem_free(pdp, sizeof (*pdp));
391843e1988Sjohnlev 	}
392843e1988Sjohnlev }
393843e1988Sjohnlev 
394843e1988Sjohnlev /*
395843e1988Sjohnlev  * Bind the event channel for this device instance.
396843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
397843e1988Sjohnlev  */
398843e1988Sjohnlev int
399843e1988Sjohnlev xvdi_bind_evtchn(dev_info_t *dip, evtchn_port_t evtchn)
400843e1988Sjohnlev {
401843e1988Sjohnlev 	struct xendev_ppd *pdp;
402843e1988Sjohnlev 	domid_t oeid;
403843e1988Sjohnlev 	int r;
404843e1988Sjohnlev 
405843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
406843e1988Sjohnlev 	ASSERT(pdp != NULL);
407843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
408843e1988Sjohnlev 
4097eea693dSMark Johnson 	mutex_enter(&pdp->xd_evt_lk);
410843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE) {
411843e1988Sjohnlev 		if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
412843e1988Sjohnlev 			pdp->xd_evtchn = xen_info->console.domU.evtchn;
413843e1988Sjohnlev 		} else {
414843e1988Sjohnlev 			pdp->xd_evtchn = INVALID_EVTCHN;
4157eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
416843e1988Sjohnlev 			return (DDI_SUCCESS);
417843e1988Sjohnlev 		}
418843e1988Sjohnlev 	} else {
419843e1988Sjohnlev 		oeid = pdp->xd_xsdev.otherend_id;
420843e1988Sjohnlev 		if (oeid == (domid_t)-1) {
4217eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
422843e1988Sjohnlev 			return (DDI_FAILURE);
423843e1988Sjohnlev 		}
424843e1988Sjohnlev 
425843e1988Sjohnlev 		if ((r = xen_bind_interdomain(oeid, evtchn, &pdp->xd_evtchn))) {
426843e1988Sjohnlev 			xvdi_dev_error(dip, r, "bind event channel");
4277eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
428843e1988Sjohnlev 			return (DDI_FAILURE);
429843e1988Sjohnlev 		}
430843e1988Sjohnlev 	}
431551bc2a6Smrj #ifndef XPV_HVM_DRIVER
432843e1988Sjohnlev 	pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
433551bc2a6Smrj #endif
4347eea693dSMark Johnson 	mutex_exit(&pdp->xd_evt_lk);
435843e1988Sjohnlev 
436843e1988Sjohnlev 	return (DDI_SUCCESS);
437843e1988Sjohnlev }
438843e1988Sjohnlev 
439843e1988Sjohnlev /*
440843e1988Sjohnlev  * Allocate an event channel for this device instance.
441843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
442843e1988Sjohnlev  */
443843e1988Sjohnlev int
444843e1988Sjohnlev xvdi_alloc_evtchn(dev_info_t *dip)
445843e1988Sjohnlev {
446843e1988Sjohnlev 	struct xendev_ppd *pdp;
447843e1988Sjohnlev 	domid_t oeid;
448843e1988Sjohnlev 	int rv;
449843e1988Sjohnlev 
450843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
451843e1988Sjohnlev 	ASSERT(pdp != NULL);
452843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
453843e1988Sjohnlev 
4547eea693dSMark Johnson 	mutex_enter(&pdp->xd_evt_lk);
455843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE) {
456843e1988Sjohnlev 		if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
457843e1988Sjohnlev 			pdp->xd_evtchn = xen_info->console.domU.evtchn;
458843e1988Sjohnlev 		} else {
459843e1988Sjohnlev 			pdp->xd_evtchn = INVALID_EVTCHN;
4607eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
461843e1988Sjohnlev 			return (DDI_SUCCESS);
462843e1988Sjohnlev 		}
463843e1988Sjohnlev 	} else {
464843e1988Sjohnlev 		oeid = pdp->xd_xsdev.otherend_id;
465843e1988Sjohnlev 		if (oeid == (domid_t)-1) {
4667eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
467843e1988Sjohnlev 			return (DDI_FAILURE);
468843e1988Sjohnlev 		}
469843e1988Sjohnlev 
470843e1988Sjohnlev 		if ((rv = xen_alloc_unbound_evtchn(oeid, &pdp->xd_evtchn))) {
471843e1988Sjohnlev 			xvdi_dev_error(dip, rv, "bind event channel");
4727eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
473843e1988Sjohnlev 			return (DDI_FAILURE);
474843e1988Sjohnlev 		}
475843e1988Sjohnlev 	}
476551bc2a6Smrj #ifndef XPV_HVM_DRIVER
477843e1988Sjohnlev 	pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
478551bc2a6Smrj #endif
4797eea693dSMark Johnson 	mutex_exit(&pdp->xd_evt_lk);
480843e1988Sjohnlev 
481843e1988Sjohnlev 	return (DDI_SUCCESS);
482843e1988Sjohnlev }
483843e1988Sjohnlev 
484843e1988Sjohnlev /*
485843e1988Sjohnlev  * Unbind the event channel for this device instance.
486843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
487843e1988Sjohnlev  */
488843e1988Sjohnlev void
489843e1988Sjohnlev xvdi_free_evtchn(dev_info_t *dip)
490843e1988Sjohnlev {
491843e1988Sjohnlev 	struct xendev_ppd *pdp;
492843e1988Sjohnlev 
493843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
494843e1988Sjohnlev 	ASSERT(pdp != NULL);
495843e1988Sjohnlev 
4967eea693dSMark Johnson 	mutex_enter(&pdp->xd_evt_lk);
497843e1988Sjohnlev 	if (pdp->xd_evtchn != INVALID_EVTCHN) {
498551bc2a6Smrj #ifndef XPV_HVM_DRIVER
499843e1988Sjohnlev 		ec_unbind_irq(pdp->xd_ispec.intrspec_vec);
500843e1988Sjohnlev 		pdp->xd_ispec.intrspec_vec = 0;
501551bc2a6Smrj #endif
502551bc2a6Smrj 		pdp->xd_evtchn = INVALID_EVTCHN;
503843e1988Sjohnlev 	}
5047eea693dSMark Johnson 	mutex_exit(&pdp->xd_evt_lk);
505843e1988Sjohnlev }
506843e1988Sjohnlev 
507551bc2a6Smrj #ifndef XPV_HVM_DRIVER
508843e1988Sjohnlev /*
509843e1988Sjohnlev  * Map an inter-domain communication ring for a virtual device.
510843e1988Sjohnlev  * This is used by backend drivers.
511843e1988Sjohnlev  */
512843e1988Sjohnlev int
513843e1988Sjohnlev xvdi_map_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
514843e1988Sjohnlev     grant_ref_t gref, xendev_ring_t **ringpp)
515843e1988Sjohnlev {
516843e1988Sjohnlev 	domid_t oeid;
517843e1988Sjohnlev 	gnttab_map_grant_ref_t mapop;
518843e1988Sjohnlev 	gnttab_unmap_grant_ref_t unmapop;
519843e1988Sjohnlev 	caddr_t ringva;
520843e1988Sjohnlev 	ddi_acc_hdl_t *ap;
521843e1988Sjohnlev 	ddi_acc_impl_t *iap;
522843e1988Sjohnlev 	xendev_ring_t *ring;
523843e1988Sjohnlev 	int err;
524843e1988Sjohnlev 	char errstr[] = "mapping in ring buffer";
525843e1988Sjohnlev 
526843e1988Sjohnlev 	ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
527843e1988Sjohnlev 	oeid = xvdi_get_oeid(dip);
528843e1988Sjohnlev 
529843e1988Sjohnlev 	/* alloc va in backend dom for ring buffer */
530843e1988Sjohnlev 	ringva = vmem_xalloc(heap_arena, PAGESIZE, PAGESIZE,
531843e1988Sjohnlev 	    0, 0, 0, 0, VM_SLEEP);
532843e1988Sjohnlev 
533843e1988Sjohnlev 	/* map in ring page */
5347eea693dSMark Johnson 	hat_prepare_mapping(kas.a_hat, ringva, NULL);
535843e1988Sjohnlev 	mapop.host_addr = (uint64_t)(uintptr_t)ringva;
536843e1988Sjohnlev 	mapop.flags = GNTMAP_host_map;
537843e1988Sjohnlev 	mapop.ref = gref;
538843e1988Sjohnlev 	mapop.dom = oeid;
5397eea693dSMark Johnson 	err = xen_map_gref(GNTTABOP_map_grant_ref, &mapop, 1, B_FALSE);
540843e1988Sjohnlev 	if (err) {
541843e1988Sjohnlev 		xvdi_fatal_error(dip, err, errstr);
542843e1988Sjohnlev 		goto errout1;
543843e1988Sjohnlev 	}
544843e1988Sjohnlev 
545843e1988Sjohnlev 	if (mapop.status != 0) {
546843e1988Sjohnlev 		xvdi_fatal_error(dip, err, errstr);
547843e1988Sjohnlev 		goto errout2;
548843e1988Sjohnlev 	}
549843e1988Sjohnlev 	ring->xr_vaddr = ringva;
550843e1988Sjohnlev 	ring->xr_grant_hdl = mapop.handle;
551843e1988Sjohnlev 	ring->xr_gref = gref;
552843e1988Sjohnlev 
553843e1988Sjohnlev 	/*
554843e1988Sjohnlev 	 * init an acc handle and associate it w/ this ring
555843e1988Sjohnlev 	 * this is only for backend drivers. we get the memory by calling
556843e1988Sjohnlev 	 * vmem_xalloc(), instead of calling any ddi function, so we have
557843e1988Sjohnlev 	 * to init an acc handle by ourselves
558843e1988Sjohnlev 	 */
559843e1988Sjohnlev 	ring->xr_acc_hdl = impl_acc_hdl_alloc(KM_SLEEP, NULL);
560843e1988Sjohnlev 	ap = impl_acc_hdl_get(ring->xr_acc_hdl);
561843e1988Sjohnlev 	ap->ah_vers = VERS_ACCHDL;
562843e1988Sjohnlev 	ap->ah_dip = dip;
563843e1988Sjohnlev 	ap->ah_xfermodes = DDI_DMA_CONSISTENT;
564843e1988Sjohnlev 	ap->ah_acc = xendev_dc_accattr;
565843e1988Sjohnlev 	iap = (ddi_acc_impl_t *)ap->ah_platform_private;
566843e1988Sjohnlev 	iap->ahi_acc_attr |= DDI_ACCATTR_CPU_VADDR;
567843e1988Sjohnlev 	impl_acc_hdl_init(ap);
568843e1988Sjohnlev 	ap->ah_offset = 0;
569843e1988Sjohnlev 	ap->ah_len = (off_t)PAGESIZE;
570843e1988Sjohnlev 	ap->ah_addr = ring->xr_vaddr;
571843e1988Sjohnlev 
572843e1988Sjohnlev 	/* init backend ring */
573843e1988Sjohnlev 	xvdi_ring_init_back_ring(ring, nentry, entrysize);
574843e1988Sjohnlev 
575843e1988Sjohnlev 	*ringpp = ring;
576843e1988Sjohnlev 
577843e1988Sjohnlev 	return (DDI_SUCCESS);
578843e1988Sjohnlev 
579843e1988Sjohnlev errout2:
580843e1988Sjohnlev 	/* unmap ring page */
581843e1988Sjohnlev 	unmapop.host_addr = (uint64_t)(uintptr_t)ringva;
582843e1988Sjohnlev 	unmapop.handle = ring->xr_grant_hdl;
583843e1988Sjohnlev 	unmapop.dev_bus_addr = NULL;
584843e1988Sjohnlev 	(void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
585843e1988Sjohnlev 	hat_release_mapping(kas.a_hat, ringva);
586843e1988Sjohnlev errout1:
587843e1988Sjohnlev 	vmem_xfree(heap_arena, ringva, PAGESIZE);
588843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
589843e1988Sjohnlev 	return (DDI_FAILURE);
590843e1988Sjohnlev }
591843e1988Sjohnlev 
592843e1988Sjohnlev /*
593843e1988Sjohnlev  * Unmap a ring for a virtual device.
594843e1988Sjohnlev  * This is used by backend drivers.
595843e1988Sjohnlev  */
596843e1988Sjohnlev void
597843e1988Sjohnlev xvdi_unmap_ring(xendev_ring_t *ring)
598843e1988Sjohnlev {
599843e1988Sjohnlev 	gnttab_unmap_grant_ref_t unmapop;
600843e1988Sjohnlev 
601843e1988Sjohnlev 	ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
602843e1988Sjohnlev 
603843e1988Sjohnlev 	impl_acc_hdl_free(ring->xr_acc_hdl);
604843e1988Sjohnlev 	unmapop.host_addr = (uint64_t)(uintptr_t)ring->xr_vaddr;
605843e1988Sjohnlev 	unmapop.handle = ring->xr_grant_hdl;
606843e1988Sjohnlev 	unmapop.dev_bus_addr = NULL;
607843e1988Sjohnlev 	(void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
608843e1988Sjohnlev 	hat_release_mapping(kas.a_hat, ring->xr_vaddr);
609843e1988Sjohnlev 	vmem_xfree(heap_arena, ring->xr_vaddr, PAGESIZE);
610843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
611843e1988Sjohnlev }
612551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
613843e1988Sjohnlev 
614843e1988Sjohnlev /*
615843e1988Sjohnlev  * Re-initialise an inter-domain communications ring for the backend domain.
616843e1988Sjohnlev  * ring will be re-initialized after re-grant succeed
617843e1988Sjohnlev  * ring will be freed if fails to re-grant access to backend domain
618843e1988Sjohnlev  * so, don't keep useful data in the ring
619843e1988Sjohnlev  * used only in frontend driver
620843e1988Sjohnlev  */
621843e1988Sjohnlev static void
622843e1988Sjohnlev xvdi_reinit_ring(dev_info_t *dip, grant_ref_t *gref, xendev_ring_t *ringp)
623843e1988Sjohnlev {
624843e1988Sjohnlev 	paddr_t rpaddr;
625843e1988Sjohnlev 	maddr_t rmaddr;
626843e1988Sjohnlev 
627843e1988Sjohnlev 	ASSERT((ringp != NULL) && (ringp->xr_paddr != 0));
628843e1988Sjohnlev 	rpaddr = ringp->xr_paddr;
629843e1988Sjohnlev 
630843e1988Sjohnlev 	rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? rpaddr : pa_to_ma(rpaddr);
631843e1988Sjohnlev 	gnttab_grant_foreign_access_ref(ringp->xr_gref, xvdi_get_oeid(dip),
632843e1988Sjohnlev 	    rmaddr >> PAGESHIFT, 0);
633843e1988Sjohnlev 	*gref = ringp->xr_gref;
634843e1988Sjohnlev 
635843e1988Sjohnlev 	/* init frontend ring */
636843e1988Sjohnlev 	xvdi_ring_init_sring(ringp);
637843e1988Sjohnlev 	xvdi_ring_init_front_ring(ringp, ringp->xr_sring.fr.nr_ents,
638843e1988Sjohnlev 	    ringp->xr_entry_size);
639843e1988Sjohnlev }
640843e1988Sjohnlev 
641843e1988Sjohnlev /*
642843e1988Sjohnlev  * allocate Xen inter-domain communications ring for Xen virtual devices
643843e1988Sjohnlev  * used only in frontend driver
644843e1988Sjohnlev  * if *ringpp is not NULL, we'll simply re-init it
645843e1988Sjohnlev  */
646843e1988Sjohnlev int
647843e1988Sjohnlev xvdi_alloc_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
648843e1988Sjohnlev     grant_ref_t *gref, xendev_ring_t **ringpp)
649843e1988Sjohnlev {
650843e1988Sjohnlev 	size_t len;
651843e1988Sjohnlev 	xendev_ring_t *ring;
652843e1988Sjohnlev 	ddi_dma_cookie_t dma_cookie;
653843e1988Sjohnlev 	uint_t ncookies;
654843e1988Sjohnlev 	grant_ref_t ring_gref;
655843e1988Sjohnlev 	domid_t oeid;
656843e1988Sjohnlev 	maddr_t rmaddr;
657843e1988Sjohnlev 
658843e1988Sjohnlev 	if (*ringpp) {
659843e1988Sjohnlev 		xvdi_reinit_ring(dip, gref, *ringpp);
660843e1988Sjohnlev 		return (DDI_SUCCESS);
661843e1988Sjohnlev 	}
662843e1988Sjohnlev 
663843e1988Sjohnlev 	*ringpp = ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
664843e1988Sjohnlev 	oeid = xvdi_get_oeid(dip);
665843e1988Sjohnlev 
666843e1988Sjohnlev 	/*
667843e1988Sjohnlev 	 * Allocate page for this ring buffer
668843e1988Sjohnlev 	 */
669843e1988Sjohnlev 	if (ddi_dma_alloc_handle(dip, &xendev_dc_dmaattr, DDI_DMA_SLEEP,
670843e1988Sjohnlev 	    0, &ring->xr_dma_hdl) != DDI_SUCCESS)
671843e1988Sjohnlev 		goto err;
672843e1988Sjohnlev 
673843e1988Sjohnlev 	if (ddi_dma_mem_alloc(ring->xr_dma_hdl, PAGESIZE,
674843e1988Sjohnlev 	    &xendev_dc_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
675843e1988Sjohnlev 	    &ring->xr_vaddr, &len, &ring->xr_acc_hdl) != DDI_SUCCESS) {
676843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
677843e1988Sjohnlev 		goto err;
678843e1988Sjohnlev 	}
679843e1988Sjohnlev 
680843e1988Sjohnlev 	if (ddi_dma_addr_bind_handle(ring->xr_dma_hdl, NULL,
681843e1988Sjohnlev 	    ring->xr_vaddr, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
682843e1988Sjohnlev 	    DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies) != DDI_DMA_MAPPED) {
683843e1988Sjohnlev 		ddi_dma_mem_free(&ring->xr_acc_hdl);
684843e1988Sjohnlev 		ring->xr_vaddr = NULL;
685843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
686843e1988Sjohnlev 		goto err;
687843e1988Sjohnlev 	}
688843e1988Sjohnlev 	ASSERT(ncookies == 1);
689843e1988Sjohnlev 	ring->xr_paddr = dma_cookie.dmac_laddress;
690843e1988Sjohnlev 	rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? ring->xr_paddr :
691843e1988Sjohnlev 	    pa_to_ma(ring->xr_paddr);
692843e1988Sjohnlev 
693843e1988Sjohnlev 	if ((ring_gref = gnttab_grant_foreign_access(oeid,
694843e1988Sjohnlev 	    rmaddr >> PAGESHIFT, 0)) == (grant_ref_t)-1) {
695843e1988Sjohnlev 		(void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
696843e1988Sjohnlev 		ddi_dma_mem_free(&ring->xr_acc_hdl);
697843e1988Sjohnlev 		ring->xr_vaddr = NULL;
698843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
699843e1988Sjohnlev 		goto err;
700843e1988Sjohnlev 	}
701843e1988Sjohnlev 	*gref = ring->xr_gref = ring_gref;
702843e1988Sjohnlev 
703843e1988Sjohnlev 	/* init frontend ring */
704843e1988Sjohnlev 	xvdi_ring_init_sring(ring);
705843e1988Sjohnlev 	xvdi_ring_init_front_ring(ring, nentry, entrysize);
706843e1988Sjohnlev 
707843e1988Sjohnlev 	return (DDI_SUCCESS);
708843e1988Sjohnlev 
709843e1988Sjohnlev err:
710843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
711843e1988Sjohnlev 	return (DDI_FAILURE);
712843e1988Sjohnlev }
713843e1988Sjohnlev 
714843e1988Sjohnlev /*
715843e1988Sjohnlev  * Release ring buffers allocated for Xen devices
716843e1988Sjohnlev  * used for frontend driver
717843e1988Sjohnlev  */
718843e1988Sjohnlev void
719843e1988Sjohnlev xvdi_free_ring(xendev_ring_t *ring)
720843e1988Sjohnlev {
721843e1988Sjohnlev 	ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
722843e1988Sjohnlev 
723843e1988Sjohnlev 	(void) gnttab_end_foreign_access_ref(ring->xr_gref, 0);
724843e1988Sjohnlev 	(void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
725843e1988Sjohnlev 	ddi_dma_mem_free(&ring->xr_acc_hdl);
726843e1988Sjohnlev 	ddi_dma_free_handle(&ring->xr_dma_hdl);
727843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
728843e1988Sjohnlev }
729843e1988Sjohnlev 
730843e1988Sjohnlev dev_info_t *
731843e1988Sjohnlev xvdi_create_dev(dev_info_t *parent, xendev_devclass_t devclass,
732843e1988Sjohnlev     domid_t dom, int vdev)
733843e1988Sjohnlev {
734843e1988Sjohnlev 	dev_info_t *dip;
735843e1988Sjohnlev 	boolean_t backend;
736843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
737843e1988Sjohnlev 	char xsnamebuf[TYPICALMAXPATHLEN];
738843e1988Sjohnlev 	char *type, *node = NULL, *xsname = NULL;
739843e1988Sjohnlev 	unsigned int tlen;
7401d03c31eSjohnlev 	int ret;
741843e1988Sjohnlev 
742843e1988Sjohnlev 	ASSERT(DEVI_BUSY_OWNED(parent));
743843e1988Sjohnlev 
744843e1988Sjohnlev 	backend = (dom != DOMID_SELF);
745843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
746843e1988Sjohnlev 	ASSERT(xdcp != NULL);
747843e1988Sjohnlev 
7481ca30e39Sjohnlev 	if (vdev != VDEV_NOXS) {
749843e1988Sjohnlev 		if (!backend) {
750843e1988Sjohnlev 			(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
751843e1988Sjohnlev 			    "%s/%d", xdcp->xs_path_fe, vdev);
752843e1988Sjohnlev 			xsname = xsnamebuf;
753843e1988Sjohnlev 			node = xdcp->node_fe;
754843e1988Sjohnlev 		} else {
755843e1988Sjohnlev 			(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
756843e1988Sjohnlev 			    "%s/%d/%d", xdcp->xs_path_be, dom, vdev);
757843e1988Sjohnlev 			xsname = xsnamebuf;
758843e1988Sjohnlev 			node = xdcp->node_be;
759843e1988Sjohnlev 		}
760843e1988Sjohnlev 	} else {
761843e1988Sjohnlev 		node = xdcp->node_fe;
762843e1988Sjohnlev 	}
763843e1988Sjohnlev 
764843e1988Sjohnlev 	/* Must have a driver to use. */
765843e1988Sjohnlev 	if (node == NULL)
766843e1988Sjohnlev 		return (NULL);
767843e1988Sjohnlev 
768843e1988Sjohnlev 	/*
769843e1988Sjohnlev 	 * We need to check the state of this device before we go
770843e1988Sjohnlev 	 * further, otherwise we'll end up with a dead loop if
771843e1988Sjohnlev 	 * anything goes wrong.
772843e1988Sjohnlev 	 */
773843e1988Sjohnlev 	if ((xsname != NULL) &&
774843e1988Sjohnlev 	    (xenbus_read_driver_state(xsname) >= XenbusStateClosing))
775843e1988Sjohnlev 		return (NULL);
776843e1988Sjohnlev 
777843e1988Sjohnlev 	ndi_devi_alloc_sleep(parent, node, DEVI_SID_NODEID, &dip);
778843e1988Sjohnlev 
779843e1988Sjohnlev 	/*
780843e1988Sjohnlev 	 * Driver binding uses the compatible property _before_ the
781843e1988Sjohnlev 	 * node name, so we set the node name to the 'model' of the
782843e1988Sjohnlev 	 * device (i.e. 'xnb' or 'xdb') and, if 'type' is present,
783843e1988Sjohnlev 	 * encode both the model and the type in a compatible property
784843e1988Sjohnlev 	 * (i.e. 'xnb,netfront' or 'xnb,SUNW_mac').  This allows a
785843e1988Sjohnlev 	 * driver binding based on the <model,type> pair _before_ a
786843e1988Sjohnlev 	 * binding based on the node name.
787843e1988Sjohnlev 	 */
788843e1988Sjohnlev 	if ((xsname != NULL) &&
789843e1988Sjohnlev 	    (xenbus_read(XBT_NULL, xsname, "type", (void *)&type, &tlen)
790843e1988Sjohnlev 	    == 0)) {
791843e1988Sjohnlev 		size_t clen;
792843e1988Sjohnlev 		char *c[1];
793843e1988Sjohnlev 
794843e1988Sjohnlev 		clen = strlen(node) + strlen(type) + 2;
795843e1988Sjohnlev 		c[0] = kmem_alloc(clen, KM_SLEEP);
796843e1988Sjohnlev 		(void) snprintf(c[0], clen, "%s,%s", node, type);
797843e1988Sjohnlev 
798843e1988Sjohnlev 		(void) ndi_prop_update_string_array(DDI_DEV_T_NONE,
799843e1988Sjohnlev 		    dip, "compatible", (char **)c, 1);
800843e1988Sjohnlev 
801843e1988Sjohnlev 		kmem_free(c[0], clen);
802843e1988Sjohnlev 		kmem_free(type, tlen);
803843e1988Sjohnlev 	}
804843e1988Sjohnlev 
805843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "devclass", devclass);
806843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "domain", dom);
807843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "vdev", vdev);
808843e1988Sjohnlev 
809843e1988Sjohnlev 	if (i_ddi_devi_attached(parent))
8101d03c31eSjohnlev 		ret = ndi_devi_online(dip, 0);
811843e1988Sjohnlev 	else
8121d03c31eSjohnlev 		ret = ndi_devi_bind_driver(dip, 0);
8131d03c31eSjohnlev 	if (ret != NDI_SUCCESS)
8141d03c31eSjohnlev 		(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
815843e1988Sjohnlev 
816843e1988Sjohnlev 	return (dip);
817843e1988Sjohnlev }
818843e1988Sjohnlev 
819843e1988Sjohnlev /*
820843e1988Sjohnlev  * xendev_enum_class()
821843e1988Sjohnlev  */
822843e1988Sjohnlev void
823843e1988Sjohnlev xendev_enum_class(dev_info_t *parent, xendev_devclass_t devclass)
824843e1988Sjohnlev {
825d798155dSmrj 	boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
826d798155dSmrj 	boolean_t domU = !dom0;
827843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
828843e1988Sjohnlev 
829843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
830843e1988Sjohnlev 	ASSERT(xdcp != NULL);
831843e1988Sjohnlev 
832d798155dSmrj 	if (dom0 && !(xdcp->flags & XD_DOM_ZERO))
833d798155dSmrj 		return;
834d798155dSmrj 
835d798155dSmrj 	if (domU && !(xdcp->flags & XD_DOM_GUEST))
836d798155dSmrj 		return;
837d798155dSmrj 
838843e1988Sjohnlev 	if (xdcp->xsdev == NULL) {
839843e1988Sjohnlev 		int circ;
840843e1988Sjohnlev 
841843e1988Sjohnlev 		/*
842843e1988Sjohnlev 		 * Don't need to probe this kind of device from the
843843e1988Sjohnlev 		 * store, just create one if it doesn't exist.
844843e1988Sjohnlev 		 */
845843e1988Sjohnlev 
846843e1988Sjohnlev 		ndi_devi_enter(parent, &circ);
8471ca30e39Sjohnlev 		if (xvdi_find_dev(parent, devclass, DOMID_SELF, VDEV_NOXS)
848843e1988Sjohnlev 		    == NULL)
849843e1988Sjohnlev 			(void) xvdi_create_dev(parent, devclass,
8501ca30e39Sjohnlev 			    DOMID_SELF, VDEV_NOXS);
851843e1988Sjohnlev 		ndi_devi_exit(parent, circ);
852843e1988Sjohnlev 	} else {
853843e1988Sjohnlev 		/*
854843e1988Sjohnlev 		 * Probe this kind of device from the store, both
855843e1988Sjohnlev 		 * frontend and backend.
856843e1988Sjohnlev 		 */
8577eea693dSMark Johnson 		if (xdcp->node_fe != NULL) {
8587eea693dSMark Johnson 			i_xvdi_enum_fe(parent, xdcp);
8597eea693dSMark Johnson 		}
8607eea693dSMark Johnson 		if (xdcp->node_be != NULL) {
8617eea693dSMark Johnson 			i_xvdi_enum_be(parent, xdcp);
8627eea693dSMark Johnson 		}
863843e1988Sjohnlev 	}
864843e1988Sjohnlev }
865843e1988Sjohnlev 
866843e1988Sjohnlev /*
867843e1988Sjohnlev  * xendev_enum_all()
868843e1988Sjohnlev  */
869843e1988Sjohnlev void
870843e1988Sjohnlev xendev_enum_all(dev_info_t *parent, boolean_t store_unavailable)
871843e1988Sjohnlev {
872843e1988Sjohnlev 	int i;
873843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
874843e1988Sjohnlev 	boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
875843e1988Sjohnlev 
876843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
877843e1988Sjohnlev 		/*
878843e1988Sjohnlev 		 * Dom0 relies on watchpoints to create non-soft
879843e1988Sjohnlev 		 * devices - don't attempt to iterate over the store.
880843e1988Sjohnlev 		 */
881843e1988Sjohnlev 		if (dom0 && (xdcp->xsdev != NULL))
882843e1988Sjohnlev 			continue;
883843e1988Sjohnlev 
884843e1988Sjohnlev 		/*
885843e1988Sjohnlev 		 * If the store is not yet available, don't attempt to
886843e1988Sjohnlev 		 * iterate.
887843e1988Sjohnlev 		 */
888843e1988Sjohnlev 		if (store_unavailable && (xdcp->xsdev != NULL))
889843e1988Sjohnlev 			continue;
890843e1988Sjohnlev 
891843e1988Sjohnlev 		xendev_enum_class(parent, xdcp->devclass);
892843e1988Sjohnlev 	}
893843e1988Sjohnlev }
894843e1988Sjohnlev 
895843e1988Sjohnlev xendev_devclass_t
896843e1988Sjohnlev xendev_nodename_to_devclass(char *nodename)
897843e1988Sjohnlev {
898843e1988Sjohnlev 	int i;
899843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
900843e1988Sjohnlev 
901843e1988Sjohnlev 	/*
902843e1988Sjohnlev 	 * This relies on the convention that variants of a base
903843e1988Sjohnlev 	 * driver share the same prefix and that there are no drivers
904843e1988Sjohnlev 	 * which share a common prefix with the name of any other base
905843e1988Sjohnlev 	 * drivers.
906843e1988Sjohnlev 	 *
907843e1988Sjohnlev 	 * So for a base driver 'xnb' (which is the name listed in
908843e1988Sjohnlev 	 * xdci) the variants all begin with the string 'xnb' (in fact
909843e1988Sjohnlev 	 * they are 'xnbe', 'xnbo' and 'xnbu') and there are no other
910843e1988Sjohnlev 	 * base drivers which have the prefix 'xnb'.
911843e1988Sjohnlev 	 */
912843e1988Sjohnlev 	ASSERT(nodename != NULL);
913843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
914843e1988Sjohnlev 		if (((xdcp->node_fe != NULL) &&
915843e1988Sjohnlev 		    (strncmp(nodename, xdcp->node_fe,
916843e1988Sjohnlev 		    strlen(xdcp->node_fe)) == 0)) ||
917843e1988Sjohnlev 		    ((xdcp->node_be != NULL) &&
918843e1988Sjohnlev 		    (strncmp(nodename, xdcp->node_be,
919843e1988Sjohnlev 		    strlen(xdcp->node_be)) == 0)))
920843e1988Sjohnlev 
921843e1988Sjohnlev 			return (xdcp->devclass);
922843e1988Sjohnlev 	}
923843e1988Sjohnlev 	return (XEN_INVAL);
924843e1988Sjohnlev }
925843e1988Sjohnlev 
926843e1988Sjohnlev int
927843e1988Sjohnlev xendev_devclass_ipl(xendev_devclass_t devclass)
928843e1988Sjohnlev {
929843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
930843e1988Sjohnlev 
931843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
932843e1988Sjohnlev 	ASSERT(xdcp != NULL);
933843e1988Sjohnlev 
934843e1988Sjohnlev 	return (xdcp->xd_ipl);
935843e1988Sjohnlev }
936843e1988Sjohnlev 
937843e1988Sjohnlev /*
938843e1988Sjohnlev  * Determine if a devinfo instance exists of a particular device
939843e1988Sjohnlev  * class, domain and xenstore virtual device number.
940843e1988Sjohnlev  */
941843e1988Sjohnlev dev_info_t *
942843e1988Sjohnlev xvdi_find_dev(dev_info_t *parent, xendev_devclass_t devclass,
943843e1988Sjohnlev     domid_t dom, int vdev)
944843e1988Sjohnlev {
945843e1988Sjohnlev 	dev_info_t *dip;
946843e1988Sjohnlev 
947843e1988Sjohnlev 	ASSERT(DEVI_BUSY_OWNED(parent));
948843e1988Sjohnlev 
949843e1988Sjohnlev 	switch (devclass) {
950843e1988Sjohnlev 	case XEN_CONSOLE:
951843e1988Sjohnlev 	case XEN_XENBUS:
952843e1988Sjohnlev 	case XEN_DOMCAPS:
953843e1988Sjohnlev 	case XEN_BALLOON:
954843e1988Sjohnlev 	case XEN_EVTCHN:
955843e1988Sjohnlev 	case XEN_PRIVCMD:
956843e1988Sjohnlev 		/* Console and soft devices have no vdev. */
9571ca30e39Sjohnlev 		vdev = VDEV_NOXS;
958843e1988Sjohnlev 		break;
959843e1988Sjohnlev 	default:
960843e1988Sjohnlev 		break;
961843e1988Sjohnlev 	}
962843e1988Sjohnlev 
963843e1988Sjohnlev 	for (dip = ddi_get_child(parent); dip != NULL;
964843e1988Sjohnlev 	    dip = ddi_get_next_sibling(dip)) {
965843e1988Sjohnlev 		int *vdevnump, *domidp, *devclsp, vdevnum;
966843e1988Sjohnlev 		uint_t ndomid, nvdevnum, ndevcls;
967843e1988Sjohnlev 		xendev_devclass_t devcls;
968843e1988Sjohnlev 		domid_t domid;
969843e1988Sjohnlev 		struct xendev_ppd *pdp = ddi_get_parent_data(dip);
970843e1988Sjohnlev 
971843e1988Sjohnlev 		if (pdp == NULL) {
972843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
973843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "domain", &domidp, &ndomid) !=
974843e1988Sjohnlev 			    DDI_PROP_SUCCESS)
975843e1988Sjohnlev 				continue;
976843e1988Sjohnlev 			ASSERT(ndomid == 1);
977843e1988Sjohnlev 			domid = (domid_t)*domidp;
978843e1988Sjohnlev 			ddi_prop_free(domidp);
979843e1988Sjohnlev 
980843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
981843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "vdev", &vdevnump, &nvdevnum) !=
982843e1988Sjohnlev 			    DDI_PROP_SUCCESS)
983843e1988Sjohnlev 				continue;
984843e1988Sjohnlev 			ASSERT(nvdevnum == 1);
985843e1988Sjohnlev 			vdevnum = *vdevnump;
986843e1988Sjohnlev 			ddi_prop_free(vdevnump);
987843e1988Sjohnlev 
988843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
989843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "devclass", &devclsp,
990843e1988Sjohnlev 			    &ndevcls) != DDI_PROP_SUCCESS)
991843e1988Sjohnlev 				continue;
992843e1988Sjohnlev 			ASSERT(ndevcls == 1);
993843e1988Sjohnlev 			devcls = (xendev_devclass_t)*devclsp;
994843e1988Sjohnlev 			ddi_prop_free(devclsp);
995843e1988Sjohnlev 		} else {
996843e1988Sjohnlev 			domid = pdp->xd_domain;
997843e1988Sjohnlev 			vdevnum = pdp->xd_vdevnum;
998843e1988Sjohnlev 			devcls = pdp->xd_devclass;
999843e1988Sjohnlev 		}
1000843e1988Sjohnlev 
1001843e1988Sjohnlev 		if ((domid == dom) && (vdevnum == vdev) && (devcls == devclass))
1002843e1988Sjohnlev 			return (dip);
1003843e1988Sjohnlev 	}
1004843e1988Sjohnlev 	return (NULL);
1005843e1988Sjohnlev }
1006843e1988Sjohnlev 
1007843e1988Sjohnlev int
1008843e1988Sjohnlev xvdi_get_evtchn(dev_info_t *xdip)
1009843e1988Sjohnlev {
1010843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1011843e1988Sjohnlev 
1012843e1988Sjohnlev 	ASSERT(pdp != NULL);
1013843e1988Sjohnlev 	return (pdp->xd_evtchn);
1014843e1988Sjohnlev }
1015843e1988Sjohnlev 
1016843e1988Sjohnlev int
1017843e1988Sjohnlev xvdi_get_vdevnum(dev_info_t *xdip)
1018843e1988Sjohnlev {
1019843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1020843e1988Sjohnlev 
1021843e1988Sjohnlev 	ASSERT(pdp != NULL);
1022843e1988Sjohnlev 	return (pdp->xd_vdevnum);
1023843e1988Sjohnlev }
1024843e1988Sjohnlev 
1025843e1988Sjohnlev char *
1026843e1988Sjohnlev xvdi_get_xsname(dev_info_t *xdip)
1027843e1988Sjohnlev {
1028843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1029843e1988Sjohnlev 
1030843e1988Sjohnlev 	ASSERT(pdp != NULL);
1031843e1988Sjohnlev 	return ((char *)(pdp->xd_xsdev.nodename));
1032843e1988Sjohnlev }
1033843e1988Sjohnlev 
1034843e1988Sjohnlev char *
1035843e1988Sjohnlev xvdi_get_oename(dev_info_t *xdip)
1036843e1988Sjohnlev {
1037843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1038843e1988Sjohnlev 
1039843e1988Sjohnlev 	ASSERT(pdp != NULL);
1040843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE)
1041843e1988Sjohnlev 		return (NULL);
1042843e1988Sjohnlev 	return ((char *)(pdp->xd_xsdev.otherend));
1043843e1988Sjohnlev }
1044843e1988Sjohnlev 
1045843e1988Sjohnlev struct xenbus_device *
1046843e1988Sjohnlev xvdi_get_xsd(dev_info_t *xdip)
1047843e1988Sjohnlev {
1048843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1049843e1988Sjohnlev 
1050843e1988Sjohnlev 	ASSERT(pdp != NULL);
1051843e1988Sjohnlev 	return (&pdp->xd_xsdev);
1052843e1988Sjohnlev }
1053843e1988Sjohnlev 
1054843e1988Sjohnlev domid_t
1055843e1988Sjohnlev xvdi_get_oeid(dev_info_t *xdip)
1056843e1988Sjohnlev {
1057843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1058843e1988Sjohnlev 
1059843e1988Sjohnlev 	ASSERT(pdp != NULL);
1060843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE)
1061843e1988Sjohnlev 		return ((domid_t)-1);
1062843e1988Sjohnlev 	return ((domid_t)(pdp->xd_xsdev.otherend_id));
1063843e1988Sjohnlev }
1064843e1988Sjohnlev 
1065843e1988Sjohnlev void
1066843e1988Sjohnlev xvdi_dev_error(dev_info_t *dip, int errno, char *errstr)
1067843e1988Sjohnlev {
1068843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1069843e1988Sjohnlev 
1070843e1988Sjohnlev 	ASSERT(pdp != NULL);
1071843e1988Sjohnlev 	xenbus_dev_error(&pdp->xd_xsdev, errno, errstr);
1072843e1988Sjohnlev }
1073843e1988Sjohnlev 
1074843e1988Sjohnlev void
1075843e1988Sjohnlev xvdi_fatal_error(dev_info_t *dip, int errno, char *errstr)
1076843e1988Sjohnlev {
1077843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1078843e1988Sjohnlev 
1079843e1988Sjohnlev 	ASSERT(pdp != NULL);
1080843e1988Sjohnlev 	xenbus_dev_fatal(&pdp->xd_xsdev, errno, errstr);
1081843e1988Sjohnlev }
1082843e1988Sjohnlev 
1083843e1988Sjohnlev static void
1084843e1988Sjohnlev i_xvdi_oestate_handler(void *arg)
1085843e1988Sjohnlev {
1086eea6c6b9SMax zhen 	i_oestate_evt_t *evt = (i_oestate_evt_t *)arg;
1087eea6c6b9SMax zhen 	dev_info_t *dip = evt->dip;
1088843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1089843e1988Sjohnlev 	XenbusState oestate = pdp->xd_xsdev.otherend_state;
1090eea6c6b9SMax zhen 	XenbusState curr_oestate = evt->state;
1091843e1988Sjohnlev 	ddi_eventcookie_t evc;
1092843e1988Sjohnlev 
1093eea6c6b9SMax zhen 	/* evt is alloc'ed in i_xvdi_oestate_cb */
1094eea6c6b9SMax zhen 	kmem_free(evt, sizeof (i_oestate_evt_t));
1095eea6c6b9SMax zhen 
1096eea6c6b9SMax zhen 	/*
1097eea6c6b9SMax zhen 	 * If the oestate we're handling is not the latest one,
1098eea6c6b9SMax zhen 	 * it does not make any sense to continue handling it.
1099eea6c6b9SMax zhen 	 */
1100eea6c6b9SMax zhen 	if (curr_oestate != oestate)
1101eea6c6b9SMax zhen 		return;
1102eea6c6b9SMax zhen 
11037eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1104843e1988Sjohnlev 
1105843e1988Sjohnlev 	if (pdp->xd_oe_ehid != NULL) {
1106843e1988Sjohnlev 		/* send notification to driver */
1107843e1988Sjohnlev 		if (ddi_get_eventcookie(dip, XS_OE_STATE,
1108843e1988Sjohnlev 		    &evc) == DDI_SUCCESS) {
11097eea693dSMark Johnson 			mutex_exit(&pdp->xd_ndi_lk);
1110843e1988Sjohnlev 			(void) ndi_post_event(dip, dip, evc, &oestate);
11117eea693dSMark Johnson 			mutex_enter(&pdp->xd_ndi_lk);
1112843e1988Sjohnlev 		}
1113843e1988Sjohnlev 	} else {
1114843e1988Sjohnlev 		/*
1115843e1988Sjohnlev 		 * take default action, if driver hasn't registered its
1116843e1988Sjohnlev 		 * event handler yet
1117843e1988Sjohnlev 		 */
1118843e1988Sjohnlev 		if (oestate == XenbusStateClosing) {
1119843e1988Sjohnlev 			(void) xvdi_switch_state(dip, XBT_NULL,
1120843e1988Sjohnlev 			    XenbusStateClosed);
1121843e1988Sjohnlev 		} else if (oestate == XenbusStateClosed) {
1122843e1988Sjohnlev 			(void) xvdi_switch_state(dip, XBT_NULL,
1123843e1988Sjohnlev 			    XenbusStateClosed);
1124843e1988Sjohnlev 			(void) xvdi_post_event(dip, XEN_HP_REMOVE);
1125843e1988Sjohnlev 		}
1126843e1988Sjohnlev 	}
1127843e1988Sjohnlev 
11287eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1129843e1988Sjohnlev 
1130843e1988Sjohnlev 	/*
1131843e1988Sjohnlev 	 * We'll try to remove the devinfo node of this device if the
1132843e1988Sjohnlev 	 * other end has closed.
1133843e1988Sjohnlev 	 */
1134843e1988Sjohnlev 	if (oestate == XenbusStateClosed)
1135843e1988Sjohnlev 		(void) ddi_taskq_dispatch(DEVI(ddi_get_parent(dip))->devi_taskq,
1136843e1988Sjohnlev 		    xendev_offline_device, dip, DDI_SLEEP);
1137843e1988Sjohnlev }
1138843e1988Sjohnlev 
1139843e1988Sjohnlev static void
1140843e1988Sjohnlev i_xvdi_hpstate_handler(void *arg)
1141843e1988Sjohnlev {
1142843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)arg;
1143843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1144843e1988Sjohnlev 	ddi_eventcookie_t evc;
1145843e1988Sjohnlev 	char *hp_status;
1146843e1988Sjohnlev 	unsigned int hpl;
1147843e1988Sjohnlev 
11487eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1149843e1988Sjohnlev 	if ((ddi_get_eventcookie(dip, XS_HP_STATE, &evc) == DDI_SUCCESS) &&
1150843e1988Sjohnlev 	    (xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
1151843e1988Sjohnlev 	    (void *)&hp_status, &hpl) == 0)) {
1152843e1988Sjohnlev 
1153843e1988Sjohnlev 		xendev_hotplug_state_t new_state = Unrecognized;
1154843e1988Sjohnlev 
1155843e1988Sjohnlev 		if (strcmp(hp_status, "connected") == 0)
1156843e1988Sjohnlev 			new_state = Connected;
1157843e1988Sjohnlev 
11587eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1159843e1988Sjohnlev 
1160843e1988Sjohnlev 		(void) ndi_post_event(dip, dip, evc, &new_state);
1161843e1988Sjohnlev 		kmem_free(hp_status, hpl);
1162843e1988Sjohnlev 		return;
1163843e1988Sjohnlev 	}
11647eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1165843e1988Sjohnlev }
1166843e1988Sjohnlev 
1167843e1988Sjohnlev void
1168843e1988Sjohnlev xvdi_notify_oe(dev_info_t *dip)
1169843e1988Sjohnlev {
1170843e1988Sjohnlev 	struct xendev_ppd *pdp;
1171843e1988Sjohnlev 
1172843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1173843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn != INVALID_EVTCHN);
1174843e1988Sjohnlev 	ec_notify_via_evtchn(pdp->xd_evtchn);
1175843e1988Sjohnlev }
1176843e1988Sjohnlev 
1177843e1988Sjohnlev static void
1178843e1988Sjohnlev i_xvdi_bepath_cb(struct xenbus_watch *w, const char **vec, unsigned int len)
1179843e1988Sjohnlev {
1180843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)w->dev;
1181843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1182843e1988Sjohnlev 	char *be = NULL;
1183843e1988Sjohnlev 	unsigned int bel;
1184843e1988Sjohnlev 
1185843e1988Sjohnlev 	ASSERT(len > XS_WATCH_PATH);
1186843e1988Sjohnlev 	ASSERT(vec[XS_WATCH_PATH] != NULL);
1187843e1988Sjohnlev 
1188843e1988Sjohnlev 	/*
1189843e1988Sjohnlev 	 * If the backend is not the same as that we already stored,
1190843e1988Sjohnlev 	 * re-set our watch for its' state.
1191843e1988Sjohnlev 	 */
1192843e1988Sjohnlev 	if ((xenbus_read(XBT_NULL, "", vec[XS_WATCH_PATH], (void *)be, &bel)
1193843e1988Sjohnlev 	    == 0) && (strcmp(be, pdp->xd_xsdev.otherend) != 0))
1194843e1988Sjohnlev 		(void) i_xvdi_add_watch_oestate(dip);
1195843e1988Sjohnlev 
1196843e1988Sjohnlev 	if (be != NULL) {
1197843e1988Sjohnlev 		ASSERT(bel > 0);
1198843e1988Sjohnlev 		kmem_free(be, bel);
1199843e1988Sjohnlev 	}
1200843e1988Sjohnlev }
1201843e1988Sjohnlev 
1202*7f0b8309SEdward Pilatowicz static void
1203*7f0b8309SEdward Pilatowicz i_xvdi_xb_watch_free(xd_xb_watches_t *xxwp)
1204*7f0b8309SEdward Pilatowicz {
1205*7f0b8309SEdward Pilatowicz 	ASSERT(xxwp->xxw_ref == 0);
1206*7f0b8309SEdward Pilatowicz 	strfree((char *)xxwp->xxw_watch.node);
1207*7f0b8309SEdward Pilatowicz 	kmem_free(xxwp, sizeof (*xxwp));
1208*7f0b8309SEdward Pilatowicz }
1209*7f0b8309SEdward Pilatowicz 
1210*7f0b8309SEdward Pilatowicz static void
1211*7f0b8309SEdward Pilatowicz i_xvdi_xb_watch_release(xd_xb_watches_t *xxwp)
1212*7f0b8309SEdward Pilatowicz {
1213*7f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&xxwp->xxw_xppd->xd_ndi_lk));
1214*7f0b8309SEdward Pilatowicz 	ASSERT(xxwp->xxw_ref > 0);
1215*7f0b8309SEdward Pilatowicz 	if (--xxwp->xxw_ref == 0)
1216*7f0b8309SEdward Pilatowicz 		i_xvdi_xb_watch_free(xxwp);
1217*7f0b8309SEdward Pilatowicz }
1218*7f0b8309SEdward Pilatowicz 
1219*7f0b8309SEdward Pilatowicz static void
1220*7f0b8309SEdward Pilatowicz i_xvdi_xb_watch_hold(xd_xb_watches_t *xxwp)
1221*7f0b8309SEdward Pilatowicz {
1222*7f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&xxwp->xxw_xppd->xd_ndi_lk));
1223*7f0b8309SEdward Pilatowicz 	ASSERT(xxwp->xxw_ref > 0);
1224*7f0b8309SEdward Pilatowicz 	xxwp->xxw_ref++;
1225*7f0b8309SEdward Pilatowicz }
1226*7f0b8309SEdward Pilatowicz 
1227*7f0b8309SEdward Pilatowicz static void
1228*7f0b8309SEdward Pilatowicz i_xvdi_xb_watch_cb_tq(void *arg)
1229*7f0b8309SEdward Pilatowicz {
1230*7f0b8309SEdward Pilatowicz 	xd_xb_watches_t		*xxwp = (xd_xb_watches_t *)arg;
1231*7f0b8309SEdward Pilatowicz 	dev_info_t		*dip = (dev_info_t *)xxwp->xxw_watch.dev;
1232*7f0b8309SEdward Pilatowicz 	struct xendev_ppd	*pdp = xxwp->xxw_xppd;
1233*7f0b8309SEdward Pilatowicz 
1234*7f0b8309SEdward Pilatowicz 	xxwp->xxw_cb(dip, xxwp->xxw_watch.node, xxwp->xxw_arg);
1235*7f0b8309SEdward Pilatowicz 
1236*7f0b8309SEdward Pilatowicz 	mutex_enter(&pdp->xd_ndi_lk);
1237*7f0b8309SEdward Pilatowicz 	i_xvdi_xb_watch_release(xxwp);
1238*7f0b8309SEdward Pilatowicz 	mutex_exit(&pdp->xd_ndi_lk);
1239*7f0b8309SEdward Pilatowicz }
1240*7f0b8309SEdward Pilatowicz 
1241*7f0b8309SEdward Pilatowicz static void
1242*7f0b8309SEdward Pilatowicz i_xvdi_xb_watch_cb(struct xenbus_watch *w, const char **vec, unsigned int len)
1243*7f0b8309SEdward Pilatowicz {
1244*7f0b8309SEdward Pilatowicz 	dev_info_t		*dip = (dev_info_t *)w->dev;
1245*7f0b8309SEdward Pilatowicz 	struct xendev_ppd	*pdp = ddi_get_parent_data(dip);
1246*7f0b8309SEdward Pilatowicz 	xd_xb_watches_t		*xxwp;
1247*7f0b8309SEdward Pilatowicz 
1248*7f0b8309SEdward Pilatowicz 	ASSERT(len > XS_WATCH_PATH);
1249*7f0b8309SEdward Pilatowicz 	ASSERT(vec[XS_WATCH_PATH] != NULL);
1250*7f0b8309SEdward Pilatowicz 
1251*7f0b8309SEdward Pilatowicz 	mutex_enter(&pdp->xd_ndi_lk);
1252*7f0b8309SEdward Pilatowicz 	for (xxwp = list_head(&pdp->xd_xb_watches); xxwp != NULL;
1253*7f0b8309SEdward Pilatowicz 	    xxwp = list_next(&pdp->xd_xb_watches, xxwp)) {
1254*7f0b8309SEdward Pilatowicz 		if (w == &xxwp->xxw_watch)
1255*7f0b8309SEdward Pilatowicz 			break;
1256*7f0b8309SEdward Pilatowicz 	}
1257*7f0b8309SEdward Pilatowicz 
1258*7f0b8309SEdward Pilatowicz 	if (xxwp == NULL) {
1259*7f0b8309SEdward Pilatowicz 		mutex_exit(&pdp->xd_ndi_lk);
1260*7f0b8309SEdward Pilatowicz 		return;
1261*7f0b8309SEdward Pilatowicz 	}
1262*7f0b8309SEdward Pilatowicz 
1263*7f0b8309SEdward Pilatowicz 	i_xvdi_xb_watch_hold(xxwp);
1264*7f0b8309SEdward Pilatowicz 	(void) ddi_taskq_dispatch(pdp->xd_xb_watch_taskq,
1265*7f0b8309SEdward Pilatowicz 	    i_xvdi_xb_watch_cb_tq, xxwp, DDI_SLEEP);
1266*7f0b8309SEdward Pilatowicz 	mutex_exit(&pdp->xd_ndi_lk);
1267*7f0b8309SEdward Pilatowicz }
1268*7f0b8309SEdward Pilatowicz 
1269*7f0b8309SEdward Pilatowicz /*
1270*7f0b8309SEdward Pilatowicz  * Any watches registered with xvdi_add_xb_watch_handler() get torn down during
1271*7f0b8309SEdward Pilatowicz  * a suspend operation.  So if a frontend driver want's to use these interfaces,
1272*7f0b8309SEdward Pilatowicz  * that driver is responsible for re-registering any watches it had before
1273*7f0b8309SEdward Pilatowicz  * the suspend operation.
1274*7f0b8309SEdward Pilatowicz  */
1275*7f0b8309SEdward Pilatowicz int
1276*7f0b8309SEdward Pilatowicz xvdi_add_xb_watch_handler(dev_info_t *dip, const char *dir, const char *node,
1277*7f0b8309SEdward Pilatowicz     xvdi_xb_watch_cb_t cb, void *arg)
1278*7f0b8309SEdward Pilatowicz {
1279*7f0b8309SEdward Pilatowicz 	struct xendev_ppd	*pdp = ddi_get_parent_data(dip);
1280*7f0b8309SEdward Pilatowicz 	xd_xb_watches_t		*xxw_new, *xxwp;
1281*7f0b8309SEdward Pilatowicz 	char			*path;
1282*7f0b8309SEdward Pilatowicz 	int			n;
1283*7f0b8309SEdward Pilatowicz 
1284*7f0b8309SEdward Pilatowicz 	ASSERT((dip != NULL) && (dir != NULL) && (node != NULL));
1285*7f0b8309SEdward Pilatowicz 	ASSERT(cb != NULL);
1286*7f0b8309SEdward Pilatowicz 
1287*7f0b8309SEdward Pilatowicz 	n = strlen(dir) + 1 + strlen(node) + 1;
1288*7f0b8309SEdward Pilatowicz 	path = kmem_zalloc(n, KM_SLEEP);
1289*7f0b8309SEdward Pilatowicz 	(void) strlcat(path, dir, n);
1290*7f0b8309SEdward Pilatowicz 	(void) strlcat(path, "/", n);
1291*7f0b8309SEdward Pilatowicz 	(void) strlcat(path, node, n);
1292*7f0b8309SEdward Pilatowicz 	ASSERT((strlen(path) + 1) == n);
1293*7f0b8309SEdward Pilatowicz 
1294*7f0b8309SEdward Pilatowicz 	xxw_new = kmem_zalloc(sizeof (*xxw_new), KM_SLEEP);
1295*7f0b8309SEdward Pilatowicz 	xxw_new->xxw_ref = 1;
1296*7f0b8309SEdward Pilatowicz 	xxw_new->xxw_watch.node = path;
1297*7f0b8309SEdward Pilatowicz 	xxw_new->xxw_watch.callback = i_xvdi_xb_watch_cb;
1298*7f0b8309SEdward Pilatowicz 	xxw_new->xxw_watch.dev = (struct xenbus_device *)dip;
1299*7f0b8309SEdward Pilatowicz 	xxw_new->xxw_xppd = pdp;
1300*7f0b8309SEdward Pilatowicz 	xxw_new->xxw_cb = cb;
1301*7f0b8309SEdward Pilatowicz 	xxw_new->xxw_arg = arg;
1302*7f0b8309SEdward Pilatowicz 
1303*7f0b8309SEdward Pilatowicz 	mutex_enter(&pdp->xd_ndi_lk);
1304*7f0b8309SEdward Pilatowicz 
1305*7f0b8309SEdward Pilatowicz 	/*
1306*7f0b8309SEdward Pilatowicz 	 * If this is the first watch we're setting up, create a taskq
1307*7f0b8309SEdward Pilatowicz 	 * to dispatch watch events and initialize the watch list.
1308*7f0b8309SEdward Pilatowicz 	 */
1309*7f0b8309SEdward Pilatowicz 	if (pdp->xd_xb_watch_taskq == NULL) {
1310*7f0b8309SEdward Pilatowicz 		char tq_name[TASKQ_NAMELEN];
1311*7f0b8309SEdward Pilatowicz 
1312*7f0b8309SEdward Pilatowicz 		ASSERT(list_is_empty(&pdp->xd_xb_watches));
1313*7f0b8309SEdward Pilatowicz 
1314*7f0b8309SEdward Pilatowicz 		(void) snprintf(tq_name, sizeof (tq_name),
1315*7f0b8309SEdward Pilatowicz 		    "%s_xb_watch_tq", ddi_get_name(dip));
1316*7f0b8309SEdward Pilatowicz 
1317*7f0b8309SEdward Pilatowicz 		if ((pdp->xd_xb_watch_taskq = ddi_taskq_create(dip, tq_name,
1318*7f0b8309SEdward Pilatowicz 		    1, TASKQ_DEFAULTPRI, 0)) == NULL) {
1319*7f0b8309SEdward Pilatowicz 			i_xvdi_xb_watch_release(xxw_new);
1320*7f0b8309SEdward Pilatowicz 			mutex_exit(&pdp->xd_ndi_lk);
1321*7f0b8309SEdward Pilatowicz 			return (DDI_FAILURE);
1322*7f0b8309SEdward Pilatowicz 		}
1323*7f0b8309SEdward Pilatowicz 	}
1324*7f0b8309SEdward Pilatowicz 
1325*7f0b8309SEdward Pilatowicz 	/* Don't allow duplicate watches to be registered */
1326*7f0b8309SEdward Pilatowicz 	for (xxwp = list_head(&pdp->xd_xb_watches); xxwp != NULL;
1327*7f0b8309SEdward Pilatowicz 	    xxwp = list_next(&pdp->xd_xb_watches, xxwp)) {
1328*7f0b8309SEdward Pilatowicz 
1329*7f0b8309SEdward Pilatowicz 		ASSERT(strcmp(xxwp->xxw_watch.node, path) != 0);
1330*7f0b8309SEdward Pilatowicz 		if (strcmp(xxwp->xxw_watch.node, path) != 0)
1331*7f0b8309SEdward Pilatowicz 			continue;
1332*7f0b8309SEdward Pilatowicz 		i_xvdi_xb_watch_release(xxw_new);
1333*7f0b8309SEdward Pilatowicz 		mutex_exit(&pdp->xd_ndi_lk);
1334*7f0b8309SEdward Pilatowicz 		return (DDI_FAILURE);
1335*7f0b8309SEdward Pilatowicz 	}
1336*7f0b8309SEdward Pilatowicz 
1337*7f0b8309SEdward Pilatowicz 	if (register_xenbus_watch(&xxw_new->xxw_watch) != 0) {
1338*7f0b8309SEdward Pilatowicz 		if (list_is_empty(&pdp->xd_xb_watches)) {
1339*7f0b8309SEdward Pilatowicz 			ddi_taskq_destroy(pdp->xd_xb_watch_taskq);
1340*7f0b8309SEdward Pilatowicz 			pdp->xd_xb_watch_taskq = NULL;
1341*7f0b8309SEdward Pilatowicz 		}
1342*7f0b8309SEdward Pilatowicz 		i_xvdi_xb_watch_release(xxw_new);
1343*7f0b8309SEdward Pilatowicz 		mutex_exit(&pdp->xd_ndi_lk);
1344*7f0b8309SEdward Pilatowicz 		return (DDI_FAILURE);
1345*7f0b8309SEdward Pilatowicz 	}
1346*7f0b8309SEdward Pilatowicz 
1347*7f0b8309SEdward Pilatowicz 	list_insert_head(&pdp->xd_xb_watches, xxw_new);
1348*7f0b8309SEdward Pilatowicz 	mutex_exit(&pdp->xd_ndi_lk);
1349*7f0b8309SEdward Pilatowicz 	return (DDI_SUCCESS);
1350*7f0b8309SEdward Pilatowicz }
1351*7f0b8309SEdward Pilatowicz 
1352*7f0b8309SEdward Pilatowicz /*
1353*7f0b8309SEdward Pilatowicz  * Tear down all xenbus watches registered by the specified dip.
1354*7f0b8309SEdward Pilatowicz  */
1355*7f0b8309SEdward Pilatowicz void
1356*7f0b8309SEdward Pilatowicz xvdi_remove_xb_watch_handlers(dev_info_t *dip)
1357*7f0b8309SEdward Pilatowicz {
1358*7f0b8309SEdward Pilatowicz 	struct xendev_ppd	*pdp = ddi_get_parent_data(dip);
1359*7f0b8309SEdward Pilatowicz 	xd_xb_watches_t		*xxwp;
1360*7f0b8309SEdward Pilatowicz 	ddi_taskq_t		*tq;
1361*7f0b8309SEdward Pilatowicz 
1362*7f0b8309SEdward Pilatowicz 	mutex_enter(&pdp->xd_ndi_lk);
1363*7f0b8309SEdward Pilatowicz 
1364*7f0b8309SEdward Pilatowicz 	while ((xxwp = list_remove_head(&pdp->xd_xb_watches)) != NULL) {
1365*7f0b8309SEdward Pilatowicz 		unregister_xenbus_watch(&xxwp->xxw_watch);
1366*7f0b8309SEdward Pilatowicz 		i_xvdi_xb_watch_release(xxwp);
1367*7f0b8309SEdward Pilatowicz 	}
1368*7f0b8309SEdward Pilatowicz 	ASSERT(list_is_empty(&pdp->xd_xb_watches));
1369*7f0b8309SEdward Pilatowicz 
1370*7f0b8309SEdward Pilatowicz 	/*
1371*7f0b8309SEdward Pilatowicz 	 * We can't hold xd_ndi_lk while we destroy the xd_xb_watch_taskq.
1372*7f0b8309SEdward Pilatowicz 	 * This is because if there are currently any executing taskq threads,
1373*7f0b8309SEdward Pilatowicz 	 * we will block until they are finished, and to finish they need
1374*7f0b8309SEdward Pilatowicz 	 * to aquire xd_ndi_lk in i_xvdi_xb_watch_cb_tq() so they can release
1375*7f0b8309SEdward Pilatowicz 	 * their reference on their corresponding xxwp structure.
1376*7f0b8309SEdward Pilatowicz 	 */
1377*7f0b8309SEdward Pilatowicz 	tq = pdp->xd_xb_watch_taskq;
1378*7f0b8309SEdward Pilatowicz 	pdp->xd_xb_watch_taskq = NULL;
1379*7f0b8309SEdward Pilatowicz 	mutex_exit(&pdp->xd_ndi_lk);
1380*7f0b8309SEdward Pilatowicz 	if (tq != NULL)
1381*7f0b8309SEdward Pilatowicz 		ddi_taskq_destroy(tq);
1382*7f0b8309SEdward Pilatowicz }
1383*7f0b8309SEdward Pilatowicz 
1384843e1988Sjohnlev static int
1385843e1988Sjohnlev i_xvdi_add_watch_oestate(dev_info_t *dip)
1386843e1988Sjohnlev {
1387843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1388843e1988Sjohnlev 
1389843e1988Sjohnlev 	ASSERT(pdp != NULL);
1390843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.nodename != NULL);
13917eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1392843e1988Sjohnlev 
1393843e1988Sjohnlev 	/*
1394843e1988Sjohnlev 	 * Create taskq for delivering other end state change event to
1395843e1988Sjohnlev 	 * this device later.
1396843e1988Sjohnlev 	 *
1397843e1988Sjohnlev 	 * Set nthreads to 1 to make sure that events can be delivered
1398843e1988Sjohnlev 	 * in order.
1399843e1988Sjohnlev 	 *
1400843e1988Sjohnlev 	 * Note: It is _not_ guaranteed that driver can see every
1401843e1988Sjohnlev 	 * xenstore change under the path that it is watching. If two
1402843e1988Sjohnlev 	 * changes happen consecutively in a very short amount of
1403843e1988Sjohnlev 	 * time, it is likely that the driver will see only the last
1404843e1988Sjohnlev 	 * one.
1405843e1988Sjohnlev 	 */
1406843e1988Sjohnlev 	if (pdp->xd_oe_taskq == NULL)
1407843e1988Sjohnlev 		if ((pdp->xd_oe_taskq = ddi_taskq_create(dip,
1408843e1988Sjohnlev 		    "xendev_oe_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
1409843e1988Sjohnlev 			return (DDI_FAILURE);
1410843e1988Sjohnlev 
1411843e1988Sjohnlev 	/*
1412843e1988Sjohnlev 	 * Watch for changes to the XenbusState of otherend.
1413843e1988Sjohnlev 	 */
1414843e1988Sjohnlev 	pdp->xd_xsdev.otherend_state = XenbusStateUnknown;
1415843e1988Sjohnlev 	pdp->xd_xsdev.otherend_changed = i_xvdi_oestate_cb;
1416843e1988Sjohnlev 
1417843e1988Sjohnlev 	if (talk_to_otherend(&pdp->xd_xsdev) != 0) {
1418843e1988Sjohnlev 		i_xvdi_rem_watch_oestate(dip);
1419843e1988Sjohnlev 		return (DDI_FAILURE);
1420843e1988Sjohnlev 	}
1421843e1988Sjohnlev 
1422843e1988Sjohnlev 	return (DDI_SUCCESS);
1423843e1988Sjohnlev }
1424843e1988Sjohnlev 
1425843e1988Sjohnlev static void
1426843e1988Sjohnlev i_xvdi_rem_watch_oestate(dev_info_t *dip)
1427843e1988Sjohnlev {
1428843e1988Sjohnlev 	struct xendev_ppd *pdp;
1429843e1988Sjohnlev 	struct xenbus_device *dev;
1430843e1988Sjohnlev 
1431843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1432843e1988Sjohnlev 	ASSERT(pdp != NULL);
14337eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1434843e1988Sjohnlev 
1435843e1988Sjohnlev 	dev = &pdp->xd_xsdev;
1436843e1988Sjohnlev 
1437843e1988Sjohnlev 	/* Unwatch for changes to XenbusState of otherend */
1438843e1988Sjohnlev 	if (dev->otherend_watch.node != NULL) {
14397eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1440843e1988Sjohnlev 		unregister_xenbus_watch(&dev->otherend_watch);
14417eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1442843e1988Sjohnlev 	}
1443843e1988Sjohnlev 
1444843e1988Sjohnlev 	/* make sure no event handler is running */
1445843e1988Sjohnlev 	if (pdp->xd_oe_taskq != NULL) {
14467eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1447843e1988Sjohnlev 		ddi_taskq_destroy(pdp->xd_oe_taskq);
14487eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1449843e1988Sjohnlev 		pdp->xd_oe_taskq = NULL;
1450843e1988Sjohnlev 	}
1451843e1988Sjohnlev 
1452843e1988Sjohnlev 	/* clean up */
1453843e1988Sjohnlev 	dev->otherend_state = XenbusStateUnknown;
1454843e1988Sjohnlev 	dev->otherend_id = (domid_t)-1;
1455843e1988Sjohnlev 	if (dev->otherend_watch.node != NULL)
1456843e1988Sjohnlev 		kmem_free((void *)dev->otherend_watch.node,
1457843e1988Sjohnlev 		    strlen(dev->otherend_watch.node) + 1);
1458843e1988Sjohnlev 	dev->otherend_watch.node = NULL;
1459843e1988Sjohnlev 	if (dev->otherend != NULL)
1460843e1988Sjohnlev 		kmem_free((void *)dev->otherend, strlen(dev->otherend) + 1);
1461843e1988Sjohnlev 	dev->otherend = NULL;
1462843e1988Sjohnlev }
1463843e1988Sjohnlev 
1464843e1988Sjohnlev static int
1465843e1988Sjohnlev i_xvdi_add_watch_hpstate(dev_info_t *dip)
1466843e1988Sjohnlev {
1467843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1468843e1988Sjohnlev 
1469843e1988Sjohnlev 	ASSERT(pdp != NULL);
1470843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 0);
14717eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1472843e1988Sjohnlev 
1473843e1988Sjohnlev 	/*
1474843e1988Sjohnlev 	 * Create taskq for delivering hotplug status change event to
1475843e1988Sjohnlev 	 * this device later.
1476843e1988Sjohnlev 	 *
1477843e1988Sjohnlev 	 * Set nthreads to 1 to make sure that events can be delivered
1478843e1988Sjohnlev 	 * in order.
1479843e1988Sjohnlev 	 *
1480843e1988Sjohnlev 	 * Note: It is _not_ guaranteed that driver can see every
1481843e1988Sjohnlev 	 * hotplug status change under the path that it is
1482843e1988Sjohnlev 	 * watching. If two changes happen consecutively in a very
1483843e1988Sjohnlev 	 * short amount of time, it is likely that the driver only
1484843e1988Sjohnlev 	 * sees the last one.
1485843e1988Sjohnlev 	 */
1486843e1988Sjohnlev 	if (pdp->xd_hp_taskq == NULL)
1487843e1988Sjohnlev 		if ((pdp->xd_hp_taskq = ddi_taskq_create(dip,
1488843e1988Sjohnlev 		    "xendev_hp_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
1489843e1988Sjohnlev 			return (DDI_FAILURE);
1490843e1988Sjohnlev 
1491843e1988Sjohnlev 	if (pdp->xd_hp_watch.node == NULL) {
1492843e1988Sjohnlev 		size_t len;
1493843e1988Sjohnlev 		char *path;
1494843e1988Sjohnlev 
1495843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.nodename != NULL);
1496843e1988Sjohnlev 
1497843e1988Sjohnlev 		len = strlen(pdp->xd_xsdev.nodename) +
1498843e1988Sjohnlev 		    strlen("/hotplug-status") + 1;
1499843e1988Sjohnlev 		path = kmem_alloc(len, KM_SLEEP);
1500843e1988Sjohnlev 		(void) snprintf(path, len, "%s/hotplug-status",
1501843e1988Sjohnlev 		    pdp->xd_xsdev.nodename);
1502843e1988Sjohnlev 
1503843e1988Sjohnlev 		pdp->xd_hp_watch.node = path;
1504843e1988Sjohnlev 		pdp->xd_hp_watch.callback = i_xvdi_hpstate_cb;
1505843e1988Sjohnlev 		pdp->xd_hp_watch.dev = (struct xenbus_device *)dip; /* yuck! */
1506843e1988Sjohnlev 		if (register_xenbus_watch(&pdp->xd_hp_watch) != 0) {
1507843e1988Sjohnlev 			i_xvdi_rem_watch_hpstate(dip);
1508843e1988Sjohnlev 			return (DDI_FAILURE);
1509843e1988Sjohnlev 		}
1510843e1988Sjohnlev 	}
1511843e1988Sjohnlev 
1512843e1988Sjohnlev 	return (DDI_SUCCESS);
1513843e1988Sjohnlev }
1514843e1988Sjohnlev 
1515843e1988Sjohnlev static void
1516843e1988Sjohnlev i_xvdi_rem_watch_hpstate(dev_info_t *dip)
1517843e1988Sjohnlev {
1518843e1988Sjohnlev 	struct xendev_ppd *pdp;
1519843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1520843e1988Sjohnlev 
1521843e1988Sjohnlev 	ASSERT(pdp != NULL);
1522843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 0);
15237eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1524843e1988Sjohnlev 
1525843e1988Sjohnlev 	/* Unwatch for changes to "hotplug-status" node for backend device. */
1526843e1988Sjohnlev 	if (pdp->xd_hp_watch.node != NULL) {
15277eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1528843e1988Sjohnlev 		unregister_xenbus_watch(&pdp->xd_hp_watch);
15297eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1530843e1988Sjohnlev 	}
1531843e1988Sjohnlev 
1532843e1988Sjohnlev 	/* Make sure no event handler is running. */
1533843e1988Sjohnlev 	if (pdp->xd_hp_taskq != NULL) {
15347eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1535843e1988Sjohnlev 		ddi_taskq_destroy(pdp->xd_hp_taskq);
15367eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1537843e1988Sjohnlev 		pdp->xd_hp_taskq = NULL;
1538843e1988Sjohnlev 	}
1539843e1988Sjohnlev 
1540843e1988Sjohnlev 	/* Clean up. */
1541843e1988Sjohnlev 	if (pdp->xd_hp_watch.node != NULL) {
1542843e1988Sjohnlev 		kmem_free((void *)pdp->xd_hp_watch.node,
1543843e1988Sjohnlev 		    strlen(pdp->xd_hp_watch.node) + 1);
1544843e1988Sjohnlev 		pdp->xd_hp_watch.node = NULL;
1545843e1988Sjohnlev 	}
1546843e1988Sjohnlev }
1547843e1988Sjohnlev 
1548843e1988Sjohnlev static int
1549843e1988Sjohnlev i_xvdi_add_watches(dev_info_t *dip)
1550843e1988Sjohnlev {
1551843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1552843e1988Sjohnlev 
1553843e1988Sjohnlev 	ASSERT(pdp != NULL);
1554843e1988Sjohnlev 
15557eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1556843e1988Sjohnlev 
1557843e1988Sjohnlev 	if (i_xvdi_add_watch_oestate(dip) != DDI_SUCCESS) {
15587eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1559843e1988Sjohnlev 		return (DDI_FAILURE);
1560843e1988Sjohnlev 	}
1561843e1988Sjohnlev 
1562843e1988Sjohnlev 	if (pdp->xd_xsdev.frontend == 1) {
1563843e1988Sjohnlev 		/*
1564843e1988Sjohnlev 		 * Frontend devices must watch for the backend path
1565843e1988Sjohnlev 		 * changing.
1566843e1988Sjohnlev 		 */
1567843e1988Sjohnlev 		if (i_xvdi_add_watch_bepath(dip) != DDI_SUCCESS)
1568843e1988Sjohnlev 			goto unwatch_and_fail;
1569843e1988Sjohnlev 	} else {
1570843e1988Sjohnlev 		/*
1571843e1988Sjohnlev 		 * Backend devices must watch for hotplug events.
1572843e1988Sjohnlev 		 */
1573843e1988Sjohnlev 		if (i_xvdi_add_watch_hpstate(dip) != DDI_SUCCESS)
1574843e1988Sjohnlev 			goto unwatch_and_fail;
1575843e1988Sjohnlev 	}
1576843e1988Sjohnlev 
15777eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1578843e1988Sjohnlev 
1579843e1988Sjohnlev 	return (DDI_SUCCESS);
1580843e1988Sjohnlev 
1581843e1988Sjohnlev unwatch_and_fail:
1582843e1988Sjohnlev 	i_xvdi_rem_watch_oestate(dip);
15837eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1584843e1988Sjohnlev 
1585843e1988Sjohnlev 	return (DDI_FAILURE);
1586843e1988Sjohnlev }
1587843e1988Sjohnlev 
1588843e1988Sjohnlev static void
1589843e1988Sjohnlev i_xvdi_rem_watches(dev_info_t *dip)
1590843e1988Sjohnlev {
1591843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1592843e1988Sjohnlev 
1593843e1988Sjohnlev 	ASSERT(pdp != NULL);
1594843e1988Sjohnlev 
15957eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1596843e1988Sjohnlev 
1597843e1988Sjohnlev 	i_xvdi_rem_watch_oestate(dip);
1598843e1988Sjohnlev 
1599843e1988Sjohnlev 	if (pdp->xd_xsdev.frontend == 1)
1600843e1988Sjohnlev 		i_xvdi_rem_watch_bepath(dip);
1601843e1988Sjohnlev 	else
1602843e1988Sjohnlev 		i_xvdi_rem_watch_hpstate(dip);
1603843e1988Sjohnlev 
16047eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1605*7f0b8309SEdward Pilatowicz 
1606*7f0b8309SEdward Pilatowicz 	xvdi_remove_xb_watch_handlers(dip);
1607843e1988Sjohnlev }
1608843e1988Sjohnlev 
1609843e1988Sjohnlev static int
1610843e1988Sjohnlev i_xvdi_add_watch_bepath(dev_info_t *dip)
1611843e1988Sjohnlev {
1612843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1613843e1988Sjohnlev 
1614843e1988Sjohnlev 	ASSERT(pdp != NULL);
1615843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 1);
1616843e1988Sjohnlev 
1617843e1988Sjohnlev 	/*
1618843e1988Sjohnlev 	 * Frontend devices need to watch for the backend path changing.
1619843e1988Sjohnlev 	 */
1620843e1988Sjohnlev 	if (pdp->xd_bepath_watch.node == NULL) {
1621843e1988Sjohnlev 		size_t len;
1622843e1988Sjohnlev 		char *path;
1623843e1988Sjohnlev 
1624843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.nodename != NULL);
1625843e1988Sjohnlev 
1626843e1988Sjohnlev 		len = strlen(pdp->xd_xsdev.nodename) + strlen("/backend") + 1;
1627843e1988Sjohnlev 		path = kmem_alloc(len, KM_SLEEP);
1628843e1988Sjohnlev 		(void) snprintf(path, len, "%s/backend",
1629843e1988Sjohnlev 		    pdp->xd_xsdev.nodename);
1630843e1988Sjohnlev 
1631843e1988Sjohnlev 		pdp->xd_bepath_watch.node = path;
1632843e1988Sjohnlev 		pdp->xd_bepath_watch.callback = i_xvdi_bepath_cb;
1633843e1988Sjohnlev 		pdp->xd_bepath_watch.dev = (struct xenbus_device *)dip;
1634843e1988Sjohnlev 		if (register_xenbus_watch(&pdp->xd_bepath_watch) != 0) {
1635843e1988Sjohnlev 			kmem_free(path, len);
1636843e1988Sjohnlev 			pdp->xd_bepath_watch.node = NULL;
1637843e1988Sjohnlev 			return (DDI_FAILURE);
1638843e1988Sjohnlev 		}
1639843e1988Sjohnlev 	}
1640843e1988Sjohnlev 
1641843e1988Sjohnlev 	return (DDI_SUCCESS);
1642843e1988Sjohnlev }
1643843e1988Sjohnlev 
1644843e1988Sjohnlev static void
1645843e1988Sjohnlev i_xvdi_rem_watch_bepath(dev_info_t *dip)
1646843e1988Sjohnlev {
1647843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1648843e1988Sjohnlev 
1649843e1988Sjohnlev 	ASSERT(pdp != NULL);
1650843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 1);
16517eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1652843e1988Sjohnlev 
1653843e1988Sjohnlev 	if (pdp->xd_bepath_watch.node != NULL) {
16547eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1655843e1988Sjohnlev 		unregister_xenbus_watch(&pdp->xd_bepath_watch);
16567eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1657843e1988Sjohnlev 
1658843e1988Sjohnlev 		kmem_free((void *)(pdp->xd_bepath_watch.node),
1659843e1988Sjohnlev 		    strlen(pdp->xd_bepath_watch.node) + 1);
1660843e1988Sjohnlev 		pdp->xd_bepath_watch.node = NULL;
1661843e1988Sjohnlev 	}
1662843e1988Sjohnlev }
1663843e1988Sjohnlev 
1664843e1988Sjohnlev int
1665843e1988Sjohnlev xvdi_switch_state(dev_info_t *dip, xenbus_transaction_t xbt,
1666843e1988Sjohnlev     XenbusState newState)
1667843e1988Sjohnlev {
1668843e1988Sjohnlev 	int rv;
1669843e1988Sjohnlev 	struct xendev_ppd *pdp;
1670843e1988Sjohnlev 
1671843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1672843e1988Sjohnlev 	ASSERT(pdp != NULL);
1673843e1988Sjohnlev 
1674843e1988Sjohnlev 	XVDI_DPRINTF(XVDI_DBG_STATE,
1675eea6c6b9SMax zhen 	    "xvdi_switch_state: %s@%s's xenbus state moves to %d\n",
1676eea6c6b9SMax zhen 	    ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
1677eea6c6b9SMax zhen 	    ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
1678eea6c6b9SMax zhen 	    newState);
1679843e1988Sjohnlev 
1680843e1988Sjohnlev 	rv = xenbus_switch_state(&pdp->xd_xsdev, xbt, newState);
1681843e1988Sjohnlev 	if (rv > 0)
1682843e1988Sjohnlev 		cmn_err(CE_WARN, "xvdi_switch_state: change state failed");
1683843e1988Sjohnlev 
1684843e1988Sjohnlev 	return (rv);
1685843e1988Sjohnlev }
1686843e1988Sjohnlev 
1687843e1988Sjohnlev /*
1688843e1988Sjohnlev  * Notify hotplug script running in userland
1689843e1988Sjohnlev  */
1690843e1988Sjohnlev int
1691843e1988Sjohnlev xvdi_post_event(dev_info_t *dip, xendev_hotplug_cmd_t hpc)
1692843e1988Sjohnlev {
1693843e1988Sjohnlev 	struct xendev_ppd *pdp;
1694843e1988Sjohnlev 	nvlist_t *attr_list = NULL;
1695843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
1696843e1988Sjohnlev 	sysevent_id_t eid;
1697843e1988Sjohnlev 	int err;
1698843e1988Sjohnlev 	char devname[256]; /* XXPV dme: ? */
1699843e1988Sjohnlev 
1700843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1701843e1988Sjohnlev 	ASSERT(pdp != NULL);
1702843e1988Sjohnlev 
1703843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(pdp->xd_devclass);
1704843e1988Sjohnlev 	ASSERT(xdcp != NULL);
1705843e1988Sjohnlev 
1706843e1988Sjohnlev 	(void) snprintf(devname, sizeof (devname) - 1, "%s%d",
1707843e1988Sjohnlev 	    ddi_driver_name(dip),  ddi_get_instance(dip));
1708843e1988Sjohnlev 
1709843e1988Sjohnlev 	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME, KM_NOSLEEP);
1710843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1711843e1988Sjohnlev 		goto failure;
1712843e1988Sjohnlev 
1713843e1988Sjohnlev 	err = nvlist_add_int32(attr_list, "domain", pdp->xd_domain);
1714843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1715843e1988Sjohnlev 		goto failure;
1716843e1988Sjohnlev 	err = nvlist_add_int32(attr_list, "vdev", pdp->xd_vdevnum);
1717843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1718843e1988Sjohnlev 		goto failure;
1719843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "devclass", xdcp->xsdev);
1720843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1721843e1988Sjohnlev 		goto failure;
1722843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "device", devname);
1723843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1724843e1988Sjohnlev 		goto failure;
1725843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "fob",
1726843e1988Sjohnlev 	    ((pdp->xd_xsdev.frontend == 1) ? "frontend" : "backend"));
1727843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1728843e1988Sjohnlev 		goto failure;
1729843e1988Sjohnlev 
1730843e1988Sjohnlev 	switch (hpc) {
1731843e1988Sjohnlev 	case XEN_HP_ADD:
1732843e1988Sjohnlev 		err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
1733843e1988Sjohnlev 		    "add", attr_list, &eid, DDI_NOSLEEP);
1734843e1988Sjohnlev 		break;
1735843e1988Sjohnlev 	case XEN_HP_REMOVE:
1736843e1988Sjohnlev 		err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
1737843e1988Sjohnlev 		    "remove", attr_list, &eid, DDI_NOSLEEP);
1738843e1988Sjohnlev 		break;
1739843e1988Sjohnlev 	default:
1740843e1988Sjohnlev 		err = DDI_FAILURE;
1741843e1988Sjohnlev 		goto failure;
1742843e1988Sjohnlev 	}
1743843e1988Sjohnlev 
1744843e1988Sjohnlev failure:
1745843e1988Sjohnlev 	if (attr_list != NULL)
1746843e1988Sjohnlev 		nvlist_free(attr_list);
1747843e1988Sjohnlev 
1748843e1988Sjohnlev 	return (err);
1749843e1988Sjohnlev }
1750843e1988Sjohnlev 
1751843e1988Sjohnlev /* ARGSUSED */
1752843e1988Sjohnlev static void
1753843e1988Sjohnlev i_xvdi_probe_path_cb(struct xenbus_watch *w, const char **vec,
1754843e1988Sjohnlev     unsigned int len)
1755843e1988Sjohnlev {
1756843e1988Sjohnlev 	char *path;
1757843e1988Sjohnlev 
1758843e1988Sjohnlev 	if (xendev_dip == NULL)
1759843e1988Sjohnlev 		xendev_dip = ddi_find_devinfo("xpvd", -1, 0);
1760843e1988Sjohnlev 
1761843e1988Sjohnlev 	path = i_ddi_strdup((char *)vec[XS_WATCH_PATH], KM_SLEEP);
1762843e1988Sjohnlev 
1763843e1988Sjohnlev 	(void) ddi_taskq_dispatch(DEVI(xendev_dip)->devi_taskq,
1764843e1988Sjohnlev 	    i_xvdi_probe_path_handler, (void *)path, DDI_SLEEP);
1765843e1988Sjohnlev }
1766843e1988Sjohnlev 
1767843e1988Sjohnlev static void
1768843e1988Sjohnlev i_xvdi_watch_device(char *path)
1769843e1988Sjohnlev {
1770843e1988Sjohnlev 	struct xenbus_watch *w;
1771843e1988Sjohnlev 
1772843e1988Sjohnlev 	ASSERT(path != NULL);
1773843e1988Sjohnlev 
1774843e1988Sjohnlev 	w = kmem_zalloc(sizeof (*w), KM_SLEEP);
1775843e1988Sjohnlev 	w->node = path;
1776843e1988Sjohnlev 	w->callback = &i_xvdi_probe_path_cb;
1777843e1988Sjohnlev 	w->dev = NULL;
1778843e1988Sjohnlev 
1779843e1988Sjohnlev 	if (register_xenbus_watch(w) != 0) {
1780843e1988Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_watch_device: "
1781843e1988Sjohnlev 		    "cannot set watch on %s", path);
1782843e1988Sjohnlev 		kmem_free(w, sizeof (*w));
1783843e1988Sjohnlev 		return;
1784843e1988Sjohnlev 	}
1785843e1988Sjohnlev }
1786843e1988Sjohnlev 
1787843e1988Sjohnlev void
1788843e1988Sjohnlev xvdi_watch_devices(int newstate)
1789843e1988Sjohnlev {
1790843e1988Sjohnlev 	int devclass;
1791843e1988Sjohnlev 
1792843e1988Sjohnlev 	/*
1793843e1988Sjohnlev 	 * Watch for devices being created in the store.
1794843e1988Sjohnlev 	 */
1795843e1988Sjohnlev 	if (newstate == XENSTORE_DOWN)
1796843e1988Sjohnlev 		return;
1797843e1988Sjohnlev 	for (devclass = 0; devclass < NXDC; devclass++) {
1798843e1988Sjohnlev 		if (xdci[devclass].xs_path_fe != NULL)
1799843e1988Sjohnlev 			i_xvdi_watch_device(xdci[devclass].xs_path_fe);
1800843e1988Sjohnlev 		if (xdci[devclass].xs_path_be != NULL)
1801843e1988Sjohnlev 			i_xvdi_watch_device(xdci[devclass].xs_path_be);
1802843e1988Sjohnlev 	}
1803843e1988Sjohnlev }
1804843e1988Sjohnlev 
1805843e1988Sjohnlev /*
1806843e1988Sjohnlev  * Iterate over the store looking for backend devices to create.
1807843e1988Sjohnlev  */
1808843e1988Sjohnlev static void
1809843e1988Sjohnlev i_xvdi_enum_be(dev_info_t *parent, i_xd_cfg_t *xdcp)
1810843e1988Sjohnlev {
1811843e1988Sjohnlev 	char **domains;
1812843e1988Sjohnlev 	unsigned int ndomains;
1813843e1988Sjohnlev 	int ldomains, i;
1814843e1988Sjohnlev 
1815843e1988Sjohnlev 	if ((domains = xenbus_directory(XBT_NULL, xdcp->xs_path_be, "",
1816843e1988Sjohnlev 	    &ndomains)) == NULL)
1817843e1988Sjohnlev 		return;
1818843e1988Sjohnlev 
1819843e1988Sjohnlev 	for (i = 0, ldomains = 0; i < ndomains; i++) {
1820843e1988Sjohnlev 		ldomains += strlen(domains[i]) + 1 + sizeof (char *);
1821843e1988Sjohnlev 
1822843e1988Sjohnlev 		i_xvdi_enum_worker(parent, xdcp, domains[i]);
1823843e1988Sjohnlev 	}
1824843e1988Sjohnlev 	kmem_free(domains, ldomains);
1825843e1988Sjohnlev }
1826843e1988Sjohnlev 
1827843e1988Sjohnlev /*
1828843e1988Sjohnlev  * Iterate over the store looking for frontend devices to create.
1829843e1988Sjohnlev  */
1830843e1988Sjohnlev static void
1831843e1988Sjohnlev i_xvdi_enum_fe(dev_info_t *parent, i_xd_cfg_t *xdcp)
1832843e1988Sjohnlev {
1833843e1988Sjohnlev 	i_xvdi_enum_worker(parent, xdcp, NULL);
1834843e1988Sjohnlev }
1835843e1988Sjohnlev 
1836843e1988Sjohnlev static void
1837843e1988Sjohnlev i_xvdi_enum_worker(dev_info_t *parent, i_xd_cfg_t *xdcp,
1838843e1988Sjohnlev     char *domain)
1839843e1988Sjohnlev {
1840843e1988Sjohnlev 	char *path, *domain_path, *ep;
1841843e1988Sjohnlev 	char **devices;
1842843e1988Sjohnlev 	unsigned int ndevices;
1843843e1988Sjohnlev 	int ldevices, j, circ;
1844843e1988Sjohnlev 	domid_t dom;
18456e24ea8fSedp 	long tmplong;
1846843e1988Sjohnlev 
1847843e1988Sjohnlev 	if (domain == NULL) {
1848843e1988Sjohnlev 		dom = DOMID_SELF;
1849843e1988Sjohnlev 		path = xdcp->xs_path_fe;
1850843e1988Sjohnlev 		domain_path = "";
1851843e1988Sjohnlev 	} else {
18526e24ea8fSedp 		(void) ddi_strtol(domain, &ep, 0, &tmplong);
18536e24ea8fSedp 		dom = tmplong;
1854843e1988Sjohnlev 		path = xdcp->xs_path_be;
1855843e1988Sjohnlev 		domain_path = domain;
1856843e1988Sjohnlev 	}
1857843e1988Sjohnlev 
1858843e1988Sjohnlev 	if ((devices = xenbus_directory(XBT_NULL, path, domain_path,
1859843e1988Sjohnlev 	    &ndevices)) == NULL)
1860843e1988Sjohnlev 		return;
1861843e1988Sjohnlev 
1862843e1988Sjohnlev 	for (j = 0, ldevices = 0; j < ndevices; j++) {
1863843e1988Sjohnlev 		int vdev;
1864843e1988Sjohnlev 
1865843e1988Sjohnlev 		ldevices += strlen(devices[j]) + 1 + sizeof (char *);
18666e24ea8fSedp 		(void) ddi_strtol(devices[j], &ep, 0, &tmplong);
18676e24ea8fSedp 		vdev = tmplong;
1868843e1988Sjohnlev 
1869843e1988Sjohnlev 		ndi_devi_enter(parent, &circ);
1870843e1988Sjohnlev 
18716e24ea8fSedp 		if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL)
1872843e1988Sjohnlev 			(void) xvdi_create_dev(parent, xdcp->devclass,
1873843e1988Sjohnlev 			    dom, vdev);
1874843e1988Sjohnlev 
1875843e1988Sjohnlev 		ndi_devi_exit(parent, circ);
1876843e1988Sjohnlev 	}
1877843e1988Sjohnlev 	kmem_free(devices, ldevices);
1878843e1988Sjohnlev }
1879843e1988Sjohnlev 
1880843e1988Sjohnlev /*
1881843e1988Sjohnlev  * Leaf drivers should call this in their detach() routine during suspend.
1882843e1988Sjohnlev  */
1883843e1988Sjohnlev void
1884843e1988Sjohnlev xvdi_suspend(dev_info_t *dip)
1885843e1988Sjohnlev {
1886843e1988Sjohnlev 	i_xvdi_rem_watches(dip);
1887843e1988Sjohnlev }
1888843e1988Sjohnlev 
1889843e1988Sjohnlev /*
1890843e1988Sjohnlev  * Leaf drivers should call this in their attach() routine during resume.
1891843e1988Sjohnlev  */
1892843e1988Sjohnlev int
1893843e1988Sjohnlev xvdi_resume(dev_info_t *dip)
1894843e1988Sjohnlev {
1895843e1988Sjohnlev 	return (i_xvdi_add_watches(dip));
1896843e1988Sjohnlev }
1897843e1988Sjohnlev 
1898843e1988Sjohnlev /*
1899843e1988Sjohnlev  * Add event handler for the leaf driver
1900843e1988Sjohnlev  * to handle event triggered by the change in xenstore
1901843e1988Sjohnlev  */
1902843e1988Sjohnlev int
1903843e1988Sjohnlev xvdi_add_event_handler(dev_info_t *dip, char *name,
19047eea693dSMark Johnson     void (*evthandler)(dev_info_t *, ddi_eventcookie_t, void *, void *),
19057eea693dSMark Johnson     void *arg)
1906843e1988Sjohnlev {
1907843e1988Sjohnlev 	ddi_eventcookie_t ecv;
1908843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1909843e1988Sjohnlev 	ddi_callback_id_t *cbid;
19107eea693dSMark Johnson 	boolean_t call_handler;
19117eea693dSMark Johnson 	i_oestate_evt_t *evt = NULL;
19127eea693dSMark Johnson 	XenbusState oestate;
1913843e1988Sjohnlev 
1914843e1988Sjohnlev 	ASSERT(pdp != NULL);
1915843e1988Sjohnlev 
19167eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1917843e1988Sjohnlev 
1918843e1988Sjohnlev 	if (strcmp(name, XS_OE_STATE) == 0) {
1919843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.otherend != NULL);
1920843e1988Sjohnlev 
1921843e1988Sjohnlev 		cbid = &pdp->xd_oe_ehid;
1922843e1988Sjohnlev 	} else if (strcmp(name, XS_HP_STATE) == 0) {
1923843e1988Sjohnlev 		if (pdp->xd_xsdev.frontend == 1) {
19247eea693dSMark Johnson 			mutex_exit(&pdp->xd_ndi_lk);
1925843e1988Sjohnlev 			return (DDI_FAILURE);
1926843e1988Sjohnlev 		}
1927843e1988Sjohnlev 
1928843e1988Sjohnlev 		ASSERT(pdp->xd_hp_watch.node != NULL);
1929843e1988Sjohnlev 
1930843e1988Sjohnlev 		cbid = &pdp->xd_hp_ehid;
1931843e1988Sjohnlev 	} else {
1932843e1988Sjohnlev 		/* Unsupported watch. */
19337eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1934843e1988Sjohnlev 		return (DDI_FAILURE);
1935843e1988Sjohnlev 	}
1936843e1988Sjohnlev 
1937843e1988Sjohnlev 	/*
1938843e1988Sjohnlev 	 * No event handler provided, take default action to handle
1939843e1988Sjohnlev 	 * event.
1940843e1988Sjohnlev 	 */
1941843e1988Sjohnlev 	if (evthandler == NULL) {
19427eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1943843e1988Sjohnlev 		return (DDI_SUCCESS);
1944843e1988Sjohnlev 	}
1945843e1988Sjohnlev 
1946843e1988Sjohnlev 	ASSERT(*cbid == NULL);
1947843e1988Sjohnlev 
1948843e1988Sjohnlev 	if (ddi_get_eventcookie(dip, name, &ecv) != DDI_SUCCESS) {
1949843e1988Sjohnlev 		cmn_err(CE_WARN, "failed to find %s cookie for %s@%s",
1950843e1988Sjohnlev 		    name, ddi_get_name(dip), ddi_get_name_addr(dip));
19517eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1952843e1988Sjohnlev 		return (DDI_FAILURE);
1953843e1988Sjohnlev 	}
19547eea693dSMark Johnson 	if (ddi_add_event_handler(dip, ecv, evthandler, arg, cbid)
1955843e1988Sjohnlev 	    != DDI_SUCCESS) {
1956843e1988Sjohnlev 		cmn_err(CE_WARN, "failed to add %s event handler for %s@%s",
1957843e1988Sjohnlev 		    name, ddi_get_name(dip), ddi_get_name_addr(dip));
1958843e1988Sjohnlev 		*cbid = NULL;
19597eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1960843e1988Sjohnlev 		return (DDI_FAILURE);
1961843e1988Sjohnlev 	}
1962843e1988Sjohnlev 
19637eea693dSMark Johnson 	/*
19647eea693dSMark Johnson 	 * if we're adding an oe state callback, and the ring has already
19657eea693dSMark Johnson 	 * transitioned out of Unknown, call the handler after we release
19667eea693dSMark Johnson 	 * the mutex.
19677eea693dSMark Johnson 	 */
19687eea693dSMark Johnson 	call_handler = B_FALSE;
19697eea693dSMark Johnson 	if ((strcmp(name, XS_OE_STATE) == 0) &&
19707eea693dSMark Johnson 	    (pdp->xd_xsdev.otherend_state != XenbusStateUnknown)) {
19717eea693dSMark Johnson 		oestate = pdp->xd_xsdev.otherend_state;
19727eea693dSMark Johnson 		call_handler = B_TRUE;
19737eea693dSMark Johnson 	}
19747eea693dSMark Johnson 
19757eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
19767eea693dSMark Johnson 
19777eea693dSMark Johnson 	if (call_handler) {
19787eea693dSMark Johnson 		evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP);
19797eea693dSMark Johnson 		evt->dip = dip;
19807eea693dSMark Johnson 		evt->state = oestate;
19817eea693dSMark Johnson 		(void) ddi_taskq_dispatch(pdp->xd_oe_taskq,
19827eea693dSMark Johnson 		    i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP);
19837eea693dSMark Johnson 	}
1984843e1988Sjohnlev 
1985843e1988Sjohnlev 	return (DDI_SUCCESS);
1986843e1988Sjohnlev }
1987843e1988Sjohnlev 
1988843e1988Sjohnlev /*
1989843e1988Sjohnlev  * Remove event handler for the leaf driver and unwatch xenstore
1990843e1988Sjohnlev  * so, driver will not be notified when xenstore entry changed later
1991843e1988Sjohnlev  */
1992843e1988Sjohnlev void
1993843e1988Sjohnlev xvdi_remove_event_handler(dev_info_t *dip, char *name)
1994843e1988Sjohnlev {
1995843e1988Sjohnlev 	struct xendev_ppd *pdp;
1996843e1988Sjohnlev 	boolean_t rem_oe = B_FALSE, rem_hp = B_FALSE;
1997843e1988Sjohnlev 	ddi_callback_id_t oeid = NULL, hpid = NULL;
1998843e1988Sjohnlev 
1999843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
2000843e1988Sjohnlev 	ASSERT(pdp != NULL);
2001843e1988Sjohnlev 
2002843e1988Sjohnlev 	if (name == NULL) {
2003843e1988Sjohnlev 		rem_oe = B_TRUE;
2004843e1988Sjohnlev 		rem_hp = B_TRUE;
2005843e1988Sjohnlev 	} else if (strcmp(name, XS_OE_STATE) == 0) {
2006843e1988Sjohnlev 		rem_oe = B_TRUE;
2007843e1988Sjohnlev 	} else if (strcmp(name, XS_HP_STATE) == 0) {
2008843e1988Sjohnlev 		rem_hp = B_TRUE;
2009843e1988Sjohnlev 	} else {
2010843e1988Sjohnlev 		cmn_err(CE_WARN, "event %s not supported, cannot remove", name);
2011843e1988Sjohnlev 		return;
2012843e1988Sjohnlev 	}
2013843e1988Sjohnlev 
20147eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
2015843e1988Sjohnlev 
2016843e1988Sjohnlev 	if (rem_oe && (pdp->xd_oe_ehid != NULL)) {
2017843e1988Sjohnlev 		oeid = pdp->xd_oe_ehid;
2018843e1988Sjohnlev 		pdp->xd_oe_ehid = NULL;
2019843e1988Sjohnlev 	}
2020843e1988Sjohnlev 
2021843e1988Sjohnlev 	if (rem_hp && (pdp->xd_hp_ehid != NULL)) {
2022843e1988Sjohnlev 		hpid = pdp->xd_hp_ehid;
2023843e1988Sjohnlev 		pdp->xd_hp_ehid = NULL;
2024843e1988Sjohnlev 	}
2025843e1988Sjohnlev 
20267eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
2027843e1988Sjohnlev 
2028843e1988Sjohnlev 	if (oeid != NULL)
2029843e1988Sjohnlev 		(void) ddi_remove_event_handler(oeid);
2030843e1988Sjohnlev 	if (hpid != NULL)
2031843e1988Sjohnlev 		(void) ddi_remove_event_handler(hpid);
2032843e1988Sjohnlev }
2033843e1988Sjohnlev 
2034843e1988Sjohnlev 
2035843e1988Sjohnlev /*
2036843e1988Sjohnlev  * common ring interfaces
2037843e1988Sjohnlev  */
2038843e1988Sjohnlev 
2039843e1988Sjohnlev #define	FRONT_RING(_ringp)	(&(_ringp)->xr_sring.fr)
2040843e1988Sjohnlev #define	BACK_RING(_ringp)	(&(_ringp)->xr_sring.br)
2041843e1988Sjohnlev #define	GET_RING_SIZE(_ringp)	RING_SIZE(FRONT_RING(ringp))
2042843e1988Sjohnlev #define	GET_RING_ENTRY_FE(_ringp, _idx)		\
2043843e1988Sjohnlev 	(FRONT_RING(_ringp)->sring->ring +	\
2044843e1988Sjohnlev 	(_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
2045843e1988Sjohnlev #define	GET_RING_ENTRY_BE(_ringp, _idx)		\
2046843e1988Sjohnlev 	(BACK_RING(_ringp)->sring->ring +	\
2047843e1988Sjohnlev 	(_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
2048843e1988Sjohnlev 
2049843e1988Sjohnlev unsigned int
2050843e1988Sjohnlev xvdi_ring_avail_slots(xendev_ring_t *ringp)
2051843e1988Sjohnlev {
2052843e1988Sjohnlev 	comif_ring_fe_t *frp;
2053843e1988Sjohnlev 	comif_ring_be_t *brp;
2054843e1988Sjohnlev 
2055843e1988Sjohnlev 	if (ringp->xr_frontend) {
2056843e1988Sjohnlev 		frp = FRONT_RING(ringp);
2057843e1988Sjohnlev 		return (GET_RING_SIZE(ringp) -
2058843e1988Sjohnlev 		    (frp->req_prod_pvt - frp->rsp_cons));
2059843e1988Sjohnlev 	} else {
2060843e1988Sjohnlev 		brp = BACK_RING(ringp);
2061843e1988Sjohnlev 		return (GET_RING_SIZE(ringp) -
2062843e1988Sjohnlev 		    (brp->rsp_prod_pvt - brp->req_cons));
2063843e1988Sjohnlev 	}
2064843e1988Sjohnlev }
2065843e1988Sjohnlev 
2066843e1988Sjohnlev int
2067843e1988Sjohnlev xvdi_ring_has_unconsumed_requests(xendev_ring_t *ringp)
2068843e1988Sjohnlev {
2069843e1988Sjohnlev 	comif_ring_be_t *brp;
2070843e1988Sjohnlev 
2071843e1988Sjohnlev 	ASSERT(!ringp->xr_frontend);
2072843e1988Sjohnlev 	brp = BACK_RING(ringp);
2073843e1988Sjohnlev 	return ((brp->req_cons !=
2074843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &brp->sring->req_prod)) &&
2075843e1988Sjohnlev 	    ((brp->req_cons - brp->rsp_prod_pvt) != RING_SIZE(brp)));
2076843e1988Sjohnlev }
2077843e1988Sjohnlev 
2078843e1988Sjohnlev int
2079843e1988Sjohnlev xvdi_ring_has_incomp_request(xendev_ring_t *ringp)
2080843e1988Sjohnlev {
2081843e1988Sjohnlev 	comif_ring_fe_t *frp;
2082843e1988Sjohnlev 
2083843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
2084843e1988Sjohnlev 	frp = FRONT_RING(ringp);
2085843e1988Sjohnlev 	return (frp->req_prod_pvt !=
2086843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
2087843e1988Sjohnlev }
2088843e1988Sjohnlev 
2089843e1988Sjohnlev int
2090843e1988Sjohnlev xvdi_ring_has_unconsumed_responses(xendev_ring_t *ringp)
2091843e1988Sjohnlev {
2092843e1988Sjohnlev 	comif_ring_fe_t *frp;
2093843e1988Sjohnlev 
2094843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
2095843e1988Sjohnlev 	frp = FRONT_RING(ringp);
2096843e1988Sjohnlev 	return (frp->rsp_cons !=
2097843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
2098843e1988Sjohnlev }
2099843e1988Sjohnlev 
2100843e1988Sjohnlev /* NOTE: req_event will be increased as needed */
2101843e1988Sjohnlev void *
2102843e1988Sjohnlev xvdi_ring_get_request(xendev_ring_t *ringp)
2103843e1988Sjohnlev {
2104843e1988Sjohnlev 	comif_ring_fe_t *frp;
2105843e1988Sjohnlev 	comif_ring_be_t *brp;
2106843e1988Sjohnlev 
2107843e1988Sjohnlev 	if (ringp->xr_frontend) {
2108843e1988Sjohnlev 		/* for frontend ring */
2109843e1988Sjohnlev 		frp = FRONT_RING(ringp);
2110843e1988Sjohnlev 		if (!RING_FULL(frp))
2111843e1988Sjohnlev 			return (GET_RING_ENTRY_FE(ringp, frp->req_prod_pvt++));
2112843e1988Sjohnlev 		else
2113843e1988Sjohnlev 			return (NULL);
2114843e1988Sjohnlev 	} else {
2115843e1988Sjohnlev 		/* for backend ring */
2116843e1988Sjohnlev 		brp = BACK_RING(ringp);
2117843e1988Sjohnlev 		/* RING_FINAL_CHECK_FOR_REQUESTS() */
2118843e1988Sjohnlev 		if (xvdi_ring_has_unconsumed_requests(ringp))
2119843e1988Sjohnlev 			return (GET_RING_ENTRY_BE(ringp, brp->req_cons++));
2120843e1988Sjohnlev 		else {
2121843e1988Sjohnlev 			ddi_put32(ringp->xr_acc_hdl, &brp->sring->req_event,
2122843e1988Sjohnlev 			    brp->req_cons + 1);
2123843e1988Sjohnlev 			membar_enter();
2124843e1988Sjohnlev 			if (xvdi_ring_has_unconsumed_requests(ringp))
2125843e1988Sjohnlev 				return (GET_RING_ENTRY_BE(ringp,
2126843e1988Sjohnlev 				    brp->req_cons++));
2127843e1988Sjohnlev 			else
2128843e1988Sjohnlev 				return (NULL);
2129843e1988Sjohnlev 		}
2130843e1988Sjohnlev 	}
2131843e1988Sjohnlev }
2132843e1988Sjohnlev 
2133843e1988Sjohnlev int
2134843e1988Sjohnlev xvdi_ring_push_request(xendev_ring_t *ringp)
2135843e1988Sjohnlev {
2136843e1988Sjohnlev 	RING_IDX old, new, reqevt;
2137843e1988Sjohnlev 	comif_ring_fe_t *frp;
2138843e1988Sjohnlev 
2139843e1988Sjohnlev 	/* only frontend should be able to push request */
2140843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
2141843e1988Sjohnlev 
2142843e1988Sjohnlev 	/* RING_PUSH_REQUEST_AND_CHECK_NOTIFY() */
2143843e1988Sjohnlev 	frp = FRONT_RING(ringp);
2144843e1988Sjohnlev 	old = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_prod);
2145843e1988Sjohnlev 	new = frp->req_prod_pvt;
2146843e1988Sjohnlev 	ddi_put32(ringp->xr_acc_hdl, &frp->sring->req_prod, new);
2147843e1988Sjohnlev 	membar_enter();
2148843e1988Sjohnlev 	reqevt = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_event);
2149843e1988Sjohnlev 	return ((RING_IDX)(new - reqevt) < (RING_IDX)(new - old));
2150843e1988Sjohnlev }
2151843e1988Sjohnlev 
2152843e1988Sjohnlev /* NOTE: rsp_event will be increased as needed */
2153843e1988Sjohnlev void *
2154843e1988Sjohnlev xvdi_ring_get_response(xendev_ring_t *ringp)
2155843e1988Sjohnlev {
2156843e1988Sjohnlev 	comif_ring_fe_t *frp;
2157843e1988Sjohnlev 	comif_ring_be_t *brp;
2158843e1988Sjohnlev 
2159843e1988Sjohnlev 	if (!ringp->xr_frontend) {
2160843e1988Sjohnlev 		/* for backend ring */
2161843e1988Sjohnlev 		brp = BACK_RING(ringp);
2162843e1988Sjohnlev 		return (GET_RING_ENTRY_BE(ringp, brp->rsp_prod_pvt++));
2163843e1988Sjohnlev 	} else {
2164843e1988Sjohnlev 		/* for frontend ring */
2165843e1988Sjohnlev 		frp = FRONT_RING(ringp);
2166843e1988Sjohnlev 		/* RING_FINAL_CHECK_FOR_RESPONSES() */
2167843e1988Sjohnlev 		if (xvdi_ring_has_unconsumed_responses(ringp))
2168843e1988Sjohnlev 			return (GET_RING_ENTRY_FE(ringp, frp->rsp_cons++));
2169843e1988Sjohnlev 		else {
2170843e1988Sjohnlev 			ddi_put32(ringp->xr_acc_hdl, &frp->sring->rsp_event,
2171843e1988Sjohnlev 			    frp->rsp_cons + 1);
2172843e1988Sjohnlev 			membar_enter();
2173843e1988Sjohnlev 			if (xvdi_ring_has_unconsumed_responses(ringp))
2174843e1988Sjohnlev 				return (GET_RING_ENTRY_FE(ringp,
2175843e1988Sjohnlev 				    frp->rsp_cons++));
2176843e1988Sjohnlev 			else
2177843e1988Sjohnlev 				return (NULL);
2178843e1988Sjohnlev 		}
2179843e1988Sjohnlev 	}
2180843e1988Sjohnlev }
2181843e1988Sjohnlev 
2182843e1988Sjohnlev int
2183843e1988Sjohnlev xvdi_ring_push_response(xendev_ring_t *ringp)
2184843e1988Sjohnlev {
2185843e1988Sjohnlev 	RING_IDX old, new, rspevt;
2186843e1988Sjohnlev 	comif_ring_be_t *brp;
2187843e1988Sjohnlev 
2188843e1988Sjohnlev 	/* only backend should be able to push response */
2189843e1988Sjohnlev 	ASSERT(!ringp->xr_frontend);
2190843e1988Sjohnlev 
2191843e1988Sjohnlev 	/* RING_PUSH_RESPONSE_AND_CHECK_NOTIFY() */
2192843e1988Sjohnlev 	brp = BACK_RING(ringp);
2193843e1988Sjohnlev 	old = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_prod);
2194843e1988Sjohnlev 	new = brp->rsp_prod_pvt;
2195843e1988Sjohnlev 	ddi_put32(ringp->xr_acc_hdl, &brp->sring->rsp_prod, new);
2196843e1988Sjohnlev 	membar_enter();
2197843e1988Sjohnlev 	rspevt = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_event);
2198843e1988Sjohnlev 	return ((RING_IDX)(new - rspevt) < (RING_IDX)(new - old));
2199843e1988Sjohnlev }
2200843e1988Sjohnlev 
2201843e1988Sjohnlev static void
2202843e1988Sjohnlev xvdi_ring_init_sring(xendev_ring_t *ringp)
2203843e1988Sjohnlev {
2204843e1988Sjohnlev 	ddi_acc_handle_t acchdl;
2205843e1988Sjohnlev 	comif_sring_t *xsrp;
2206843e1988Sjohnlev 	int i;
2207843e1988Sjohnlev 
2208843e1988Sjohnlev 	xsrp = (comif_sring_t *)ringp->xr_vaddr;
2209843e1988Sjohnlev 	acchdl = ringp->xr_acc_hdl;
2210843e1988Sjohnlev 
2211843e1988Sjohnlev 	/* shared ring initialization */
2212843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->req_prod, 0);
2213843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->rsp_prod, 0);
2214843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->req_event, 1);
2215843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->rsp_event, 1);
2216843e1988Sjohnlev 	for (i = 0; i < sizeof (xsrp->pad); i++)
2217843e1988Sjohnlev 		ddi_put8(acchdl, xsrp->pad + i, 0);
2218843e1988Sjohnlev }
2219843e1988Sjohnlev 
2220843e1988Sjohnlev static void
2221843e1988Sjohnlev xvdi_ring_init_front_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
2222843e1988Sjohnlev {
2223843e1988Sjohnlev 	comif_ring_fe_t *xfrp;
2224843e1988Sjohnlev 
2225843e1988Sjohnlev 	xfrp = &ringp->xr_sring.fr;
2226843e1988Sjohnlev 	xfrp->req_prod_pvt = 0;
2227843e1988Sjohnlev 	xfrp->rsp_cons = 0;
2228843e1988Sjohnlev 	xfrp->nr_ents = nentry;
2229843e1988Sjohnlev 	xfrp->sring = (comif_sring_t *)ringp->xr_vaddr;
2230843e1988Sjohnlev 
2231843e1988Sjohnlev 	ringp->xr_frontend = 1;
2232843e1988Sjohnlev 	ringp->xr_entry_size = entrysize;
2233843e1988Sjohnlev }
2234843e1988Sjohnlev 
2235551bc2a6Smrj #ifndef XPV_HVM_DRIVER
2236843e1988Sjohnlev static void
2237843e1988Sjohnlev xvdi_ring_init_back_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
2238843e1988Sjohnlev {
2239843e1988Sjohnlev 	comif_ring_be_t *xbrp;
2240843e1988Sjohnlev 
2241843e1988Sjohnlev 	xbrp = &ringp->xr_sring.br;
2242843e1988Sjohnlev 	xbrp->rsp_prod_pvt = 0;
2243843e1988Sjohnlev 	xbrp->req_cons = 0;
2244843e1988Sjohnlev 	xbrp->nr_ents = nentry;
2245843e1988Sjohnlev 	xbrp->sring = (comif_sring_t *)ringp->xr_vaddr;
2246843e1988Sjohnlev 
2247843e1988Sjohnlev 	ringp->xr_frontend = 0;
2248843e1988Sjohnlev 	ringp->xr_entry_size = entrysize;
2249843e1988Sjohnlev }
2250551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
2251843e1988Sjohnlev 
2252843e1988Sjohnlev static void
2253843e1988Sjohnlev xendev_offline_device(void *arg)
2254843e1988Sjohnlev {
2255843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)arg;
2256843e1988Sjohnlev 	char devname[MAXNAMELEN] = {0};
2257843e1988Sjohnlev 
2258843e1988Sjohnlev 	/*
2259843e1988Sjohnlev 	 * This is currently the only chance to delete a devinfo node, which
2260843e1988Sjohnlev 	 * is _not_ always successful.
2261843e1988Sjohnlev 	 */
2262843e1988Sjohnlev 	(void) ddi_deviname(dip, devname);
2263843e1988Sjohnlev 	(void) devfs_clean(ddi_get_parent(dip), devname + 1, DV_CLEAN_FORCE);
2264843e1988Sjohnlev 	(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
2265843e1988Sjohnlev }
2266843e1988Sjohnlev 
2267843e1988Sjohnlev static void
2268843e1988Sjohnlev i_xvdi_oestate_cb(struct xenbus_device *dev, XenbusState oestate)
2269843e1988Sjohnlev {
2270843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)dev->data;
2271843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
2272eea6c6b9SMax zhen 	i_oestate_evt_t *evt = NULL;
22737eea693dSMark Johnson 	boolean_t call_handler;
2274eea6c6b9SMax zhen 
2275eea6c6b9SMax zhen 	XVDI_DPRINTF(XVDI_DBG_STATE,
2276eea6c6b9SMax zhen 	    "i_xvdi_oestate_cb: %s@%s sees oestate change to %d\n",
2277eea6c6b9SMax zhen 	    ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
2278eea6c6b9SMax zhen 	    ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
2279eea6c6b9SMax zhen 	    oestate);
2280843e1988Sjohnlev 
22817eea693dSMark Johnson 	/* only call the handler if our state has changed */
22827eea693dSMark Johnson 	call_handler = B_FALSE;
22837eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
22847eea693dSMark Johnson 	if (dev->otherend_state != oestate) {
22857eea693dSMark Johnson 		dev->otherend_state = oestate;
22867eea693dSMark Johnson 		call_handler = B_TRUE;
22877eea693dSMark Johnson 	}
22887eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
2289eea6c6b9SMax zhen 
22907eea693dSMark Johnson 	if (call_handler) {
22917eea693dSMark Johnson 		/*
22927eea693dSMark Johnson 		 * Try to deliver the oestate change event to the dip
22937eea693dSMark Johnson 		 */
22947eea693dSMark Johnson 		evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP);
22957eea693dSMark Johnson 		evt->dip = dip;
22967eea693dSMark Johnson 		evt->state = oestate;
22977eea693dSMark Johnson 		(void) ddi_taskq_dispatch(pdp->xd_oe_taskq,
22987eea693dSMark Johnson 		    i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP);
22997eea693dSMark Johnson 	}
2300843e1988Sjohnlev }
2301843e1988Sjohnlev 
2302843e1988Sjohnlev /*ARGSUSED*/
2303843e1988Sjohnlev static void
2304843e1988Sjohnlev i_xvdi_hpstate_cb(struct xenbus_watch *w, const char **vec,
2305843e1988Sjohnlev     unsigned int len)
2306843e1988Sjohnlev {
2307843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)w->dev;
2308843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
2309843e1988Sjohnlev 
2310eea6c6b9SMax zhen #ifdef DEBUG
2311eea6c6b9SMax zhen 	char *hp_status = NULL;
2312eea6c6b9SMax zhen 	unsigned int hpl = 0;
2313eea6c6b9SMax zhen 
2314eea6c6b9SMax zhen 	(void) xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
2315eea6c6b9SMax zhen 	    (void *)&hp_status, &hpl);
2316eea6c6b9SMax zhen 	XVDI_DPRINTF(XVDI_DBG_STATE,
2317eea6c6b9SMax zhen 	    "i_xvdi_hpstate_cb: %s@%s sees hpstate change to %s\n",
2318eea6c6b9SMax zhen 	    ddi_binding_name(dip) == NULL ?  "null" : ddi_binding_name(dip),
2319eea6c6b9SMax zhen 	    ddi_get_name_addr(dip) == NULL ?  "null" : ddi_get_name_addr(dip),
2320eea6c6b9SMax zhen 	    hp_status == NULL ? "null" : hp_status);
2321eea6c6b9SMax zhen 	if (hp_status != NULL)
2322eea6c6b9SMax zhen 		kmem_free(hp_status, hpl);
2323eea6c6b9SMax zhen #endif /* DEBUG */
2324eea6c6b9SMax zhen 
2325843e1988Sjohnlev 	(void) ddi_taskq_dispatch(pdp->xd_hp_taskq,
2326843e1988Sjohnlev 	    i_xvdi_hpstate_handler, (void *)dip, DDI_SLEEP);
2327843e1988Sjohnlev }
2328843e1988Sjohnlev 
2329843e1988Sjohnlev static void
2330843e1988Sjohnlev i_xvdi_probe_path_handler(void *arg)
2331843e1988Sjohnlev {
2332843e1988Sjohnlev 	dev_info_t *parent;
2333843e1988Sjohnlev 	char *path = arg, *p = NULL;
2334843e1988Sjohnlev 	int i, vdev, circ;
2335843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
2336843e1988Sjohnlev 	boolean_t frontend;
2337843e1988Sjohnlev 	domid_t dom;
2338843e1988Sjohnlev 
2339843e1988Sjohnlev 	for (i = 0, xdcp = &xdci[0]; i < NXDC; i++, xdcp++) {
2340843e1988Sjohnlev 
2341843e1988Sjohnlev 		if ((xdcp->xs_path_fe != NULL) &&
2342843e1988Sjohnlev 		    (strncmp(path, xdcp->xs_path_fe, strlen(xdcp->xs_path_fe))
2343843e1988Sjohnlev 		    == 0)) {
2344843e1988Sjohnlev 
2345843e1988Sjohnlev 			frontend = B_TRUE;
2346843e1988Sjohnlev 			p = path + strlen(xdcp->xs_path_fe);
2347843e1988Sjohnlev 			break;
2348843e1988Sjohnlev 		}
2349843e1988Sjohnlev 
2350843e1988Sjohnlev 		if ((xdcp->xs_path_be != NULL) &&
2351843e1988Sjohnlev 		    (strncmp(path, xdcp->xs_path_be, strlen(xdcp->xs_path_be))
2352843e1988Sjohnlev 		    == 0)) {
2353843e1988Sjohnlev 
2354843e1988Sjohnlev 			frontend = B_FALSE;
2355843e1988Sjohnlev 			p = path + strlen(xdcp->xs_path_be);
2356843e1988Sjohnlev 			break;
2357843e1988Sjohnlev 		}
2358843e1988Sjohnlev 
2359843e1988Sjohnlev 	}
2360843e1988Sjohnlev 
2361843e1988Sjohnlev 	if (p == NULL) {
2362843e1988Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
2363843e1988Sjohnlev 		    "unexpected path prefix in %s", path);
2364843e1988Sjohnlev 		goto done;
2365843e1988Sjohnlev 	}
2366843e1988Sjohnlev 
2367843e1988Sjohnlev 	if (frontend) {
2368843e1988Sjohnlev 		dom = DOMID_SELF;
2369843e1988Sjohnlev 		if (sscanf(p, "/%d/", &vdev) != 1) {
2370843e1988Sjohnlev 			XVDI_DPRINTF(XVDI_DBG_PROBE,
2371843e1988Sjohnlev 			    "i_xvdi_probe_path_handler: "
2372843e1988Sjohnlev 			    "cannot parse frontend path %s",
2373843e1988Sjohnlev 			    path);
2374843e1988Sjohnlev 			goto done;
2375843e1988Sjohnlev 		}
2376843e1988Sjohnlev 	} else {
23778793b36bSNick Todd 		if (sscanf(p, "/%hu/%d/", &dom, &vdev) != 2) {
2378843e1988Sjohnlev 			XVDI_DPRINTF(XVDI_DBG_PROBE,
2379843e1988Sjohnlev 			    "i_xvdi_probe_path_handler: "
2380843e1988Sjohnlev 			    "cannot parse backend path %s",
2381843e1988Sjohnlev 			    path);
2382843e1988Sjohnlev 			goto done;
2383843e1988Sjohnlev 		}
2384843e1988Sjohnlev 	}
2385843e1988Sjohnlev 
23861ca30e39Sjohnlev 	/*
23871ca30e39Sjohnlev 	 * This is an oxymoron, so indicates a bogus configuration we
23881ca30e39Sjohnlev 	 * must check for.
23891ca30e39Sjohnlev 	 */
23901ca30e39Sjohnlev 	if (vdev == VDEV_NOXS) {
23911ca30e39Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
23921ca30e39Sjohnlev 		    "invalid path %s", path);
23931ca30e39Sjohnlev 		goto done;
23941ca30e39Sjohnlev 	}
23951ca30e39Sjohnlev 
2396843e1988Sjohnlev 	parent = xendev_dip;
2397843e1988Sjohnlev 	ASSERT(parent != NULL);
2398843e1988Sjohnlev 
2399843e1988Sjohnlev 	ndi_devi_enter(parent, &circ);
2400843e1988Sjohnlev 
2401843e1988Sjohnlev 	if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL) {
2402843e1988Sjohnlev 		XVDI_DPRINTF(XVDI_DBG_PROBE,
2403843e1988Sjohnlev 		    "i_xvdi_probe_path_handler: create for %s", path);
2404843e1988Sjohnlev 		(void) xvdi_create_dev(parent, xdcp->devclass, dom, vdev);
2405843e1988Sjohnlev 	} else {
2406843e1988Sjohnlev 		XVDI_DPRINTF(XVDI_DBG_PROBE,
2407843e1988Sjohnlev 		    "i_xvdi_probe_path_handler: %s already exists", path);
2408843e1988Sjohnlev 	}
2409843e1988Sjohnlev 
2410843e1988Sjohnlev 	ndi_devi_exit(parent, circ);
2411843e1988Sjohnlev 
2412843e1988Sjohnlev done:
2413843e1988Sjohnlev 	kmem_free(path, strlen(path) + 1);
2414843e1988Sjohnlev }
2415