xref: /illumos-gate/usr/src/uts/common/xen/os/xvdi.c (revision 6d1e6c90)
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 /*
237f0b8309SEdward Pilatowicz  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
273599414cSJeremy Jones /*
283599414cSJeremy Jones  * Copyright (c) 2014 by Delphix. All rights reserved.
29*6d1e6c90SYuri Pankov  * Copyright 2018 Nexenta Systems, Inc.
303599414cSJeremy Jones  */
313599414cSJeremy Jones 
32843e1988Sjohnlev /*
33843e1988Sjohnlev  * Xen virtual device driver interfaces
34843e1988Sjohnlev  */
35843e1988Sjohnlev 
36843e1988Sjohnlev /*
37843e1988Sjohnlev  * todo:
38843e1988Sjohnlev  * + name space clean up:
39843e1988Sjohnlev  *	xvdi_* - public xen interfaces, for use by all leaf drivers
40843e1988Sjohnlev  *	xd_* - public xen data structures
41843e1988Sjohnlev  *	i_xvdi_* - implementation private functions
42843e1988Sjohnlev  *	xendev_* - xendev driver interfaces, both internal and in cb_ops/bus_ops
43843e1988Sjohnlev  * + add mdb dcmds to dump ring status
44843e1988Sjohnlev  * + implement xvdi_xxx to wrap xenbus_xxx read/write function
45843e1988Sjohnlev  * + convert (xendev_ring_t *) into xvdi_ring_handle_t
46843e1988Sjohnlev  */
47843e1988Sjohnlev #include <sys/conf.h>
48843e1988Sjohnlev #include <sys/param.h>
49843e1988Sjohnlev #include <sys/kmem.h>
50843e1988Sjohnlev #include <vm/seg_kmem.h>
51843e1988Sjohnlev #include <sys/debug.h>
52843e1988Sjohnlev #include <sys/modctl.h>
53843e1988Sjohnlev #include <sys/autoconf.h>
54843e1988Sjohnlev #include <sys/ddi_impldefs.h>
55843e1988Sjohnlev #include <sys/ddi_subrdefs.h>
56843e1988Sjohnlev #include <sys/ddi.h>
57843e1988Sjohnlev #include <sys/sunddi.h>
58843e1988Sjohnlev #include <sys/sunndi.h>
59843e1988Sjohnlev #include <sys/sunldi.h>
60843e1988Sjohnlev #include <sys/fs/dv_node.h>
61843e1988Sjohnlev #include <sys/avintr.h>
62843e1988Sjohnlev #include <sys/psm.h>
63843e1988Sjohnlev #include <sys/spl.h>
64843e1988Sjohnlev #include <sys/promif.h>
65843e1988Sjohnlev #include <sys/list.h>
66843e1988Sjohnlev #include <sys/bootconf.h>
67843e1988Sjohnlev #include <sys/bootsvcs.h>
68843e1988Sjohnlev #include <sys/bootinfo.h>
69843e1988Sjohnlev #include <sys/note.h>
707f0b8309SEdward Pilatowicz #include <sys/sysmacros.h>
71551bc2a6Smrj #ifdef XPV_HVM_DRIVER
72551bc2a6Smrj #include <sys/xpv_support.h>
73551bc2a6Smrj #include <sys/hypervisor.h>
74551bc2a6Smrj #include <public/grant_table.h>
75551bc2a6Smrj #include <public/xen.h>
76551bc2a6Smrj #include <public/io/xenbus.h>
77551bc2a6Smrj #include <public/io/xs_wire.h>
78551bc2a6Smrj #include <public/event_channel.h>
79551bc2a6Smrj #include <public/io/xenbus.h>
80551bc2a6Smrj #else /* XPV_HVM_DRIVER */
81551bc2a6Smrj #include <sys/hypervisor.h>
82843e1988Sjohnlev #include <sys/xen_mmu.h>
83843e1988Sjohnlev #include <xen/sys/xenbus_impl.h>
84551bc2a6Smrj #include <sys/evtchn_impl.h>
85551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
86551bc2a6Smrj #include <sys/gnttab.h>
87843e1988Sjohnlev #include <xen/sys/xendev.h>
88843e1988Sjohnlev #include <vm/hat_i86.h>
89843e1988Sjohnlev #include <sys/scsi/generic/inquiry.h>
90843e1988Sjohnlev #include <util/sscanf.h>
91843e1988Sjohnlev #include <xen/public/io/xs_wire.h>
92843e1988Sjohnlev 
93843e1988Sjohnlev 
9406bbe1e0Sedp #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
9506bbe1e0Sedp #define	isxdigit(ch)	(isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
9606bbe1e0Sedp 			((ch) >= 'A' && (ch) <= 'F'))
9706bbe1e0Sedp 
98843e1988Sjohnlev static void xvdi_ring_init_sring(xendev_ring_t *);
99843e1988Sjohnlev static void xvdi_ring_init_front_ring(xendev_ring_t *, size_t, size_t);
100551bc2a6Smrj #ifndef XPV_HVM_DRIVER
101843e1988Sjohnlev static void xvdi_ring_init_back_ring(xendev_ring_t *, size_t, size_t);
102551bc2a6Smrj #endif
103843e1988Sjohnlev static void xvdi_reinit_ring(dev_info_t *, grant_ref_t *, xendev_ring_t *);
104843e1988Sjohnlev 
105843e1988Sjohnlev static int i_xvdi_add_watches(dev_info_t *);
106843e1988Sjohnlev static void i_xvdi_rem_watches(dev_info_t *);
107843e1988Sjohnlev 
108843e1988Sjohnlev static int i_xvdi_add_watch_oestate(dev_info_t *);
109843e1988Sjohnlev static void i_xvdi_rem_watch_oestate(dev_info_t *);
110843e1988Sjohnlev static void i_xvdi_oestate_cb(struct xenbus_device *, XenbusState);
111843e1988Sjohnlev static void i_xvdi_oestate_handler(void *);
112843e1988Sjohnlev 
113843e1988Sjohnlev static int i_xvdi_add_watch_hpstate(dev_info_t *);
114843e1988Sjohnlev static void i_xvdi_rem_watch_hpstate(dev_info_t *);
115843e1988Sjohnlev static void i_xvdi_hpstate_cb(struct xenbus_watch *, const char **,
116843e1988Sjohnlev     unsigned int);
117843e1988Sjohnlev static void i_xvdi_hpstate_handler(void *);
118843e1988Sjohnlev 
119843e1988Sjohnlev static int i_xvdi_add_watch_bepath(dev_info_t *);
120843e1988Sjohnlev static void i_xvdi_rem_watch_bepath(dev_info_t *);
121843e1988Sjohnlev static void i_xvdi_bepath_cb(struct xenbus_watch *, const char **,
122843e1988Sjohnlev     unsigned in);
123843e1988Sjohnlev 
124843e1988Sjohnlev static void xendev_offline_device(void *);
125843e1988Sjohnlev 
126843e1988Sjohnlev static void i_xvdi_probe_path_cb(struct xenbus_watch *, const char **,
127843e1988Sjohnlev     unsigned int);
128843e1988Sjohnlev static void i_xvdi_probe_path_handler(void *);
129843e1988Sjohnlev 
130eea6c6b9SMax zhen typedef struct oestate_evt {
131eea6c6b9SMax zhen 	dev_info_t *dip;
132eea6c6b9SMax zhen 	XenbusState state;
133eea6c6b9SMax zhen } i_oestate_evt_t;
134eea6c6b9SMax zhen 
135843e1988Sjohnlev typedef struct xd_cfg {
136843e1988Sjohnlev 	xendev_devclass_t devclass;
137843e1988Sjohnlev 	char *xsdev;
138843e1988Sjohnlev 	char *xs_path_fe;
139843e1988Sjohnlev 	char *xs_path_be;
140843e1988Sjohnlev 	char *node_fe;
141843e1988Sjohnlev 	char *node_be;
142843e1988Sjohnlev 	char *device_type;
143843e1988Sjohnlev 	int xd_ipl;
144843e1988Sjohnlev 	int flags;
145843e1988Sjohnlev } i_xd_cfg_t;
146843e1988Sjohnlev 
147843e1988Sjohnlev #define	XD_DOM_ZERO	0x01	/* dom0 only. */
148843e1988Sjohnlev #define	XD_DOM_GUEST	0x02	/* Guest domains (i.e. non-dom0). */
149843e1988Sjohnlev #define	XD_DOM_IO	0x04	/* IO domains. */
150843e1988Sjohnlev 
151843e1988Sjohnlev #define	XD_DOM_ALL	(XD_DOM_ZERO | XD_DOM_GUEST)
152843e1988Sjohnlev 
153843e1988Sjohnlev static i_xd_cfg_t xdci[] = {
1543599414cSJeremy Jones #ifndef XPV_HVM_DRIVER
155843e1988Sjohnlev 	{ XEN_CONSOLE, NULL, NULL, NULL, "xencons", NULL,
156843e1988Sjohnlev 	    "console", IPL_CONS, XD_DOM_ALL, },
1573599414cSJeremy Jones #endif
158843e1988Sjohnlev 
159843e1988Sjohnlev 	{ XEN_VNET, "vif", "device/vif", "backend/vif", "xnf", "xnb",
160843e1988Sjohnlev 	    "network", IPL_VIF, XD_DOM_ALL, },
161843e1988Sjohnlev 
162843e1988Sjohnlev 	{ XEN_VBLK, "vbd", "device/vbd", "backend/vbd", "xdf", "xdb",
163843e1988Sjohnlev 	    "block", IPL_VBD, XD_DOM_ALL, },
164843e1988Sjohnlev 
1657eea693dSMark Johnson 	{ XEN_BLKTAP, "tap", NULL, "backend/tap", NULL, "xpvtap",
1667eea693dSMark Johnson 	    "block", IPL_VBD, XD_DOM_ALL, },
1677eea693dSMark Johnson 
1683599414cSJeremy Jones #ifndef XPV_HVM_DRIVER
169843e1988Sjohnlev 	{ XEN_XENBUS, NULL, NULL, NULL, "xenbus", NULL,
170843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
171843e1988Sjohnlev 
172843e1988Sjohnlev 	{ XEN_DOMCAPS, NULL, NULL, NULL, "domcaps", NULL,
173843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
174843e1988Sjohnlev 
175843e1988Sjohnlev 	{ XEN_BALLOON, NULL, NULL, NULL, "balloon", NULL,
176843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
1773599414cSJeremy Jones #endif
178843e1988Sjohnlev 
179843e1988Sjohnlev 	{ XEN_EVTCHN, NULL, NULL, NULL, "evtchn", NULL,
180843e1988Sjohnlev 	    NULL, 0, XD_DOM_ZERO, },
181843e1988Sjohnlev 
182843e1988Sjohnlev 	{ XEN_PRIVCMD, NULL, NULL, NULL, "privcmd", NULL,
183843e1988Sjohnlev 	    NULL, 0, XD_DOM_ZERO, },
184843e1988Sjohnlev };
185843e1988Sjohnlev #define	NXDC	(sizeof (xdci) / sizeof (xdci[0]))
186843e1988Sjohnlev 
187843e1988Sjohnlev static void i_xvdi_enum_fe(dev_info_t *, i_xd_cfg_t *);
188843e1988Sjohnlev static void i_xvdi_enum_be(dev_info_t *, i_xd_cfg_t *);
189843e1988Sjohnlev static void i_xvdi_enum_worker(dev_info_t *, i_xd_cfg_t *, char *);
190843e1988Sjohnlev 
191843e1988Sjohnlev /*
192843e1988Sjohnlev  * Xen device channel device access and DMA attributes
193843e1988Sjohnlev  */
194843e1988Sjohnlev static ddi_device_acc_attr_t xendev_dc_accattr = {
195843e1988Sjohnlev 	DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC
196843e1988Sjohnlev };
197843e1988Sjohnlev 
198843e1988Sjohnlev static ddi_dma_attr_t xendev_dc_dmaattr = {
199843e1988Sjohnlev 	DMA_ATTR_V0,		/* version of this structure */
200843e1988Sjohnlev 	0,			/* lowest usable address */
201843e1988Sjohnlev 	0xffffffffffffffffULL,	/* highest usable address */
202843e1988Sjohnlev 	0x7fffffff,		/* maximum DMAable byte count */
203843e1988Sjohnlev 	MMU_PAGESIZE,		/* alignment in bytes */
204843e1988Sjohnlev 	0x7ff,			/* bitmap of burst sizes */
205843e1988Sjohnlev 	1,			/* minimum transfer */
206843e1988Sjohnlev 	0xffffffffU,		/* maximum transfer */
207843e1988Sjohnlev 	0xffffffffffffffffULL,	/* maximum segment length */
208843e1988Sjohnlev 	1,			/* maximum number of segments */
209843e1988Sjohnlev 	1,			/* granularity */
210843e1988Sjohnlev 	0,			/* flags (reserved) */
211843e1988Sjohnlev };
212843e1988Sjohnlev 
213843e1988Sjohnlev static dev_info_t *xendev_dip = NULL;
214843e1988Sjohnlev 
215843e1988Sjohnlev #define	XVDI_DBG_STATE	0x01
216843e1988Sjohnlev #define	XVDI_DBG_PROBE	0x02
217843e1988Sjohnlev 
218843e1988Sjohnlev #ifdef DEBUG
219ab4a9bebSjohnlev int i_xvdi_debug = 0;
220843e1988Sjohnlev 
221843e1988Sjohnlev #define	XVDI_DPRINTF(flag, format, ...)			\
222843e1988Sjohnlev {							\
223843e1988Sjohnlev 	if (i_xvdi_debug & (flag))			\
224843e1988Sjohnlev 		prom_printf((format), __VA_ARGS__);	\
225843e1988Sjohnlev }
226843e1988Sjohnlev #else
227843e1988Sjohnlev #define	XVDI_DPRINTF(flag, format, ...)
228843e1988Sjohnlev #endif /* DEBUG */
229843e1988Sjohnlev 
230843e1988Sjohnlev static i_xd_cfg_t *
231843e1988Sjohnlev i_xvdi_devclass2cfg(xendev_devclass_t devclass)
232843e1988Sjohnlev {
233843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
234843e1988Sjohnlev 	int i;
235843e1988Sjohnlev 
236843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++)
237843e1988Sjohnlev 		if (xdcp->devclass == devclass)
238843e1988Sjohnlev 			return (xdcp);
239843e1988Sjohnlev 
240843e1988Sjohnlev 	return (NULL);
241843e1988Sjohnlev }
242843e1988Sjohnlev 
243843e1988Sjohnlev int
244843e1988Sjohnlev xvdi_init_dev(dev_info_t *dip)
245843e1988Sjohnlev {
246843e1988Sjohnlev 	xendev_devclass_t devcls;
247843e1988Sjohnlev 	int vdevnum;
248843e1988Sjohnlev 	domid_t domid;
249843e1988Sjohnlev 	struct xendev_ppd *pdp;
250843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
251843e1988Sjohnlev 	boolean_t backend;
252843e1988Sjohnlev 	char xsnamebuf[TYPICALMAXPATHLEN];
253843e1988Sjohnlev 	char *xsname;
25497869ac5Sjhd 	void *prop_str;
2553de3be76Sjhd 	unsigned int prop_len;
256*6d1e6c90SYuri Pankov 	char unitaddr[16];
257843e1988Sjohnlev 
258843e1988Sjohnlev 	devcls = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
259843e1988Sjohnlev 	    DDI_PROP_DONTPASS, "devclass", XEN_INVAL);
260843e1988Sjohnlev 	vdevnum = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2611ca30e39Sjohnlev 	    DDI_PROP_DONTPASS, "vdev", VDEV_NOXS);
262843e1988Sjohnlev 	domid = (domid_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
263843e1988Sjohnlev 	    DDI_PROP_DONTPASS, "domain", DOMID_SELF);
264843e1988Sjohnlev 
265843e1988Sjohnlev 	backend = (domid != DOMID_SELF);
266843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devcls);
267843e1988Sjohnlev 	if (xdcp->device_type != NULL)
268843e1988Sjohnlev 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
269843e1988Sjohnlev 		    "device_type", xdcp->device_type);
270843e1988Sjohnlev 
271843e1988Sjohnlev 	pdp = kmem_zalloc(sizeof (*pdp), KM_SLEEP);
272843e1988Sjohnlev 	pdp->xd_domain = domid;
273843e1988Sjohnlev 	pdp->xd_vdevnum = vdevnum;
274843e1988Sjohnlev 	pdp->xd_devclass = devcls;
275843e1988Sjohnlev 	pdp->xd_evtchn = INVALID_EVTCHN;
2767f0b8309SEdward Pilatowicz 	list_create(&pdp->xd_xb_watches, sizeof (xd_xb_watches_t),
2777f0b8309SEdward Pilatowicz 	    offsetof(xd_xb_watches_t, xxw_list));
2787eea693dSMark Johnson 	mutex_init(&pdp->xd_evt_lk, NULL, MUTEX_DRIVER, NULL);
2797eea693dSMark Johnson 	mutex_init(&pdp->xd_ndi_lk, NULL, MUTEX_DRIVER, NULL);
280843e1988Sjohnlev 	ddi_set_parent_data(dip, pdp);
281843e1988Sjohnlev 
282843e1988Sjohnlev 	/*
283843e1988Sjohnlev 	 * devices that do not need to interact with xenstore
284843e1988Sjohnlev 	 */
2851ca30e39Sjohnlev 	if (vdevnum == VDEV_NOXS) {
286843e1988Sjohnlev 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
287843e1988Sjohnlev 		    "unit-address", "0");
288843e1988Sjohnlev 		if (devcls == XEN_CONSOLE)
289843e1988Sjohnlev 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
290843e1988Sjohnlev 			    "pm-hardware-state", "needs-suspend-resume");
291843e1988Sjohnlev 		return (DDI_SUCCESS);
292843e1988Sjohnlev 	}
293843e1988Sjohnlev 
294843e1988Sjohnlev 	/*
295843e1988Sjohnlev 	 * PV devices that need to probe xenstore
296843e1988Sjohnlev 	 */
297843e1988Sjohnlev 
298843e1988Sjohnlev 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
299843e1988Sjohnlev 	    "pm-hardware-state", "needs-suspend-resume");
300843e1988Sjohnlev 
301843e1988Sjohnlev 	xsname = xsnamebuf;
302843e1988Sjohnlev 	if (!backend)
303843e1988Sjohnlev 		(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
304843e1988Sjohnlev 		    "%s/%d", xdcp->xs_path_fe, vdevnum);
305843e1988Sjohnlev 	else
306843e1988Sjohnlev 		(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
307843e1988Sjohnlev 		    "%s/%d/%d", xdcp->xs_path_be, domid, vdevnum);
3081d03c31eSjohnlev 	if ((xenbus_read_driver_state(xsname) >= XenbusStateClosing)) {
3091d03c31eSjohnlev 		/* Don't try to init a dev that may be closing */
3107eea693dSMark Johnson 		mutex_destroy(&pdp->xd_ndi_lk);
3117eea693dSMark Johnson 		mutex_destroy(&pdp->xd_evt_lk);
3121d03c31eSjohnlev 		kmem_free(pdp, sizeof (*pdp));
3131d03c31eSjohnlev 		ddi_set_parent_data(dip, NULL);
3141d03c31eSjohnlev 		return (DDI_FAILURE);
3151d03c31eSjohnlev 	}
316843e1988Sjohnlev 
317843e1988Sjohnlev 	pdp->xd_xsdev.nodename = i_ddi_strdup(xsname, KM_SLEEP);
318843e1988Sjohnlev 	pdp->xd_xsdev.devicetype = xdcp->xsdev;
319843e1988Sjohnlev 	pdp->xd_xsdev.frontend = (backend ? 0 : 1);
320843e1988Sjohnlev 	pdp->xd_xsdev.data = dip;
321843e1988Sjohnlev 	pdp->xd_xsdev.otherend_id = (backend ? domid : -1);
322843e1988Sjohnlev 	if (i_xvdi_add_watches(dip) != DDI_SUCCESS) {
323843e1988Sjohnlev 		cmn_err(CE_WARN, "xvdi_init_dev: "
324843e1988Sjohnlev 		    "cannot add watches for %s", xsname);
325843e1988Sjohnlev 		xvdi_uninit_dev(dip);
326843e1988Sjohnlev 		return (DDI_FAILURE);
327843e1988Sjohnlev 	}
328843e1988Sjohnlev 
32997869ac5Sjhd 	if (backend)
33097869ac5Sjhd 		return (DDI_SUCCESS);
33197869ac5Sjhd 
332843e1988Sjohnlev 	/*
33397869ac5Sjhd 	 * The unit-address for frontend devices is the name of the
33497869ac5Sjhd 	 * of the xenstore node containing the device configuration
33597869ac5Sjhd 	 * and is contained in the 'vdev' property.
33697869ac5Sjhd 	 * VIF devices are named using an incrementing integer.
337*6d1e6c90SYuri Pankov 	 * VBD devices are either named using the 32-bit dev_t value
33897869ac5Sjhd 	 * for linux 'hd' and 'xvd' devices, or a simple integer value
33997869ac5Sjhd 	 * in the range 0..767.  768 is the base value of the linux
34097869ac5Sjhd 	 * dev_t namespace, the dev_t value for 'hda'.
341843e1988Sjohnlev 	 */
34297869ac5Sjhd 	(void) snprintf(unitaddr, sizeof (unitaddr), "%d", vdevnum);
34397869ac5Sjhd 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "unit-address",
34497869ac5Sjhd 	    unitaddr);
34597869ac5Sjhd 
34697869ac5Sjhd 	switch (devcls) {
34797869ac5Sjhd 	case XEN_VNET:
34897869ac5Sjhd 		if (xenbus_read(XBT_NULL, xsname, "mac", (void *)&prop_str,
34997869ac5Sjhd 		    &prop_len) != 0)
350843e1988Sjohnlev 			break;
35197869ac5Sjhd 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "mac",
35297869ac5Sjhd 		    prop_str);
35397869ac5Sjhd 		kmem_free(prop_str, prop_len);
35497869ac5Sjhd 		break;
35597869ac5Sjhd 	case XEN_VBLK:
35697869ac5Sjhd 		/*
35797869ac5Sjhd 		 * cache a copy of the otherend name
35897869ac5Sjhd 		 * for ease of observeability
35997869ac5Sjhd 		 */
36097869ac5Sjhd 		if (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend, "dev",
36197869ac5Sjhd 		    &prop_str, &prop_len) != 0)
362843e1988Sjohnlev 			break;
36397869ac5Sjhd 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
36497869ac5Sjhd 		    "dev-address", prop_str);
36597869ac5Sjhd 		kmem_free(prop_str, prop_len);
36697869ac5Sjhd 		break;
36797869ac5Sjhd 	default:
36897869ac5Sjhd 		break;
369843e1988Sjohnlev 	}
370843e1988Sjohnlev 
371843e1988Sjohnlev 	return (DDI_SUCCESS);
372843e1988Sjohnlev }
373843e1988Sjohnlev 
374843e1988Sjohnlev void
375843e1988Sjohnlev xvdi_uninit_dev(dev_info_t *dip)
376843e1988Sjohnlev {
377843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
378843e1988Sjohnlev 
379843e1988Sjohnlev 	if (pdp != NULL) {
380843e1988Sjohnlev 		/* Remove any registered callbacks. */
381843e1988Sjohnlev 		xvdi_remove_event_handler(dip, NULL);
382843e1988Sjohnlev 
383843e1988Sjohnlev 		/* Remove any registered watches. */
384843e1988Sjohnlev 		i_xvdi_rem_watches(dip);
385843e1988Sjohnlev 
3861d03c31eSjohnlev 		/* tell other end to close */
387551bc2a6Smrj 		if (pdp->xd_xsdev.otherend_id != (domid_t)-1)
388551bc2a6Smrj 			(void) xvdi_switch_state(dip, XBT_NULL,
389551bc2a6Smrj 			    XenbusStateClosed);
3901d03c31eSjohnlev 
391843e1988Sjohnlev 		if (pdp->xd_xsdev.nodename != NULL)
392843e1988Sjohnlev 			kmem_free((char *)(pdp->xd_xsdev.nodename),
393843e1988Sjohnlev 			    strlen(pdp->xd_xsdev.nodename) + 1);
394843e1988Sjohnlev 
395843e1988Sjohnlev 		ddi_set_parent_data(dip, NULL);
396843e1988Sjohnlev 
3977eea693dSMark Johnson 		mutex_destroy(&pdp->xd_ndi_lk);
3987eea693dSMark Johnson 		mutex_destroy(&pdp->xd_evt_lk);
399843e1988Sjohnlev 		kmem_free(pdp, sizeof (*pdp));
400843e1988Sjohnlev 	}
401843e1988Sjohnlev }
402843e1988Sjohnlev 
403843e1988Sjohnlev /*
404843e1988Sjohnlev  * Bind the event channel for this device instance.
405843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
406843e1988Sjohnlev  */
407843e1988Sjohnlev int
408843e1988Sjohnlev xvdi_bind_evtchn(dev_info_t *dip, evtchn_port_t evtchn)
409843e1988Sjohnlev {
410843e1988Sjohnlev 	struct xendev_ppd *pdp;
411843e1988Sjohnlev 	domid_t oeid;
412843e1988Sjohnlev 	int r;
413843e1988Sjohnlev 
414843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
415843e1988Sjohnlev 	ASSERT(pdp != NULL);
416843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
417843e1988Sjohnlev 
4187eea693dSMark Johnson 	mutex_enter(&pdp->xd_evt_lk);
419843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE) {
420843e1988Sjohnlev 		if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
421843e1988Sjohnlev 			pdp->xd_evtchn = xen_info->console.domU.evtchn;
422843e1988Sjohnlev 		} else {
423843e1988Sjohnlev 			pdp->xd_evtchn = INVALID_EVTCHN;
4247eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
425843e1988Sjohnlev 			return (DDI_SUCCESS);
426843e1988Sjohnlev 		}
427843e1988Sjohnlev 	} else {
428843e1988Sjohnlev 		oeid = pdp->xd_xsdev.otherend_id;
429843e1988Sjohnlev 		if (oeid == (domid_t)-1) {
4307eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
431843e1988Sjohnlev 			return (DDI_FAILURE);
432843e1988Sjohnlev 		}
433843e1988Sjohnlev 
434843e1988Sjohnlev 		if ((r = xen_bind_interdomain(oeid, evtchn, &pdp->xd_evtchn))) {
435843e1988Sjohnlev 			xvdi_dev_error(dip, r, "bind event channel");
4367eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
437843e1988Sjohnlev 			return (DDI_FAILURE);
438843e1988Sjohnlev 		}
439843e1988Sjohnlev 	}
440551bc2a6Smrj #ifndef XPV_HVM_DRIVER
441843e1988Sjohnlev 	pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
442551bc2a6Smrj #endif
4437eea693dSMark Johnson 	mutex_exit(&pdp->xd_evt_lk);
444843e1988Sjohnlev 
445843e1988Sjohnlev 	return (DDI_SUCCESS);
446843e1988Sjohnlev }
447843e1988Sjohnlev 
448843e1988Sjohnlev /*
449843e1988Sjohnlev  * Allocate an event channel for this device instance.
450843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
451843e1988Sjohnlev  */
452843e1988Sjohnlev int
453843e1988Sjohnlev xvdi_alloc_evtchn(dev_info_t *dip)
454843e1988Sjohnlev {
455843e1988Sjohnlev 	struct xendev_ppd *pdp;
456843e1988Sjohnlev 	domid_t oeid;
457843e1988Sjohnlev 	int rv;
458843e1988Sjohnlev 
459843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
460843e1988Sjohnlev 	ASSERT(pdp != NULL);
461843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
462843e1988Sjohnlev 
4637eea693dSMark Johnson 	mutex_enter(&pdp->xd_evt_lk);
464843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE) {
465843e1988Sjohnlev 		if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
466843e1988Sjohnlev 			pdp->xd_evtchn = xen_info->console.domU.evtchn;
467843e1988Sjohnlev 		} else {
468843e1988Sjohnlev 			pdp->xd_evtchn = INVALID_EVTCHN;
4697eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
470843e1988Sjohnlev 			return (DDI_SUCCESS);
471843e1988Sjohnlev 		}
472843e1988Sjohnlev 	} else {
473843e1988Sjohnlev 		oeid = pdp->xd_xsdev.otherend_id;
474843e1988Sjohnlev 		if (oeid == (domid_t)-1) {
4757eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
476843e1988Sjohnlev 			return (DDI_FAILURE);
477843e1988Sjohnlev 		}
478843e1988Sjohnlev 
479843e1988Sjohnlev 		if ((rv = xen_alloc_unbound_evtchn(oeid, &pdp->xd_evtchn))) {
480843e1988Sjohnlev 			xvdi_dev_error(dip, rv, "bind event channel");
4817eea693dSMark Johnson 			mutex_exit(&pdp->xd_evt_lk);
482843e1988Sjohnlev 			return (DDI_FAILURE);
483843e1988Sjohnlev 		}
484843e1988Sjohnlev 	}
485551bc2a6Smrj #ifndef XPV_HVM_DRIVER
486843e1988Sjohnlev 	pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
487551bc2a6Smrj #endif
4887eea693dSMark Johnson 	mutex_exit(&pdp->xd_evt_lk);
489843e1988Sjohnlev 
490843e1988Sjohnlev 	return (DDI_SUCCESS);
491843e1988Sjohnlev }
492843e1988Sjohnlev 
493843e1988Sjohnlev /*
494843e1988Sjohnlev  * Unbind the event channel for this device instance.
495843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
496843e1988Sjohnlev  */
497843e1988Sjohnlev void
498843e1988Sjohnlev xvdi_free_evtchn(dev_info_t *dip)
499843e1988Sjohnlev {
500843e1988Sjohnlev 	struct xendev_ppd *pdp;
501843e1988Sjohnlev 
502843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
503843e1988Sjohnlev 	ASSERT(pdp != NULL);
504843e1988Sjohnlev 
5057eea693dSMark Johnson 	mutex_enter(&pdp->xd_evt_lk);
506843e1988Sjohnlev 	if (pdp->xd_evtchn != INVALID_EVTCHN) {
507551bc2a6Smrj #ifndef XPV_HVM_DRIVER
508843e1988Sjohnlev 		ec_unbind_irq(pdp->xd_ispec.intrspec_vec);
509843e1988Sjohnlev 		pdp->xd_ispec.intrspec_vec = 0;
510551bc2a6Smrj #endif
511551bc2a6Smrj 		pdp->xd_evtchn = INVALID_EVTCHN;
512843e1988Sjohnlev 	}
5137eea693dSMark Johnson 	mutex_exit(&pdp->xd_evt_lk);
514843e1988Sjohnlev }
515843e1988Sjohnlev 
516551bc2a6Smrj #ifndef XPV_HVM_DRIVER
517843e1988Sjohnlev /*
518843e1988Sjohnlev  * Map an inter-domain communication ring for a virtual device.
519843e1988Sjohnlev  * This is used by backend drivers.
520843e1988Sjohnlev  */
521843e1988Sjohnlev int
522843e1988Sjohnlev xvdi_map_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
523843e1988Sjohnlev     grant_ref_t gref, xendev_ring_t **ringpp)
524843e1988Sjohnlev {
525843e1988Sjohnlev 	domid_t oeid;
526843e1988Sjohnlev 	gnttab_map_grant_ref_t mapop;
527843e1988Sjohnlev 	gnttab_unmap_grant_ref_t unmapop;
528843e1988Sjohnlev 	caddr_t ringva;
529843e1988Sjohnlev 	ddi_acc_hdl_t *ap;
530843e1988Sjohnlev 	ddi_acc_impl_t *iap;
531843e1988Sjohnlev 	xendev_ring_t *ring;
532843e1988Sjohnlev 	int err;
533843e1988Sjohnlev 	char errstr[] = "mapping in ring buffer";
534843e1988Sjohnlev 
535843e1988Sjohnlev 	ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
536843e1988Sjohnlev 	oeid = xvdi_get_oeid(dip);
537843e1988Sjohnlev 
538843e1988Sjohnlev 	/* alloc va in backend dom for ring buffer */
539843e1988Sjohnlev 	ringva = vmem_xalloc(heap_arena, PAGESIZE, PAGESIZE,
540843e1988Sjohnlev 	    0, 0, 0, 0, VM_SLEEP);
541843e1988Sjohnlev 
542843e1988Sjohnlev 	/* map in ring page */
5437eea693dSMark Johnson 	hat_prepare_mapping(kas.a_hat, ringva, NULL);
544843e1988Sjohnlev 	mapop.host_addr = (uint64_t)(uintptr_t)ringva;
545843e1988Sjohnlev 	mapop.flags = GNTMAP_host_map;
546843e1988Sjohnlev 	mapop.ref = gref;
547843e1988Sjohnlev 	mapop.dom = oeid;
5487eea693dSMark Johnson 	err = xen_map_gref(GNTTABOP_map_grant_ref, &mapop, 1, B_FALSE);
549843e1988Sjohnlev 	if (err) {
550843e1988Sjohnlev 		xvdi_fatal_error(dip, err, errstr);
551843e1988Sjohnlev 		goto errout1;
552843e1988Sjohnlev 	}
553843e1988Sjohnlev 
554843e1988Sjohnlev 	if (mapop.status != 0) {
555843e1988Sjohnlev 		xvdi_fatal_error(dip, err, errstr);
556843e1988Sjohnlev 		goto errout2;
557843e1988Sjohnlev 	}
558843e1988Sjohnlev 	ring->xr_vaddr = ringva;
559843e1988Sjohnlev 	ring->xr_grant_hdl = mapop.handle;
560843e1988Sjohnlev 	ring->xr_gref = gref;
561843e1988Sjohnlev 
562843e1988Sjohnlev 	/*
563843e1988Sjohnlev 	 * init an acc handle and associate it w/ this ring
564843e1988Sjohnlev 	 * this is only for backend drivers. we get the memory by calling
565843e1988Sjohnlev 	 * vmem_xalloc(), instead of calling any ddi function, so we have
566843e1988Sjohnlev 	 * to init an acc handle by ourselves
567843e1988Sjohnlev 	 */
568843e1988Sjohnlev 	ring->xr_acc_hdl = impl_acc_hdl_alloc(KM_SLEEP, NULL);
569843e1988Sjohnlev 	ap = impl_acc_hdl_get(ring->xr_acc_hdl);
570843e1988Sjohnlev 	ap->ah_vers = VERS_ACCHDL;
571843e1988Sjohnlev 	ap->ah_dip = dip;
572843e1988Sjohnlev 	ap->ah_xfermodes = DDI_DMA_CONSISTENT;
573843e1988Sjohnlev 	ap->ah_acc = xendev_dc_accattr;
574843e1988Sjohnlev 	iap = (ddi_acc_impl_t *)ap->ah_platform_private;
575843e1988Sjohnlev 	iap->ahi_acc_attr |= DDI_ACCATTR_CPU_VADDR;
576843e1988Sjohnlev 	impl_acc_hdl_init(ap);
577843e1988Sjohnlev 	ap->ah_offset = 0;
578843e1988Sjohnlev 	ap->ah_len = (off_t)PAGESIZE;
579843e1988Sjohnlev 	ap->ah_addr = ring->xr_vaddr;
580843e1988Sjohnlev 
581843e1988Sjohnlev 	/* init backend ring */
582843e1988Sjohnlev 	xvdi_ring_init_back_ring(ring, nentry, entrysize);
583843e1988Sjohnlev 
584843e1988Sjohnlev 	*ringpp = ring;
585843e1988Sjohnlev 
586843e1988Sjohnlev 	return (DDI_SUCCESS);
587843e1988Sjohnlev 
588843e1988Sjohnlev errout2:
589843e1988Sjohnlev 	/* unmap ring page */
590843e1988Sjohnlev 	unmapop.host_addr = (uint64_t)(uintptr_t)ringva;
591843e1988Sjohnlev 	unmapop.handle = ring->xr_grant_hdl;
592843e1988Sjohnlev 	unmapop.dev_bus_addr = NULL;
593843e1988Sjohnlev 	(void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
594843e1988Sjohnlev 	hat_release_mapping(kas.a_hat, ringva);
595843e1988Sjohnlev errout1:
596843e1988Sjohnlev 	vmem_xfree(heap_arena, ringva, PAGESIZE);
597843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
598843e1988Sjohnlev 	return (DDI_FAILURE);
599843e1988Sjohnlev }
600843e1988Sjohnlev 
601843e1988Sjohnlev /*
602843e1988Sjohnlev  * Unmap a ring for a virtual device.
603843e1988Sjohnlev  * This is used by backend drivers.
604843e1988Sjohnlev  */
605843e1988Sjohnlev void
606843e1988Sjohnlev xvdi_unmap_ring(xendev_ring_t *ring)
607843e1988Sjohnlev {
608843e1988Sjohnlev 	gnttab_unmap_grant_ref_t unmapop;
609843e1988Sjohnlev 
610843e1988Sjohnlev 	ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
611843e1988Sjohnlev 
612843e1988Sjohnlev 	impl_acc_hdl_free(ring->xr_acc_hdl);
613843e1988Sjohnlev 	unmapop.host_addr = (uint64_t)(uintptr_t)ring->xr_vaddr;
614843e1988Sjohnlev 	unmapop.handle = ring->xr_grant_hdl;
615843e1988Sjohnlev 	unmapop.dev_bus_addr = NULL;
616843e1988Sjohnlev 	(void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
617843e1988Sjohnlev 	hat_release_mapping(kas.a_hat, ring->xr_vaddr);
618843e1988Sjohnlev 	vmem_xfree(heap_arena, ring->xr_vaddr, PAGESIZE);
619843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
620843e1988Sjohnlev }
621551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
622843e1988Sjohnlev 
623843e1988Sjohnlev /*
624843e1988Sjohnlev  * Re-initialise an inter-domain communications ring for the backend domain.
625843e1988Sjohnlev  * ring will be re-initialized after re-grant succeed
626843e1988Sjohnlev  * ring will be freed if fails to re-grant access to backend domain
627843e1988Sjohnlev  * so, don't keep useful data in the ring
628843e1988Sjohnlev  * used only in frontend driver
629843e1988Sjohnlev  */
630843e1988Sjohnlev static void
631843e1988Sjohnlev xvdi_reinit_ring(dev_info_t *dip, grant_ref_t *gref, xendev_ring_t *ringp)
632843e1988Sjohnlev {
633843e1988Sjohnlev 	paddr_t rpaddr;
634843e1988Sjohnlev 	maddr_t rmaddr;
635843e1988Sjohnlev 
636843e1988Sjohnlev 	ASSERT((ringp != NULL) && (ringp->xr_paddr != 0));
637843e1988Sjohnlev 	rpaddr = ringp->xr_paddr;
638843e1988Sjohnlev 
639843e1988Sjohnlev 	rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? rpaddr : pa_to_ma(rpaddr);
640843e1988Sjohnlev 	gnttab_grant_foreign_access_ref(ringp->xr_gref, xvdi_get_oeid(dip),
641843e1988Sjohnlev 	    rmaddr >> PAGESHIFT, 0);
642843e1988Sjohnlev 	*gref = ringp->xr_gref;
643843e1988Sjohnlev 
644843e1988Sjohnlev 	/* init frontend ring */
645843e1988Sjohnlev 	xvdi_ring_init_sring(ringp);
646843e1988Sjohnlev 	xvdi_ring_init_front_ring(ringp, ringp->xr_sring.fr.nr_ents,
647843e1988Sjohnlev 	    ringp->xr_entry_size);
648843e1988Sjohnlev }
649843e1988Sjohnlev 
650843e1988Sjohnlev /*
651843e1988Sjohnlev  * allocate Xen inter-domain communications ring for Xen virtual devices
652843e1988Sjohnlev  * used only in frontend driver
653843e1988Sjohnlev  * if *ringpp is not NULL, we'll simply re-init it
654843e1988Sjohnlev  */
655843e1988Sjohnlev int
656843e1988Sjohnlev xvdi_alloc_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
657843e1988Sjohnlev     grant_ref_t *gref, xendev_ring_t **ringpp)
658843e1988Sjohnlev {
659843e1988Sjohnlev 	size_t len;
660843e1988Sjohnlev 	xendev_ring_t *ring;
661843e1988Sjohnlev 	ddi_dma_cookie_t dma_cookie;
662843e1988Sjohnlev 	uint_t ncookies;
663843e1988Sjohnlev 	grant_ref_t ring_gref;
664843e1988Sjohnlev 	domid_t oeid;
665843e1988Sjohnlev 	maddr_t rmaddr;
666843e1988Sjohnlev 
667843e1988Sjohnlev 	if (*ringpp) {
668843e1988Sjohnlev 		xvdi_reinit_ring(dip, gref, *ringpp);
669843e1988Sjohnlev 		return (DDI_SUCCESS);
670843e1988Sjohnlev 	}
671843e1988Sjohnlev 
672843e1988Sjohnlev 	*ringpp = ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
673843e1988Sjohnlev 	oeid = xvdi_get_oeid(dip);
674843e1988Sjohnlev 
675843e1988Sjohnlev 	/*
676843e1988Sjohnlev 	 * Allocate page for this ring buffer
677843e1988Sjohnlev 	 */
678843e1988Sjohnlev 	if (ddi_dma_alloc_handle(dip, &xendev_dc_dmaattr, DDI_DMA_SLEEP,
679843e1988Sjohnlev 	    0, &ring->xr_dma_hdl) != DDI_SUCCESS)
680843e1988Sjohnlev 		goto err;
681843e1988Sjohnlev 
682843e1988Sjohnlev 	if (ddi_dma_mem_alloc(ring->xr_dma_hdl, PAGESIZE,
683843e1988Sjohnlev 	    &xendev_dc_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
684843e1988Sjohnlev 	    &ring->xr_vaddr, &len, &ring->xr_acc_hdl) != DDI_SUCCESS) {
685843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
686843e1988Sjohnlev 		goto err;
687843e1988Sjohnlev 	}
688843e1988Sjohnlev 
689843e1988Sjohnlev 	if (ddi_dma_addr_bind_handle(ring->xr_dma_hdl, NULL,
690843e1988Sjohnlev 	    ring->xr_vaddr, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
691843e1988Sjohnlev 	    DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies) != DDI_DMA_MAPPED) {
692843e1988Sjohnlev 		ddi_dma_mem_free(&ring->xr_acc_hdl);
693843e1988Sjohnlev 		ring->xr_vaddr = NULL;
694843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
695843e1988Sjohnlev 		goto err;
696843e1988Sjohnlev 	}
697843e1988Sjohnlev 	ASSERT(ncookies == 1);
698843e1988Sjohnlev 	ring->xr_paddr = dma_cookie.dmac_laddress;
699843e1988Sjohnlev 	rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? ring->xr_paddr :
700843e1988Sjohnlev 	    pa_to_ma(ring->xr_paddr);
701843e1988Sjohnlev 
702843e1988Sjohnlev 	if ((ring_gref = gnttab_grant_foreign_access(oeid,
703843e1988Sjohnlev 	    rmaddr >> PAGESHIFT, 0)) == (grant_ref_t)-1) {
704843e1988Sjohnlev 		(void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
705843e1988Sjohnlev 		ddi_dma_mem_free(&ring->xr_acc_hdl);
706843e1988Sjohnlev 		ring->xr_vaddr = NULL;
707843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
708843e1988Sjohnlev 		goto err;
709843e1988Sjohnlev 	}
710843e1988Sjohnlev 	*gref = ring->xr_gref = ring_gref;
711843e1988Sjohnlev 
712843e1988Sjohnlev 	/* init frontend ring */
713843e1988Sjohnlev 	xvdi_ring_init_sring(ring);
714843e1988Sjohnlev 	xvdi_ring_init_front_ring(ring, nentry, entrysize);
715843e1988Sjohnlev 
716843e1988Sjohnlev 	return (DDI_SUCCESS);
717843e1988Sjohnlev 
718843e1988Sjohnlev err:
719843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
720843e1988Sjohnlev 	return (DDI_FAILURE);
721843e1988Sjohnlev }
722843e1988Sjohnlev 
723843e1988Sjohnlev /*
724843e1988Sjohnlev  * Release ring buffers allocated for Xen devices
725843e1988Sjohnlev  * used for frontend driver
726843e1988Sjohnlev  */
727843e1988Sjohnlev void
728843e1988Sjohnlev xvdi_free_ring(xendev_ring_t *ring)
729843e1988Sjohnlev {
730843e1988Sjohnlev 	ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
731843e1988Sjohnlev 
732843e1988Sjohnlev 	(void) gnttab_end_foreign_access_ref(ring->xr_gref, 0);
733843e1988Sjohnlev 	(void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
734843e1988Sjohnlev 	ddi_dma_mem_free(&ring->xr_acc_hdl);
735843e1988Sjohnlev 	ddi_dma_free_handle(&ring->xr_dma_hdl);
736843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
737843e1988Sjohnlev }
738843e1988Sjohnlev 
739843e1988Sjohnlev dev_info_t *
740843e1988Sjohnlev xvdi_create_dev(dev_info_t *parent, xendev_devclass_t devclass,
741843e1988Sjohnlev     domid_t dom, int vdev)
742843e1988Sjohnlev {
743843e1988Sjohnlev 	dev_info_t *dip;
744843e1988Sjohnlev 	boolean_t backend;
745843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
746843e1988Sjohnlev 	char xsnamebuf[TYPICALMAXPATHLEN];
747843e1988Sjohnlev 	char *type, *node = NULL, *xsname = NULL;
748843e1988Sjohnlev 	unsigned int tlen;
7491d03c31eSjohnlev 	int ret;
750843e1988Sjohnlev 
751843e1988Sjohnlev 	ASSERT(DEVI_BUSY_OWNED(parent));
752843e1988Sjohnlev 
753843e1988Sjohnlev 	backend = (dom != DOMID_SELF);
754843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
755843e1988Sjohnlev 	ASSERT(xdcp != NULL);
756843e1988Sjohnlev 
7571ca30e39Sjohnlev 	if (vdev != VDEV_NOXS) {
758843e1988Sjohnlev 		if (!backend) {
759843e1988Sjohnlev 			(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
760843e1988Sjohnlev 			    "%s/%d", xdcp->xs_path_fe, vdev);
761843e1988Sjohnlev 			xsname = xsnamebuf;
762843e1988Sjohnlev 			node = xdcp->node_fe;
763843e1988Sjohnlev 		} else {
764843e1988Sjohnlev 			(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
765843e1988Sjohnlev 			    "%s/%d/%d", xdcp->xs_path_be, dom, vdev);
766843e1988Sjohnlev 			xsname = xsnamebuf;
767843e1988Sjohnlev 			node = xdcp->node_be;
768843e1988Sjohnlev 		}
769843e1988Sjohnlev 	} else {
770843e1988Sjohnlev 		node = xdcp->node_fe;
771843e1988Sjohnlev 	}
772843e1988Sjohnlev 
773843e1988Sjohnlev 	/* Must have a driver to use. */
774843e1988Sjohnlev 	if (node == NULL)
775843e1988Sjohnlev 		return (NULL);
776843e1988Sjohnlev 
777843e1988Sjohnlev 	/*
778843e1988Sjohnlev 	 * We need to check the state of this device before we go
779843e1988Sjohnlev 	 * further, otherwise we'll end up with a dead loop if
780843e1988Sjohnlev 	 * anything goes wrong.
781843e1988Sjohnlev 	 */
782843e1988Sjohnlev 	if ((xsname != NULL) &&
783843e1988Sjohnlev 	    (xenbus_read_driver_state(xsname) >= XenbusStateClosing))
784843e1988Sjohnlev 		return (NULL);
785843e1988Sjohnlev 
786843e1988Sjohnlev 	ndi_devi_alloc_sleep(parent, node, DEVI_SID_NODEID, &dip);
787843e1988Sjohnlev 
788843e1988Sjohnlev 	/*
789843e1988Sjohnlev 	 * Driver binding uses the compatible property _before_ the
790843e1988Sjohnlev 	 * node name, so we set the node name to the 'model' of the
791843e1988Sjohnlev 	 * device (i.e. 'xnb' or 'xdb') and, if 'type' is present,
792843e1988Sjohnlev 	 * encode both the model and the type in a compatible property
793843e1988Sjohnlev 	 * (i.e. 'xnb,netfront' or 'xnb,SUNW_mac').  This allows a
794843e1988Sjohnlev 	 * driver binding based on the <model,type> pair _before_ a
795843e1988Sjohnlev 	 * binding based on the node name.
796843e1988Sjohnlev 	 */
797843e1988Sjohnlev 	if ((xsname != NULL) &&
798843e1988Sjohnlev 	    (xenbus_read(XBT_NULL, xsname, "type", (void *)&type, &tlen)
799843e1988Sjohnlev 	    == 0)) {
800843e1988Sjohnlev 		size_t clen;
801843e1988Sjohnlev 		char *c[1];
802843e1988Sjohnlev 
803843e1988Sjohnlev 		clen = strlen(node) + strlen(type) + 2;
804843e1988Sjohnlev 		c[0] = kmem_alloc(clen, KM_SLEEP);
805843e1988Sjohnlev 		(void) snprintf(c[0], clen, "%s,%s", node, type);
806843e1988Sjohnlev 
807843e1988Sjohnlev 		(void) ndi_prop_update_string_array(DDI_DEV_T_NONE,
808843e1988Sjohnlev 		    dip, "compatible", (char **)c, 1);
809843e1988Sjohnlev 
810843e1988Sjohnlev 		kmem_free(c[0], clen);
811843e1988Sjohnlev 		kmem_free(type, tlen);
812843e1988Sjohnlev 	}
813843e1988Sjohnlev 
814843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "devclass", devclass);
815843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "domain", dom);
816843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "vdev", vdev);
817843e1988Sjohnlev 
818843e1988Sjohnlev 	if (i_ddi_devi_attached(parent))
8191d03c31eSjohnlev 		ret = ndi_devi_online(dip, 0);
820843e1988Sjohnlev 	else
8211d03c31eSjohnlev 		ret = ndi_devi_bind_driver(dip, 0);
8221d03c31eSjohnlev 	if (ret != NDI_SUCCESS)
8231d03c31eSjohnlev 		(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
824843e1988Sjohnlev 
825843e1988Sjohnlev 	return (dip);
826843e1988Sjohnlev }
827843e1988Sjohnlev 
828843e1988Sjohnlev /*
829843e1988Sjohnlev  * xendev_enum_class()
830843e1988Sjohnlev  */
831843e1988Sjohnlev void
832843e1988Sjohnlev xendev_enum_class(dev_info_t *parent, xendev_devclass_t devclass)
833843e1988Sjohnlev {
834d798155dSmrj 	boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
835d798155dSmrj 	boolean_t domU = !dom0;
836843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
837843e1988Sjohnlev 
838843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
839843e1988Sjohnlev 	ASSERT(xdcp != NULL);
840843e1988Sjohnlev 
841d798155dSmrj 	if (dom0 && !(xdcp->flags & XD_DOM_ZERO))
842d798155dSmrj 		return;
843d798155dSmrj 
844d798155dSmrj 	if (domU && !(xdcp->flags & XD_DOM_GUEST))
845d798155dSmrj 		return;
846d798155dSmrj 
847843e1988Sjohnlev 	if (xdcp->xsdev == NULL) {
848843e1988Sjohnlev 		int circ;
849843e1988Sjohnlev 
850843e1988Sjohnlev 		/*
851843e1988Sjohnlev 		 * Don't need to probe this kind of device from the
852843e1988Sjohnlev 		 * store, just create one if it doesn't exist.
853843e1988Sjohnlev 		 */
854843e1988Sjohnlev 
855843e1988Sjohnlev 		ndi_devi_enter(parent, &circ);
8561ca30e39Sjohnlev 		if (xvdi_find_dev(parent, devclass, DOMID_SELF, VDEV_NOXS)
857843e1988Sjohnlev 		    == NULL)
858843e1988Sjohnlev 			(void) xvdi_create_dev(parent, devclass,
8591ca30e39Sjohnlev 			    DOMID_SELF, VDEV_NOXS);
860843e1988Sjohnlev 		ndi_devi_exit(parent, circ);
861843e1988Sjohnlev 	} else {
862843e1988Sjohnlev 		/*
863843e1988Sjohnlev 		 * Probe this kind of device from the store, both
864843e1988Sjohnlev 		 * frontend and backend.
865843e1988Sjohnlev 		 */
8667eea693dSMark Johnson 		if (xdcp->node_fe != NULL) {
8677eea693dSMark Johnson 			i_xvdi_enum_fe(parent, xdcp);
8687eea693dSMark Johnson 		}
8697eea693dSMark Johnson 		if (xdcp->node_be != NULL) {
8707eea693dSMark Johnson 			i_xvdi_enum_be(parent, xdcp);
8717eea693dSMark Johnson 		}
872843e1988Sjohnlev 	}
873843e1988Sjohnlev }
874843e1988Sjohnlev 
875843e1988Sjohnlev /*
876843e1988Sjohnlev  * xendev_enum_all()
877843e1988Sjohnlev  */
878843e1988Sjohnlev void
879843e1988Sjohnlev xendev_enum_all(dev_info_t *parent, boolean_t store_unavailable)
880843e1988Sjohnlev {
881843e1988Sjohnlev 	int i;
882843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
883843e1988Sjohnlev 	boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
884843e1988Sjohnlev 
885843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
886843e1988Sjohnlev 		/*
887843e1988Sjohnlev 		 * Dom0 relies on watchpoints to create non-soft
888843e1988Sjohnlev 		 * devices - don't attempt to iterate over the store.
889843e1988Sjohnlev 		 */
890843e1988Sjohnlev 		if (dom0 && (xdcp->xsdev != NULL))
891843e1988Sjohnlev 			continue;
892843e1988Sjohnlev 
893843e1988Sjohnlev 		/*
894843e1988Sjohnlev 		 * If the store is not yet available, don't attempt to
895843e1988Sjohnlev 		 * iterate.
896843e1988Sjohnlev 		 */
897843e1988Sjohnlev 		if (store_unavailable && (xdcp->xsdev != NULL))
898843e1988Sjohnlev 			continue;
899843e1988Sjohnlev 
900843e1988Sjohnlev 		xendev_enum_class(parent, xdcp->devclass);
901843e1988Sjohnlev 	}
902843e1988Sjohnlev }
903843e1988Sjohnlev 
904843e1988Sjohnlev xendev_devclass_t
905843e1988Sjohnlev xendev_nodename_to_devclass(char *nodename)
906843e1988Sjohnlev {
907843e1988Sjohnlev 	int i;
908843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
909843e1988Sjohnlev 
910843e1988Sjohnlev 	/*
911843e1988Sjohnlev 	 * This relies on the convention that variants of a base
912843e1988Sjohnlev 	 * driver share the same prefix and that there are no drivers
913843e1988Sjohnlev 	 * which share a common prefix with the name of any other base
914843e1988Sjohnlev 	 * drivers.
915843e1988Sjohnlev 	 *
916843e1988Sjohnlev 	 * So for a base driver 'xnb' (which is the name listed in
917843e1988Sjohnlev 	 * xdci) the variants all begin with the string 'xnb' (in fact
918843e1988Sjohnlev 	 * they are 'xnbe', 'xnbo' and 'xnbu') and there are no other
919843e1988Sjohnlev 	 * base drivers which have the prefix 'xnb'.
920843e1988Sjohnlev 	 */
921843e1988Sjohnlev 	ASSERT(nodename != NULL);
922843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
923843e1988Sjohnlev 		if (((xdcp->node_fe != NULL) &&
924843e1988Sjohnlev 		    (strncmp(nodename, xdcp->node_fe,
925843e1988Sjohnlev 		    strlen(xdcp->node_fe)) == 0)) ||
926843e1988Sjohnlev 		    ((xdcp->node_be != NULL) &&
927843e1988Sjohnlev 		    (strncmp(nodename, xdcp->node_be,
928843e1988Sjohnlev 		    strlen(xdcp->node_be)) == 0)))
929843e1988Sjohnlev 
930843e1988Sjohnlev 			return (xdcp->devclass);
931843e1988Sjohnlev 	}
932843e1988Sjohnlev 	return (XEN_INVAL);
933843e1988Sjohnlev }
934843e1988Sjohnlev 
935843e1988Sjohnlev int
936843e1988Sjohnlev xendev_devclass_ipl(xendev_devclass_t devclass)
937843e1988Sjohnlev {
938843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
939843e1988Sjohnlev 
940843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
941843e1988Sjohnlev 	ASSERT(xdcp != NULL);
942843e1988Sjohnlev 
943843e1988Sjohnlev 	return (xdcp->xd_ipl);
944843e1988Sjohnlev }
945843e1988Sjohnlev 
946843e1988Sjohnlev /*
947843e1988Sjohnlev  * Determine if a devinfo instance exists of a particular device
948843e1988Sjohnlev  * class, domain and xenstore virtual device number.
949843e1988Sjohnlev  */
950843e1988Sjohnlev dev_info_t *
951843e1988Sjohnlev xvdi_find_dev(dev_info_t *parent, xendev_devclass_t devclass,
952843e1988Sjohnlev     domid_t dom, int vdev)
953843e1988Sjohnlev {
954843e1988Sjohnlev 	dev_info_t *dip;
955843e1988Sjohnlev 
956843e1988Sjohnlev 	ASSERT(DEVI_BUSY_OWNED(parent));
957843e1988Sjohnlev 
958843e1988Sjohnlev 	switch (devclass) {
959843e1988Sjohnlev 	case XEN_CONSOLE:
960843e1988Sjohnlev 	case XEN_XENBUS:
961843e1988Sjohnlev 	case XEN_DOMCAPS:
962843e1988Sjohnlev 	case XEN_BALLOON:
963843e1988Sjohnlev 	case XEN_EVTCHN:
964843e1988Sjohnlev 	case XEN_PRIVCMD:
965843e1988Sjohnlev 		/* Console and soft devices have no vdev. */
9661ca30e39Sjohnlev 		vdev = VDEV_NOXS;
967843e1988Sjohnlev 		break;
968843e1988Sjohnlev 	default:
969843e1988Sjohnlev 		break;
970843e1988Sjohnlev 	}
971843e1988Sjohnlev 
972843e1988Sjohnlev 	for (dip = ddi_get_child(parent); dip != NULL;
973843e1988Sjohnlev 	    dip = ddi_get_next_sibling(dip)) {
974843e1988Sjohnlev 		int *vdevnump, *domidp, *devclsp, vdevnum;
975843e1988Sjohnlev 		uint_t ndomid, nvdevnum, ndevcls;
976843e1988Sjohnlev 		xendev_devclass_t devcls;
977843e1988Sjohnlev 		domid_t domid;
978843e1988Sjohnlev 		struct xendev_ppd *pdp = ddi_get_parent_data(dip);
979843e1988Sjohnlev 
980843e1988Sjohnlev 		if (pdp == NULL) {
981843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
982843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "domain", &domidp, &ndomid) !=
983843e1988Sjohnlev 			    DDI_PROP_SUCCESS)
984843e1988Sjohnlev 				continue;
985843e1988Sjohnlev 			ASSERT(ndomid == 1);
986843e1988Sjohnlev 			domid = (domid_t)*domidp;
987843e1988Sjohnlev 			ddi_prop_free(domidp);
988843e1988Sjohnlev 
989843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
990843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "vdev", &vdevnump, &nvdevnum) !=
991843e1988Sjohnlev 			    DDI_PROP_SUCCESS)
992843e1988Sjohnlev 				continue;
993843e1988Sjohnlev 			ASSERT(nvdevnum == 1);
994843e1988Sjohnlev 			vdevnum = *vdevnump;
995843e1988Sjohnlev 			ddi_prop_free(vdevnump);
996843e1988Sjohnlev 
997843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
998843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "devclass", &devclsp,
999843e1988Sjohnlev 			    &ndevcls) != DDI_PROP_SUCCESS)
1000843e1988Sjohnlev 				continue;
1001843e1988Sjohnlev 			ASSERT(ndevcls == 1);
1002843e1988Sjohnlev 			devcls = (xendev_devclass_t)*devclsp;
1003843e1988Sjohnlev 			ddi_prop_free(devclsp);
1004843e1988Sjohnlev 		} else {
1005843e1988Sjohnlev 			domid = pdp->xd_domain;
1006843e1988Sjohnlev 			vdevnum = pdp->xd_vdevnum;
1007843e1988Sjohnlev 			devcls = pdp->xd_devclass;
1008843e1988Sjohnlev 		}
1009843e1988Sjohnlev 
1010843e1988Sjohnlev 		if ((domid == dom) && (vdevnum == vdev) && (devcls == devclass))
1011843e1988Sjohnlev 			return (dip);
1012843e1988Sjohnlev 	}
1013843e1988Sjohnlev 	return (NULL);
1014843e1988Sjohnlev }
1015843e1988Sjohnlev 
1016843e1988Sjohnlev int
1017843e1988Sjohnlev xvdi_get_evtchn(dev_info_t *xdip)
1018843e1988Sjohnlev {
1019843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1020843e1988Sjohnlev 
1021843e1988Sjohnlev 	ASSERT(pdp != NULL);
1022843e1988Sjohnlev 	return (pdp->xd_evtchn);
1023843e1988Sjohnlev }
1024843e1988Sjohnlev 
1025843e1988Sjohnlev int
1026843e1988Sjohnlev xvdi_get_vdevnum(dev_info_t *xdip)
1027843e1988Sjohnlev {
1028843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1029843e1988Sjohnlev 
1030843e1988Sjohnlev 	ASSERT(pdp != NULL);
1031843e1988Sjohnlev 	return (pdp->xd_vdevnum);
1032843e1988Sjohnlev }
1033843e1988Sjohnlev 
1034843e1988Sjohnlev char *
1035843e1988Sjohnlev xvdi_get_xsname(dev_info_t *xdip)
1036843e1988Sjohnlev {
1037843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1038843e1988Sjohnlev 
1039843e1988Sjohnlev 	ASSERT(pdp != NULL);
1040843e1988Sjohnlev 	return ((char *)(pdp->xd_xsdev.nodename));
1041843e1988Sjohnlev }
1042843e1988Sjohnlev 
1043843e1988Sjohnlev char *
1044843e1988Sjohnlev xvdi_get_oename(dev_info_t *xdip)
1045843e1988Sjohnlev {
1046843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1047843e1988Sjohnlev 
1048843e1988Sjohnlev 	ASSERT(pdp != NULL);
1049843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE)
1050843e1988Sjohnlev 		return (NULL);
1051843e1988Sjohnlev 	return ((char *)(pdp->xd_xsdev.otherend));
1052843e1988Sjohnlev }
1053843e1988Sjohnlev 
1054843e1988Sjohnlev struct xenbus_device *
1055843e1988Sjohnlev xvdi_get_xsd(dev_info_t *xdip)
1056843e1988Sjohnlev {
1057843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1058843e1988Sjohnlev 
1059843e1988Sjohnlev 	ASSERT(pdp != NULL);
1060843e1988Sjohnlev 	return (&pdp->xd_xsdev);
1061843e1988Sjohnlev }
1062843e1988Sjohnlev 
1063843e1988Sjohnlev domid_t
1064843e1988Sjohnlev xvdi_get_oeid(dev_info_t *xdip)
1065843e1988Sjohnlev {
1066843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1067843e1988Sjohnlev 
1068843e1988Sjohnlev 	ASSERT(pdp != NULL);
1069843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE)
1070843e1988Sjohnlev 		return ((domid_t)-1);
1071843e1988Sjohnlev 	return ((domid_t)(pdp->xd_xsdev.otherend_id));
1072843e1988Sjohnlev }
1073843e1988Sjohnlev 
1074843e1988Sjohnlev void
1075843e1988Sjohnlev xvdi_dev_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_error(&pdp->xd_xsdev, errno, errstr);
1081843e1988Sjohnlev }
1082843e1988Sjohnlev 
1083843e1988Sjohnlev void
1084843e1988Sjohnlev xvdi_fatal_error(dev_info_t *dip, int errno, char *errstr)
1085843e1988Sjohnlev {
1086843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1087843e1988Sjohnlev 
1088843e1988Sjohnlev 	ASSERT(pdp != NULL);
1089843e1988Sjohnlev 	xenbus_dev_fatal(&pdp->xd_xsdev, errno, errstr);
1090843e1988Sjohnlev }
1091843e1988Sjohnlev 
1092843e1988Sjohnlev static void
1093843e1988Sjohnlev i_xvdi_oestate_handler(void *arg)
1094843e1988Sjohnlev {
1095eea6c6b9SMax zhen 	i_oestate_evt_t *evt = (i_oestate_evt_t *)arg;
1096eea6c6b9SMax zhen 	dev_info_t *dip = evt->dip;
1097843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1098843e1988Sjohnlev 	XenbusState oestate = pdp->xd_xsdev.otherend_state;
1099eea6c6b9SMax zhen 	XenbusState curr_oestate = evt->state;
1100843e1988Sjohnlev 	ddi_eventcookie_t evc;
1101843e1988Sjohnlev 
1102eea6c6b9SMax zhen 	/* evt is alloc'ed in i_xvdi_oestate_cb */
1103eea6c6b9SMax zhen 	kmem_free(evt, sizeof (i_oestate_evt_t));
1104eea6c6b9SMax zhen 
1105eea6c6b9SMax zhen 	/*
1106eea6c6b9SMax zhen 	 * If the oestate we're handling is not the latest one,
1107eea6c6b9SMax zhen 	 * it does not make any sense to continue handling it.
1108eea6c6b9SMax zhen 	 */
1109eea6c6b9SMax zhen 	if (curr_oestate != oestate)
1110eea6c6b9SMax zhen 		return;
1111eea6c6b9SMax zhen 
11127eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1113843e1988Sjohnlev 
1114843e1988Sjohnlev 	if (pdp->xd_oe_ehid != NULL) {
1115843e1988Sjohnlev 		/* send notification to driver */
1116843e1988Sjohnlev 		if (ddi_get_eventcookie(dip, XS_OE_STATE,
1117843e1988Sjohnlev 		    &evc) == DDI_SUCCESS) {
11187eea693dSMark Johnson 			mutex_exit(&pdp->xd_ndi_lk);
1119843e1988Sjohnlev 			(void) ndi_post_event(dip, dip, evc, &oestate);
11207eea693dSMark Johnson 			mutex_enter(&pdp->xd_ndi_lk);
1121843e1988Sjohnlev 		}
1122843e1988Sjohnlev 	} else {
1123843e1988Sjohnlev 		/*
1124843e1988Sjohnlev 		 * take default action, if driver hasn't registered its
1125843e1988Sjohnlev 		 * event handler yet
1126843e1988Sjohnlev 		 */
1127843e1988Sjohnlev 		if (oestate == XenbusStateClosing) {
1128843e1988Sjohnlev 			(void) xvdi_switch_state(dip, XBT_NULL,
1129843e1988Sjohnlev 			    XenbusStateClosed);
1130843e1988Sjohnlev 		} else if (oestate == XenbusStateClosed) {
1131843e1988Sjohnlev 			(void) xvdi_switch_state(dip, XBT_NULL,
1132843e1988Sjohnlev 			    XenbusStateClosed);
1133843e1988Sjohnlev 			(void) xvdi_post_event(dip, XEN_HP_REMOVE);
1134843e1988Sjohnlev 		}
1135843e1988Sjohnlev 	}
1136843e1988Sjohnlev 
11377eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1138843e1988Sjohnlev 
1139843e1988Sjohnlev 	/*
1140843e1988Sjohnlev 	 * We'll try to remove the devinfo node of this device if the
1141843e1988Sjohnlev 	 * other end has closed.
1142843e1988Sjohnlev 	 */
1143843e1988Sjohnlev 	if (oestate == XenbusStateClosed)
1144843e1988Sjohnlev 		(void) ddi_taskq_dispatch(DEVI(ddi_get_parent(dip))->devi_taskq,
1145843e1988Sjohnlev 		    xendev_offline_device, dip, DDI_SLEEP);
1146843e1988Sjohnlev }
1147843e1988Sjohnlev 
1148843e1988Sjohnlev static void
1149843e1988Sjohnlev i_xvdi_hpstate_handler(void *arg)
1150843e1988Sjohnlev {
1151843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)arg;
1152843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1153843e1988Sjohnlev 	ddi_eventcookie_t evc;
1154843e1988Sjohnlev 	char *hp_status;
1155843e1988Sjohnlev 	unsigned int hpl;
1156843e1988Sjohnlev 
11577eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1158843e1988Sjohnlev 	if ((ddi_get_eventcookie(dip, XS_HP_STATE, &evc) == DDI_SUCCESS) &&
1159843e1988Sjohnlev 	    (xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
1160843e1988Sjohnlev 	    (void *)&hp_status, &hpl) == 0)) {
1161843e1988Sjohnlev 
1162843e1988Sjohnlev 		xendev_hotplug_state_t new_state = Unrecognized;
1163843e1988Sjohnlev 
1164843e1988Sjohnlev 		if (strcmp(hp_status, "connected") == 0)
1165843e1988Sjohnlev 			new_state = Connected;
1166843e1988Sjohnlev 
11677eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1168843e1988Sjohnlev 
1169843e1988Sjohnlev 		(void) ndi_post_event(dip, dip, evc, &new_state);
1170843e1988Sjohnlev 		kmem_free(hp_status, hpl);
1171843e1988Sjohnlev 		return;
1172843e1988Sjohnlev 	}
11737eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1174843e1988Sjohnlev }
1175843e1988Sjohnlev 
1176843e1988Sjohnlev void
1177843e1988Sjohnlev xvdi_notify_oe(dev_info_t *dip)
1178843e1988Sjohnlev {
1179843e1988Sjohnlev 	struct xendev_ppd *pdp;
1180843e1988Sjohnlev 
1181843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1182843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn != INVALID_EVTCHN);
1183843e1988Sjohnlev 	ec_notify_via_evtchn(pdp->xd_evtchn);
1184843e1988Sjohnlev }
1185843e1988Sjohnlev 
1186843e1988Sjohnlev static void
1187843e1988Sjohnlev i_xvdi_bepath_cb(struct xenbus_watch *w, const char **vec, unsigned int len)
1188843e1988Sjohnlev {
1189843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)w->dev;
1190843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1191843e1988Sjohnlev 	char *be = NULL;
1192843e1988Sjohnlev 	unsigned int bel;
1193843e1988Sjohnlev 
1194843e1988Sjohnlev 	ASSERT(len > XS_WATCH_PATH);
1195843e1988Sjohnlev 	ASSERT(vec[XS_WATCH_PATH] != NULL);
1196843e1988Sjohnlev 
1197843e1988Sjohnlev 	/*
1198843e1988Sjohnlev 	 * If the backend is not the same as that we already stored,
1199843e1988Sjohnlev 	 * re-set our watch for its' state.
1200843e1988Sjohnlev 	 */
1201843e1988Sjohnlev 	if ((xenbus_read(XBT_NULL, "", vec[XS_WATCH_PATH], (void *)be, &bel)
1202843e1988Sjohnlev 	    == 0) && (strcmp(be, pdp->xd_xsdev.otherend) != 0))
1203843e1988Sjohnlev 		(void) i_xvdi_add_watch_oestate(dip);
1204843e1988Sjohnlev 
1205843e1988Sjohnlev 	if (be != NULL) {
1206843e1988Sjohnlev 		ASSERT(bel > 0);
1207843e1988Sjohnlev 		kmem_free(be, bel);
1208843e1988Sjohnlev 	}
1209843e1988Sjohnlev }
1210843e1988Sjohnlev 
12117f0b8309SEdward Pilatowicz static void
12127f0b8309SEdward Pilatowicz i_xvdi_xb_watch_free(xd_xb_watches_t *xxwp)
12137f0b8309SEdward Pilatowicz {
12147f0b8309SEdward Pilatowicz 	ASSERT(xxwp->xxw_ref == 0);
12157f0b8309SEdward Pilatowicz 	strfree((char *)xxwp->xxw_watch.node);
12167f0b8309SEdward Pilatowicz 	kmem_free(xxwp, sizeof (*xxwp));
12177f0b8309SEdward Pilatowicz }
12187f0b8309SEdward Pilatowicz 
12197f0b8309SEdward Pilatowicz static void
12207f0b8309SEdward Pilatowicz i_xvdi_xb_watch_release(xd_xb_watches_t *xxwp)
12217f0b8309SEdward Pilatowicz {
12227f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&xxwp->xxw_xppd->xd_ndi_lk));
12237f0b8309SEdward Pilatowicz 	ASSERT(xxwp->xxw_ref > 0);
12247f0b8309SEdward Pilatowicz 	if (--xxwp->xxw_ref == 0)
12257f0b8309SEdward Pilatowicz 		i_xvdi_xb_watch_free(xxwp);
12267f0b8309SEdward Pilatowicz }
12277f0b8309SEdward Pilatowicz 
12287f0b8309SEdward Pilatowicz static void
12297f0b8309SEdward Pilatowicz i_xvdi_xb_watch_hold(xd_xb_watches_t *xxwp)
12307f0b8309SEdward Pilatowicz {
12317f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&xxwp->xxw_xppd->xd_ndi_lk));
12327f0b8309SEdward Pilatowicz 	ASSERT(xxwp->xxw_ref > 0);
12337f0b8309SEdward Pilatowicz 	xxwp->xxw_ref++;
12347f0b8309SEdward Pilatowicz }
12357f0b8309SEdward Pilatowicz 
12367f0b8309SEdward Pilatowicz static void
12377f0b8309SEdward Pilatowicz i_xvdi_xb_watch_cb_tq(void *arg)
12387f0b8309SEdward Pilatowicz {
12397f0b8309SEdward Pilatowicz 	xd_xb_watches_t		*xxwp = (xd_xb_watches_t *)arg;
12407f0b8309SEdward Pilatowicz 	dev_info_t		*dip = (dev_info_t *)xxwp->xxw_watch.dev;
12417f0b8309SEdward Pilatowicz 	struct xendev_ppd	*pdp = xxwp->xxw_xppd;
12427f0b8309SEdward Pilatowicz 
12437f0b8309SEdward Pilatowicz 	xxwp->xxw_cb(dip, xxwp->xxw_watch.node, xxwp->xxw_arg);
12447f0b8309SEdward Pilatowicz 
12457f0b8309SEdward Pilatowicz 	mutex_enter(&pdp->xd_ndi_lk);
12467f0b8309SEdward Pilatowicz 	i_xvdi_xb_watch_release(xxwp);
12477f0b8309SEdward Pilatowicz 	mutex_exit(&pdp->xd_ndi_lk);
12487f0b8309SEdward Pilatowicz }
12497f0b8309SEdward Pilatowicz 
12507f0b8309SEdward Pilatowicz static void
12517f0b8309SEdward Pilatowicz i_xvdi_xb_watch_cb(struct xenbus_watch *w, const char **vec, unsigned int len)
12527f0b8309SEdward Pilatowicz {
12537f0b8309SEdward Pilatowicz 	dev_info_t		*dip = (dev_info_t *)w->dev;
12547f0b8309SEdward Pilatowicz 	struct xendev_ppd	*pdp = ddi_get_parent_data(dip);
12557f0b8309SEdward Pilatowicz 	xd_xb_watches_t		*xxwp;
12567f0b8309SEdward Pilatowicz 
12577f0b8309SEdward Pilatowicz 	ASSERT(len > XS_WATCH_PATH);
12587f0b8309SEdward Pilatowicz 	ASSERT(vec[XS_WATCH_PATH] != NULL);
12597f0b8309SEdward Pilatowicz 
12607f0b8309SEdward Pilatowicz 	mutex_enter(&pdp->xd_ndi_lk);
12617f0b8309SEdward Pilatowicz 	for (xxwp = list_head(&pdp->xd_xb_watches); xxwp != NULL;
12627f0b8309SEdward Pilatowicz 	    xxwp = list_next(&pdp->xd_xb_watches, xxwp)) {
12637f0b8309SEdward Pilatowicz 		if (w == &xxwp->xxw_watch)
12647f0b8309SEdward Pilatowicz 			break;
12657f0b8309SEdward Pilatowicz 	}
12667f0b8309SEdward Pilatowicz 
12677f0b8309SEdward Pilatowicz 	if (xxwp == NULL) {
12687f0b8309SEdward Pilatowicz 		mutex_exit(&pdp->xd_ndi_lk);
12697f0b8309SEdward Pilatowicz 		return;
12707f0b8309SEdward Pilatowicz 	}
12717f0b8309SEdward Pilatowicz 
12727f0b8309SEdward Pilatowicz 	i_xvdi_xb_watch_hold(xxwp);
12737f0b8309SEdward Pilatowicz 	(void) ddi_taskq_dispatch(pdp->xd_xb_watch_taskq,
12747f0b8309SEdward Pilatowicz 	    i_xvdi_xb_watch_cb_tq, xxwp, DDI_SLEEP);
12757f0b8309SEdward Pilatowicz 	mutex_exit(&pdp->xd_ndi_lk);
12767f0b8309SEdward Pilatowicz }
12777f0b8309SEdward Pilatowicz 
12787f0b8309SEdward Pilatowicz /*
12797f0b8309SEdward Pilatowicz  * Any watches registered with xvdi_add_xb_watch_handler() get torn down during
12807f0b8309SEdward Pilatowicz  * a suspend operation.  So if a frontend driver want's to use these interfaces,
12817f0b8309SEdward Pilatowicz  * that driver is responsible for re-registering any watches it had before
12827f0b8309SEdward Pilatowicz  * the suspend operation.
12837f0b8309SEdward Pilatowicz  */
12847f0b8309SEdward Pilatowicz int
12857f0b8309SEdward Pilatowicz xvdi_add_xb_watch_handler(dev_info_t *dip, const char *dir, const char *node,
12867f0b8309SEdward Pilatowicz     xvdi_xb_watch_cb_t cb, void *arg)
12877f0b8309SEdward Pilatowicz {
12887f0b8309SEdward Pilatowicz 	struct xendev_ppd	*pdp = ddi_get_parent_data(dip);
12897f0b8309SEdward Pilatowicz 	xd_xb_watches_t		*xxw_new, *xxwp;
12907f0b8309SEdward Pilatowicz 	char			*path;
12917f0b8309SEdward Pilatowicz 	int			n;
12927f0b8309SEdward Pilatowicz 
12937f0b8309SEdward Pilatowicz 	ASSERT((dip != NULL) && (dir != NULL) && (node != NULL));
12947f0b8309SEdward Pilatowicz 	ASSERT(cb != NULL);
12957f0b8309SEdward Pilatowicz 
12967f0b8309SEdward Pilatowicz 	n = strlen(dir) + 1 + strlen(node) + 1;
12977f0b8309SEdward Pilatowicz 	path = kmem_zalloc(n, KM_SLEEP);
12987f0b8309SEdward Pilatowicz 	(void) strlcat(path, dir, n);
12997f0b8309SEdward Pilatowicz 	(void) strlcat(path, "/", n);
13007f0b8309SEdward Pilatowicz 	(void) strlcat(path, node, n);
13017f0b8309SEdward Pilatowicz 	ASSERT((strlen(path) + 1) == n);
13027f0b8309SEdward Pilatowicz 
13037f0b8309SEdward Pilatowicz 	xxw_new = kmem_zalloc(sizeof (*xxw_new), KM_SLEEP);
13047f0b8309SEdward Pilatowicz 	xxw_new->xxw_ref = 1;
13057f0b8309SEdward Pilatowicz 	xxw_new->xxw_watch.node = path;
13067f0b8309SEdward Pilatowicz 	xxw_new->xxw_watch.callback = i_xvdi_xb_watch_cb;
13077f0b8309SEdward Pilatowicz 	xxw_new->xxw_watch.dev = (struct xenbus_device *)dip;
13087f0b8309SEdward Pilatowicz 	xxw_new->xxw_xppd = pdp;
13097f0b8309SEdward Pilatowicz 	xxw_new->xxw_cb = cb;
13107f0b8309SEdward Pilatowicz 	xxw_new->xxw_arg = arg;
13117f0b8309SEdward Pilatowicz 
13127f0b8309SEdward Pilatowicz 	mutex_enter(&pdp->xd_ndi_lk);
13137f0b8309SEdward Pilatowicz 
13147f0b8309SEdward Pilatowicz 	/*
13157f0b8309SEdward Pilatowicz 	 * If this is the first watch we're setting up, create a taskq
13167f0b8309SEdward Pilatowicz 	 * to dispatch watch events and initialize the watch list.
13177f0b8309SEdward Pilatowicz 	 */
13187f0b8309SEdward Pilatowicz 	if (pdp->xd_xb_watch_taskq == NULL) {
13197f0b8309SEdward Pilatowicz 		char tq_name[TASKQ_NAMELEN];
13207f0b8309SEdward Pilatowicz 
13217f0b8309SEdward Pilatowicz 		ASSERT(list_is_empty(&pdp->xd_xb_watches));
13227f0b8309SEdward Pilatowicz 
13237f0b8309SEdward Pilatowicz 		(void) snprintf(tq_name, sizeof (tq_name),
13247f0b8309SEdward Pilatowicz 		    "%s_xb_watch_tq", ddi_get_name(dip));
13257f0b8309SEdward Pilatowicz 
13267f0b8309SEdward Pilatowicz 		if ((pdp->xd_xb_watch_taskq = ddi_taskq_create(dip, tq_name,
13277f0b8309SEdward Pilatowicz 		    1, TASKQ_DEFAULTPRI, 0)) == NULL) {
13287f0b8309SEdward Pilatowicz 			i_xvdi_xb_watch_release(xxw_new);
13297f0b8309SEdward Pilatowicz 			mutex_exit(&pdp->xd_ndi_lk);
13307f0b8309SEdward Pilatowicz 			return (DDI_FAILURE);
13317f0b8309SEdward Pilatowicz 		}
13327f0b8309SEdward Pilatowicz 	}
13337f0b8309SEdward Pilatowicz 
13347f0b8309SEdward Pilatowicz 	/* Don't allow duplicate watches to be registered */
13357f0b8309SEdward Pilatowicz 	for (xxwp = list_head(&pdp->xd_xb_watches); xxwp != NULL;
13367f0b8309SEdward Pilatowicz 	    xxwp = list_next(&pdp->xd_xb_watches, xxwp)) {
13377f0b8309SEdward Pilatowicz 
13387f0b8309SEdward Pilatowicz 		ASSERT(strcmp(xxwp->xxw_watch.node, path) != 0);
13397f0b8309SEdward Pilatowicz 		if (strcmp(xxwp->xxw_watch.node, path) != 0)
13407f0b8309SEdward Pilatowicz 			continue;
13417f0b8309SEdward Pilatowicz 		i_xvdi_xb_watch_release(xxw_new);
13427f0b8309SEdward Pilatowicz 		mutex_exit(&pdp->xd_ndi_lk);
13437f0b8309SEdward Pilatowicz 		return (DDI_FAILURE);
13447f0b8309SEdward Pilatowicz 	}
13457f0b8309SEdward Pilatowicz 
13467f0b8309SEdward Pilatowicz 	if (register_xenbus_watch(&xxw_new->xxw_watch) != 0) {
13477f0b8309SEdward Pilatowicz 		if (list_is_empty(&pdp->xd_xb_watches)) {
13487f0b8309SEdward Pilatowicz 			ddi_taskq_destroy(pdp->xd_xb_watch_taskq);
13497f0b8309SEdward Pilatowicz 			pdp->xd_xb_watch_taskq = NULL;
13507f0b8309SEdward Pilatowicz 		}
13517f0b8309SEdward Pilatowicz 		i_xvdi_xb_watch_release(xxw_new);
13527f0b8309SEdward Pilatowicz 		mutex_exit(&pdp->xd_ndi_lk);
13537f0b8309SEdward Pilatowicz 		return (DDI_FAILURE);
13547f0b8309SEdward Pilatowicz 	}
13557f0b8309SEdward Pilatowicz 
13567f0b8309SEdward Pilatowicz 	list_insert_head(&pdp->xd_xb_watches, xxw_new);
13577f0b8309SEdward Pilatowicz 	mutex_exit(&pdp->xd_ndi_lk);
13587f0b8309SEdward Pilatowicz 	return (DDI_SUCCESS);
13597f0b8309SEdward Pilatowicz }
13607f0b8309SEdward Pilatowicz 
13617f0b8309SEdward Pilatowicz /*
13627f0b8309SEdward Pilatowicz  * Tear down all xenbus watches registered by the specified dip.
13637f0b8309SEdward Pilatowicz  */
13647f0b8309SEdward Pilatowicz void
13657f0b8309SEdward Pilatowicz xvdi_remove_xb_watch_handlers(dev_info_t *dip)
13667f0b8309SEdward Pilatowicz {
13677f0b8309SEdward Pilatowicz 	struct xendev_ppd	*pdp = ddi_get_parent_data(dip);
13687f0b8309SEdward Pilatowicz 	xd_xb_watches_t		*xxwp;
13697f0b8309SEdward Pilatowicz 	ddi_taskq_t		*tq;
13707f0b8309SEdward Pilatowicz 
13717f0b8309SEdward Pilatowicz 	mutex_enter(&pdp->xd_ndi_lk);
13727f0b8309SEdward Pilatowicz 
13737f0b8309SEdward Pilatowicz 	while ((xxwp = list_remove_head(&pdp->xd_xb_watches)) != NULL) {
1374df889519SMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
13757f0b8309SEdward Pilatowicz 		unregister_xenbus_watch(&xxwp->xxw_watch);
1376df889519SMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
13777f0b8309SEdward Pilatowicz 		i_xvdi_xb_watch_release(xxwp);
13787f0b8309SEdward Pilatowicz 	}
13797f0b8309SEdward Pilatowicz 	ASSERT(list_is_empty(&pdp->xd_xb_watches));
13807f0b8309SEdward Pilatowicz 
13817f0b8309SEdward Pilatowicz 	/*
13827f0b8309SEdward Pilatowicz 	 * We can't hold xd_ndi_lk while we destroy the xd_xb_watch_taskq.
13837f0b8309SEdward Pilatowicz 	 * This is because if there are currently any executing taskq threads,
13847f0b8309SEdward Pilatowicz 	 * we will block until they are finished, and to finish they need
13857f0b8309SEdward Pilatowicz 	 * to aquire xd_ndi_lk in i_xvdi_xb_watch_cb_tq() so they can release
13867f0b8309SEdward Pilatowicz 	 * their reference on their corresponding xxwp structure.
13877f0b8309SEdward Pilatowicz 	 */
13887f0b8309SEdward Pilatowicz 	tq = pdp->xd_xb_watch_taskq;
13897f0b8309SEdward Pilatowicz 	pdp->xd_xb_watch_taskq = NULL;
13907f0b8309SEdward Pilatowicz 	mutex_exit(&pdp->xd_ndi_lk);
13917f0b8309SEdward Pilatowicz 	if (tq != NULL)
13927f0b8309SEdward Pilatowicz 		ddi_taskq_destroy(tq);
13937f0b8309SEdward Pilatowicz }
13947f0b8309SEdward Pilatowicz 
1395843e1988Sjohnlev static int
1396843e1988Sjohnlev i_xvdi_add_watch_oestate(dev_info_t *dip)
1397843e1988Sjohnlev {
1398843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1399843e1988Sjohnlev 
1400843e1988Sjohnlev 	ASSERT(pdp != NULL);
1401843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.nodename != NULL);
14027eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1403843e1988Sjohnlev 
1404843e1988Sjohnlev 	/*
1405843e1988Sjohnlev 	 * Create taskq for delivering other end state change event to
1406843e1988Sjohnlev 	 * this device later.
1407843e1988Sjohnlev 	 *
1408843e1988Sjohnlev 	 * Set nthreads to 1 to make sure that events can be delivered
1409843e1988Sjohnlev 	 * in order.
1410843e1988Sjohnlev 	 *
1411843e1988Sjohnlev 	 * Note: It is _not_ guaranteed that driver can see every
1412843e1988Sjohnlev 	 * xenstore change under the path that it is watching. If two
1413843e1988Sjohnlev 	 * changes happen consecutively in a very short amount of
1414843e1988Sjohnlev 	 * time, it is likely that the driver will see only the last
1415843e1988Sjohnlev 	 * one.
1416843e1988Sjohnlev 	 */
1417843e1988Sjohnlev 	if (pdp->xd_oe_taskq == NULL)
1418843e1988Sjohnlev 		if ((pdp->xd_oe_taskq = ddi_taskq_create(dip,
1419843e1988Sjohnlev 		    "xendev_oe_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
1420843e1988Sjohnlev 			return (DDI_FAILURE);
1421843e1988Sjohnlev 
1422843e1988Sjohnlev 	/*
1423843e1988Sjohnlev 	 * Watch for changes to the XenbusState of otherend.
1424843e1988Sjohnlev 	 */
1425843e1988Sjohnlev 	pdp->xd_xsdev.otherend_state = XenbusStateUnknown;
1426843e1988Sjohnlev 	pdp->xd_xsdev.otherend_changed = i_xvdi_oestate_cb;
1427843e1988Sjohnlev 
1428843e1988Sjohnlev 	if (talk_to_otherend(&pdp->xd_xsdev) != 0) {
1429843e1988Sjohnlev 		i_xvdi_rem_watch_oestate(dip);
1430843e1988Sjohnlev 		return (DDI_FAILURE);
1431843e1988Sjohnlev 	}
1432843e1988Sjohnlev 
1433843e1988Sjohnlev 	return (DDI_SUCCESS);
1434843e1988Sjohnlev }
1435843e1988Sjohnlev 
1436843e1988Sjohnlev static void
1437843e1988Sjohnlev i_xvdi_rem_watch_oestate(dev_info_t *dip)
1438843e1988Sjohnlev {
1439843e1988Sjohnlev 	struct xendev_ppd *pdp;
1440843e1988Sjohnlev 	struct xenbus_device *dev;
1441843e1988Sjohnlev 
1442843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1443843e1988Sjohnlev 	ASSERT(pdp != NULL);
14447eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1445843e1988Sjohnlev 
1446843e1988Sjohnlev 	dev = &pdp->xd_xsdev;
1447843e1988Sjohnlev 
1448843e1988Sjohnlev 	/* Unwatch for changes to XenbusState of otherend */
1449843e1988Sjohnlev 	if (dev->otherend_watch.node != NULL) {
14507eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1451843e1988Sjohnlev 		unregister_xenbus_watch(&dev->otherend_watch);
14527eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1453843e1988Sjohnlev 	}
1454843e1988Sjohnlev 
1455843e1988Sjohnlev 	/* make sure no event handler is running */
1456843e1988Sjohnlev 	if (pdp->xd_oe_taskq != NULL) {
14577eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1458843e1988Sjohnlev 		ddi_taskq_destroy(pdp->xd_oe_taskq);
14597eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1460843e1988Sjohnlev 		pdp->xd_oe_taskq = NULL;
1461843e1988Sjohnlev 	}
1462843e1988Sjohnlev 
1463843e1988Sjohnlev 	/* clean up */
1464843e1988Sjohnlev 	dev->otherend_state = XenbusStateUnknown;
1465843e1988Sjohnlev 	dev->otherend_id = (domid_t)-1;
1466843e1988Sjohnlev 	if (dev->otherend_watch.node != NULL)
1467843e1988Sjohnlev 		kmem_free((void *)dev->otherend_watch.node,
1468843e1988Sjohnlev 		    strlen(dev->otherend_watch.node) + 1);
1469843e1988Sjohnlev 	dev->otherend_watch.node = NULL;
1470843e1988Sjohnlev 	if (dev->otherend != NULL)
1471843e1988Sjohnlev 		kmem_free((void *)dev->otherend, strlen(dev->otherend) + 1);
1472843e1988Sjohnlev 	dev->otherend = NULL;
1473843e1988Sjohnlev }
1474843e1988Sjohnlev 
1475843e1988Sjohnlev static int
1476843e1988Sjohnlev i_xvdi_add_watch_hpstate(dev_info_t *dip)
1477843e1988Sjohnlev {
1478843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1479843e1988Sjohnlev 
1480843e1988Sjohnlev 	ASSERT(pdp != NULL);
1481843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 0);
14827eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1483843e1988Sjohnlev 
1484843e1988Sjohnlev 	/*
1485843e1988Sjohnlev 	 * Create taskq for delivering hotplug status change event to
1486843e1988Sjohnlev 	 * this device later.
1487843e1988Sjohnlev 	 *
1488843e1988Sjohnlev 	 * Set nthreads to 1 to make sure that events can be delivered
1489843e1988Sjohnlev 	 * in order.
1490843e1988Sjohnlev 	 *
1491843e1988Sjohnlev 	 * Note: It is _not_ guaranteed that driver can see every
1492843e1988Sjohnlev 	 * hotplug status change under the path that it is
1493843e1988Sjohnlev 	 * watching. If two changes happen consecutively in a very
1494843e1988Sjohnlev 	 * short amount of time, it is likely that the driver only
1495843e1988Sjohnlev 	 * sees the last one.
1496843e1988Sjohnlev 	 */
1497843e1988Sjohnlev 	if (pdp->xd_hp_taskq == NULL)
1498843e1988Sjohnlev 		if ((pdp->xd_hp_taskq = ddi_taskq_create(dip,
1499843e1988Sjohnlev 		    "xendev_hp_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
1500843e1988Sjohnlev 			return (DDI_FAILURE);
1501843e1988Sjohnlev 
1502843e1988Sjohnlev 	if (pdp->xd_hp_watch.node == NULL) {
1503843e1988Sjohnlev 		size_t len;
1504843e1988Sjohnlev 		char *path;
1505843e1988Sjohnlev 
1506843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.nodename != NULL);
1507843e1988Sjohnlev 
1508843e1988Sjohnlev 		len = strlen(pdp->xd_xsdev.nodename) +
1509843e1988Sjohnlev 		    strlen("/hotplug-status") + 1;
1510843e1988Sjohnlev 		path = kmem_alloc(len, KM_SLEEP);
1511843e1988Sjohnlev 		(void) snprintf(path, len, "%s/hotplug-status",
1512843e1988Sjohnlev 		    pdp->xd_xsdev.nodename);
1513843e1988Sjohnlev 
1514843e1988Sjohnlev 		pdp->xd_hp_watch.node = path;
1515843e1988Sjohnlev 		pdp->xd_hp_watch.callback = i_xvdi_hpstate_cb;
1516843e1988Sjohnlev 		pdp->xd_hp_watch.dev = (struct xenbus_device *)dip; /* yuck! */
1517843e1988Sjohnlev 		if (register_xenbus_watch(&pdp->xd_hp_watch) != 0) {
1518843e1988Sjohnlev 			i_xvdi_rem_watch_hpstate(dip);
1519843e1988Sjohnlev 			return (DDI_FAILURE);
1520843e1988Sjohnlev 		}
1521843e1988Sjohnlev 	}
1522843e1988Sjohnlev 
1523843e1988Sjohnlev 	return (DDI_SUCCESS);
1524843e1988Sjohnlev }
1525843e1988Sjohnlev 
1526843e1988Sjohnlev static void
1527843e1988Sjohnlev i_xvdi_rem_watch_hpstate(dev_info_t *dip)
1528843e1988Sjohnlev {
1529843e1988Sjohnlev 	struct xendev_ppd *pdp;
1530843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1531843e1988Sjohnlev 
1532843e1988Sjohnlev 	ASSERT(pdp != NULL);
1533843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 0);
15347eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1535843e1988Sjohnlev 
1536843e1988Sjohnlev 	/* Unwatch for changes to "hotplug-status" node for backend device. */
1537843e1988Sjohnlev 	if (pdp->xd_hp_watch.node != NULL) {
15387eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1539843e1988Sjohnlev 		unregister_xenbus_watch(&pdp->xd_hp_watch);
15407eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1541843e1988Sjohnlev 	}
1542843e1988Sjohnlev 
1543843e1988Sjohnlev 	/* Make sure no event handler is running. */
1544843e1988Sjohnlev 	if (pdp->xd_hp_taskq != NULL) {
15457eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1546843e1988Sjohnlev 		ddi_taskq_destroy(pdp->xd_hp_taskq);
15477eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1548843e1988Sjohnlev 		pdp->xd_hp_taskq = NULL;
1549843e1988Sjohnlev 	}
1550843e1988Sjohnlev 
1551843e1988Sjohnlev 	/* Clean up. */
1552843e1988Sjohnlev 	if (pdp->xd_hp_watch.node != NULL) {
1553843e1988Sjohnlev 		kmem_free((void *)pdp->xd_hp_watch.node,
1554843e1988Sjohnlev 		    strlen(pdp->xd_hp_watch.node) + 1);
1555843e1988Sjohnlev 		pdp->xd_hp_watch.node = NULL;
1556843e1988Sjohnlev 	}
1557843e1988Sjohnlev }
1558843e1988Sjohnlev 
1559843e1988Sjohnlev static int
1560843e1988Sjohnlev i_xvdi_add_watches(dev_info_t *dip)
1561843e1988Sjohnlev {
1562843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1563843e1988Sjohnlev 
1564843e1988Sjohnlev 	ASSERT(pdp != NULL);
1565843e1988Sjohnlev 
15667eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1567843e1988Sjohnlev 
1568843e1988Sjohnlev 	if (i_xvdi_add_watch_oestate(dip) != DDI_SUCCESS) {
15697eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1570843e1988Sjohnlev 		return (DDI_FAILURE);
1571843e1988Sjohnlev 	}
1572843e1988Sjohnlev 
1573843e1988Sjohnlev 	if (pdp->xd_xsdev.frontend == 1) {
1574843e1988Sjohnlev 		/*
1575843e1988Sjohnlev 		 * Frontend devices must watch for the backend path
1576843e1988Sjohnlev 		 * changing.
1577843e1988Sjohnlev 		 */
1578843e1988Sjohnlev 		if (i_xvdi_add_watch_bepath(dip) != DDI_SUCCESS)
1579843e1988Sjohnlev 			goto unwatch_and_fail;
1580843e1988Sjohnlev 	} else {
1581843e1988Sjohnlev 		/*
1582843e1988Sjohnlev 		 * Backend devices must watch for hotplug events.
1583843e1988Sjohnlev 		 */
1584843e1988Sjohnlev 		if (i_xvdi_add_watch_hpstate(dip) != DDI_SUCCESS)
1585843e1988Sjohnlev 			goto unwatch_and_fail;
1586843e1988Sjohnlev 	}
1587843e1988Sjohnlev 
15887eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1589843e1988Sjohnlev 
1590843e1988Sjohnlev 	return (DDI_SUCCESS);
1591843e1988Sjohnlev 
1592843e1988Sjohnlev unwatch_and_fail:
1593843e1988Sjohnlev 	i_xvdi_rem_watch_oestate(dip);
15947eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
1595843e1988Sjohnlev 
1596843e1988Sjohnlev 	return (DDI_FAILURE);
1597843e1988Sjohnlev }
1598843e1988Sjohnlev 
1599843e1988Sjohnlev static void
1600843e1988Sjohnlev i_xvdi_rem_watches(dev_info_t *dip)
1601843e1988Sjohnlev {
1602843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1603843e1988Sjohnlev 
1604843e1988Sjohnlev 	ASSERT(pdp != NULL);
1605843e1988Sjohnlev 
16067eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1607843e1988Sjohnlev 
1608843e1988Sjohnlev 	i_xvdi_rem_watch_oestate(dip);
1609843e1988Sjohnlev 
1610843e1988Sjohnlev 	if (pdp->xd_xsdev.frontend == 1)
1611843e1988Sjohnlev 		i_xvdi_rem_watch_bepath(dip);
1612843e1988Sjohnlev 	else
1613843e1988Sjohnlev 		i_xvdi_rem_watch_hpstate(dip);
1614843e1988Sjohnlev 
16157eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
16167f0b8309SEdward Pilatowicz 
16177f0b8309SEdward Pilatowicz 	xvdi_remove_xb_watch_handlers(dip);
1618843e1988Sjohnlev }
1619843e1988Sjohnlev 
1620843e1988Sjohnlev static int
1621843e1988Sjohnlev i_xvdi_add_watch_bepath(dev_info_t *dip)
1622843e1988Sjohnlev {
1623843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1624843e1988Sjohnlev 
1625843e1988Sjohnlev 	ASSERT(pdp != NULL);
1626843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 1);
1627843e1988Sjohnlev 
1628843e1988Sjohnlev 	/*
1629843e1988Sjohnlev 	 * Frontend devices need to watch for the backend path changing.
1630843e1988Sjohnlev 	 */
1631843e1988Sjohnlev 	if (pdp->xd_bepath_watch.node == NULL) {
1632843e1988Sjohnlev 		size_t len;
1633843e1988Sjohnlev 		char *path;
1634843e1988Sjohnlev 
1635843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.nodename != NULL);
1636843e1988Sjohnlev 
1637843e1988Sjohnlev 		len = strlen(pdp->xd_xsdev.nodename) + strlen("/backend") + 1;
1638843e1988Sjohnlev 		path = kmem_alloc(len, KM_SLEEP);
1639843e1988Sjohnlev 		(void) snprintf(path, len, "%s/backend",
1640843e1988Sjohnlev 		    pdp->xd_xsdev.nodename);
1641843e1988Sjohnlev 
1642843e1988Sjohnlev 		pdp->xd_bepath_watch.node = path;
1643843e1988Sjohnlev 		pdp->xd_bepath_watch.callback = i_xvdi_bepath_cb;
1644843e1988Sjohnlev 		pdp->xd_bepath_watch.dev = (struct xenbus_device *)dip;
1645843e1988Sjohnlev 		if (register_xenbus_watch(&pdp->xd_bepath_watch) != 0) {
1646843e1988Sjohnlev 			kmem_free(path, len);
1647843e1988Sjohnlev 			pdp->xd_bepath_watch.node = NULL;
1648843e1988Sjohnlev 			return (DDI_FAILURE);
1649843e1988Sjohnlev 		}
1650843e1988Sjohnlev 	}
1651843e1988Sjohnlev 
1652843e1988Sjohnlev 	return (DDI_SUCCESS);
1653843e1988Sjohnlev }
1654843e1988Sjohnlev 
1655843e1988Sjohnlev static void
1656843e1988Sjohnlev i_xvdi_rem_watch_bepath(dev_info_t *dip)
1657843e1988Sjohnlev {
1658843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1659843e1988Sjohnlev 
1660843e1988Sjohnlev 	ASSERT(pdp != NULL);
1661843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 1);
16627eea693dSMark Johnson 	ASSERT(mutex_owned(&pdp->xd_ndi_lk));
1663843e1988Sjohnlev 
1664843e1988Sjohnlev 	if (pdp->xd_bepath_watch.node != NULL) {
16657eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1666843e1988Sjohnlev 		unregister_xenbus_watch(&pdp->xd_bepath_watch);
16677eea693dSMark Johnson 		mutex_enter(&pdp->xd_ndi_lk);
1668843e1988Sjohnlev 
1669843e1988Sjohnlev 		kmem_free((void *)(pdp->xd_bepath_watch.node),
1670843e1988Sjohnlev 		    strlen(pdp->xd_bepath_watch.node) + 1);
1671843e1988Sjohnlev 		pdp->xd_bepath_watch.node = NULL;
1672843e1988Sjohnlev 	}
1673843e1988Sjohnlev }
1674843e1988Sjohnlev 
1675843e1988Sjohnlev int
1676843e1988Sjohnlev xvdi_switch_state(dev_info_t *dip, xenbus_transaction_t xbt,
1677843e1988Sjohnlev     XenbusState newState)
1678843e1988Sjohnlev {
1679843e1988Sjohnlev 	int rv;
1680843e1988Sjohnlev 	struct xendev_ppd *pdp;
1681843e1988Sjohnlev 
1682843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1683843e1988Sjohnlev 	ASSERT(pdp != NULL);
1684843e1988Sjohnlev 
1685843e1988Sjohnlev 	XVDI_DPRINTF(XVDI_DBG_STATE,
1686eea6c6b9SMax zhen 	    "xvdi_switch_state: %s@%s's xenbus state moves to %d\n",
1687eea6c6b9SMax zhen 	    ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
1688eea6c6b9SMax zhen 	    ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
1689eea6c6b9SMax zhen 	    newState);
1690843e1988Sjohnlev 
1691843e1988Sjohnlev 	rv = xenbus_switch_state(&pdp->xd_xsdev, xbt, newState);
1692843e1988Sjohnlev 	if (rv > 0)
1693843e1988Sjohnlev 		cmn_err(CE_WARN, "xvdi_switch_state: change state failed");
1694843e1988Sjohnlev 
1695843e1988Sjohnlev 	return (rv);
1696843e1988Sjohnlev }
1697843e1988Sjohnlev 
1698843e1988Sjohnlev /*
1699843e1988Sjohnlev  * Notify hotplug script running in userland
1700843e1988Sjohnlev  */
1701843e1988Sjohnlev int
1702843e1988Sjohnlev xvdi_post_event(dev_info_t *dip, xendev_hotplug_cmd_t hpc)
1703843e1988Sjohnlev {
1704843e1988Sjohnlev 	struct xendev_ppd *pdp;
1705843e1988Sjohnlev 	nvlist_t *attr_list = NULL;
1706843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
1707843e1988Sjohnlev 	sysevent_id_t eid;
1708843e1988Sjohnlev 	int err;
1709843e1988Sjohnlev 	char devname[256]; /* XXPV dme: ? */
1710843e1988Sjohnlev 
1711843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1712843e1988Sjohnlev 	ASSERT(pdp != NULL);
1713843e1988Sjohnlev 
1714843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(pdp->xd_devclass);
1715843e1988Sjohnlev 	ASSERT(xdcp != NULL);
1716843e1988Sjohnlev 
1717843e1988Sjohnlev 	(void) snprintf(devname, sizeof (devname) - 1, "%s%d",
1718843e1988Sjohnlev 	    ddi_driver_name(dip),  ddi_get_instance(dip));
1719843e1988Sjohnlev 
1720843e1988Sjohnlev 	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME, KM_NOSLEEP);
1721843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1722843e1988Sjohnlev 		goto failure;
1723843e1988Sjohnlev 
1724843e1988Sjohnlev 	err = nvlist_add_int32(attr_list, "domain", pdp->xd_domain);
1725843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1726843e1988Sjohnlev 		goto failure;
1727843e1988Sjohnlev 	err = nvlist_add_int32(attr_list, "vdev", pdp->xd_vdevnum);
1728843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1729843e1988Sjohnlev 		goto failure;
1730843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "devclass", xdcp->xsdev);
1731843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1732843e1988Sjohnlev 		goto failure;
1733843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "device", devname);
1734843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1735843e1988Sjohnlev 		goto failure;
1736843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "fob",
1737843e1988Sjohnlev 	    ((pdp->xd_xsdev.frontend == 1) ? "frontend" : "backend"));
1738843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1739843e1988Sjohnlev 		goto failure;
1740843e1988Sjohnlev 
1741843e1988Sjohnlev 	switch (hpc) {
1742843e1988Sjohnlev 	case XEN_HP_ADD:
1743843e1988Sjohnlev 		err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
1744843e1988Sjohnlev 		    "add", attr_list, &eid, DDI_NOSLEEP);
1745843e1988Sjohnlev 		break;
1746843e1988Sjohnlev 	case XEN_HP_REMOVE:
1747843e1988Sjohnlev 		err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
1748843e1988Sjohnlev 		    "remove", attr_list, &eid, DDI_NOSLEEP);
1749843e1988Sjohnlev 		break;
1750843e1988Sjohnlev 	default:
1751843e1988Sjohnlev 		err = DDI_FAILURE;
1752843e1988Sjohnlev 		goto failure;
1753843e1988Sjohnlev 	}
1754843e1988Sjohnlev 
1755843e1988Sjohnlev failure:
1756aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(attr_list);
1757843e1988Sjohnlev 
1758843e1988Sjohnlev 	return (err);
1759843e1988Sjohnlev }
1760843e1988Sjohnlev 
1761843e1988Sjohnlev /* ARGSUSED */
1762843e1988Sjohnlev static void
1763843e1988Sjohnlev i_xvdi_probe_path_cb(struct xenbus_watch *w, const char **vec,
1764843e1988Sjohnlev     unsigned int len)
1765843e1988Sjohnlev {
1766843e1988Sjohnlev 	char *path;
1767843e1988Sjohnlev 
1768843e1988Sjohnlev 	if (xendev_dip == NULL)
1769843e1988Sjohnlev 		xendev_dip = ddi_find_devinfo("xpvd", -1, 0);
1770843e1988Sjohnlev 
1771843e1988Sjohnlev 	path = i_ddi_strdup((char *)vec[XS_WATCH_PATH], KM_SLEEP);
1772843e1988Sjohnlev 
1773843e1988Sjohnlev 	(void) ddi_taskq_dispatch(DEVI(xendev_dip)->devi_taskq,
1774843e1988Sjohnlev 	    i_xvdi_probe_path_handler, (void *)path, DDI_SLEEP);
1775843e1988Sjohnlev }
1776843e1988Sjohnlev 
1777843e1988Sjohnlev static void
1778843e1988Sjohnlev i_xvdi_watch_device(char *path)
1779843e1988Sjohnlev {
1780843e1988Sjohnlev 	struct xenbus_watch *w;
1781843e1988Sjohnlev 
1782843e1988Sjohnlev 	ASSERT(path != NULL);
1783843e1988Sjohnlev 
1784843e1988Sjohnlev 	w = kmem_zalloc(sizeof (*w), KM_SLEEP);
1785843e1988Sjohnlev 	w->node = path;
1786843e1988Sjohnlev 	w->callback = &i_xvdi_probe_path_cb;
1787843e1988Sjohnlev 	w->dev = NULL;
1788843e1988Sjohnlev 
1789843e1988Sjohnlev 	if (register_xenbus_watch(w) != 0) {
1790843e1988Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_watch_device: "
1791843e1988Sjohnlev 		    "cannot set watch on %s", path);
1792843e1988Sjohnlev 		kmem_free(w, sizeof (*w));
1793843e1988Sjohnlev 		return;
1794843e1988Sjohnlev 	}
1795843e1988Sjohnlev }
1796843e1988Sjohnlev 
1797843e1988Sjohnlev void
1798843e1988Sjohnlev xvdi_watch_devices(int newstate)
1799843e1988Sjohnlev {
1800843e1988Sjohnlev 	int devclass;
1801843e1988Sjohnlev 
1802843e1988Sjohnlev 	/*
1803843e1988Sjohnlev 	 * Watch for devices being created in the store.
1804843e1988Sjohnlev 	 */
1805843e1988Sjohnlev 	if (newstate == XENSTORE_DOWN)
1806843e1988Sjohnlev 		return;
1807843e1988Sjohnlev 	for (devclass = 0; devclass < NXDC; devclass++) {
1808843e1988Sjohnlev 		if (xdci[devclass].xs_path_fe != NULL)
1809843e1988Sjohnlev 			i_xvdi_watch_device(xdci[devclass].xs_path_fe);
1810843e1988Sjohnlev 		if (xdci[devclass].xs_path_be != NULL)
1811843e1988Sjohnlev 			i_xvdi_watch_device(xdci[devclass].xs_path_be);
1812843e1988Sjohnlev 	}
1813843e1988Sjohnlev }
1814843e1988Sjohnlev 
1815843e1988Sjohnlev /*
1816843e1988Sjohnlev  * Iterate over the store looking for backend devices to create.
1817843e1988Sjohnlev  */
1818843e1988Sjohnlev static void
1819843e1988Sjohnlev i_xvdi_enum_be(dev_info_t *parent, i_xd_cfg_t *xdcp)
1820843e1988Sjohnlev {
1821843e1988Sjohnlev 	char **domains;
1822843e1988Sjohnlev 	unsigned int ndomains;
1823843e1988Sjohnlev 	int ldomains, i;
1824843e1988Sjohnlev 
1825843e1988Sjohnlev 	if ((domains = xenbus_directory(XBT_NULL, xdcp->xs_path_be, "",
1826843e1988Sjohnlev 	    &ndomains)) == NULL)
1827843e1988Sjohnlev 		return;
1828843e1988Sjohnlev 
1829843e1988Sjohnlev 	for (i = 0, ldomains = 0; i < ndomains; i++) {
1830843e1988Sjohnlev 		ldomains += strlen(domains[i]) + 1 + sizeof (char *);
1831843e1988Sjohnlev 
1832843e1988Sjohnlev 		i_xvdi_enum_worker(parent, xdcp, domains[i]);
1833843e1988Sjohnlev 	}
1834843e1988Sjohnlev 	kmem_free(domains, ldomains);
1835843e1988Sjohnlev }
1836843e1988Sjohnlev 
1837843e1988Sjohnlev /*
1838843e1988Sjohnlev  * Iterate over the store looking for frontend devices to create.
1839843e1988Sjohnlev  */
1840843e1988Sjohnlev static void
1841843e1988Sjohnlev i_xvdi_enum_fe(dev_info_t *parent, i_xd_cfg_t *xdcp)
1842843e1988Sjohnlev {
1843843e1988Sjohnlev 	i_xvdi_enum_worker(parent, xdcp, NULL);
1844843e1988Sjohnlev }
1845843e1988Sjohnlev 
1846843e1988Sjohnlev static void
1847843e1988Sjohnlev i_xvdi_enum_worker(dev_info_t *parent, i_xd_cfg_t *xdcp,
1848843e1988Sjohnlev     char *domain)
1849843e1988Sjohnlev {
1850843e1988Sjohnlev 	char *path, *domain_path, *ep;
1851843e1988Sjohnlev 	char **devices;
1852843e1988Sjohnlev 	unsigned int ndevices;
1853843e1988Sjohnlev 	int ldevices, j, circ;
1854843e1988Sjohnlev 	domid_t dom;
18556e24ea8fSedp 	long tmplong;
1856843e1988Sjohnlev 
1857843e1988Sjohnlev 	if (domain == NULL) {
1858843e1988Sjohnlev 		dom = DOMID_SELF;
1859843e1988Sjohnlev 		path = xdcp->xs_path_fe;
1860843e1988Sjohnlev 		domain_path = "";
1861843e1988Sjohnlev 	} else {
18626e24ea8fSedp 		(void) ddi_strtol(domain, &ep, 0, &tmplong);
18636e24ea8fSedp 		dom = tmplong;
1864843e1988Sjohnlev 		path = xdcp->xs_path_be;
1865843e1988Sjohnlev 		domain_path = domain;
1866843e1988Sjohnlev 	}
1867843e1988Sjohnlev 
1868843e1988Sjohnlev 	if ((devices = xenbus_directory(XBT_NULL, path, domain_path,
1869843e1988Sjohnlev 	    &ndevices)) == NULL)
1870843e1988Sjohnlev 		return;
1871843e1988Sjohnlev 
1872843e1988Sjohnlev 	for (j = 0, ldevices = 0; j < ndevices; j++) {
1873843e1988Sjohnlev 		int vdev;
1874843e1988Sjohnlev 
1875843e1988Sjohnlev 		ldevices += strlen(devices[j]) + 1 + sizeof (char *);
18766e24ea8fSedp 		(void) ddi_strtol(devices[j], &ep, 0, &tmplong);
18776e24ea8fSedp 		vdev = tmplong;
1878843e1988Sjohnlev 
1879843e1988Sjohnlev 		ndi_devi_enter(parent, &circ);
1880843e1988Sjohnlev 
18816e24ea8fSedp 		if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL)
1882843e1988Sjohnlev 			(void) xvdi_create_dev(parent, xdcp->devclass,
1883843e1988Sjohnlev 			    dom, vdev);
1884843e1988Sjohnlev 
1885843e1988Sjohnlev 		ndi_devi_exit(parent, circ);
1886843e1988Sjohnlev 	}
1887843e1988Sjohnlev 	kmem_free(devices, ldevices);
1888843e1988Sjohnlev }
1889843e1988Sjohnlev 
1890843e1988Sjohnlev /*
1891843e1988Sjohnlev  * Leaf drivers should call this in their detach() routine during suspend.
1892843e1988Sjohnlev  */
1893843e1988Sjohnlev void
1894843e1988Sjohnlev xvdi_suspend(dev_info_t *dip)
1895843e1988Sjohnlev {
1896843e1988Sjohnlev 	i_xvdi_rem_watches(dip);
1897843e1988Sjohnlev }
1898843e1988Sjohnlev 
1899843e1988Sjohnlev /*
1900843e1988Sjohnlev  * Leaf drivers should call this in their attach() routine during resume.
1901843e1988Sjohnlev  */
1902843e1988Sjohnlev int
1903843e1988Sjohnlev xvdi_resume(dev_info_t *dip)
1904843e1988Sjohnlev {
1905843e1988Sjohnlev 	return (i_xvdi_add_watches(dip));
1906843e1988Sjohnlev }
1907843e1988Sjohnlev 
1908843e1988Sjohnlev /*
1909843e1988Sjohnlev  * Add event handler for the leaf driver
1910843e1988Sjohnlev  * to handle event triggered by the change in xenstore
1911843e1988Sjohnlev  */
1912843e1988Sjohnlev int
1913843e1988Sjohnlev xvdi_add_event_handler(dev_info_t *dip, char *name,
19147eea693dSMark Johnson     void (*evthandler)(dev_info_t *, ddi_eventcookie_t, void *, void *),
19157eea693dSMark Johnson     void *arg)
1916843e1988Sjohnlev {
1917843e1988Sjohnlev 	ddi_eventcookie_t ecv;
1918843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1919843e1988Sjohnlev 	ddi_callback_id_t *cbid;
19207eea693dSMark Johnson 	boolean_t call_handler;
19217eea693dSMark Johnson 	i_oestate_evt_t *evt = NULL;
19227eea693dSMark Johnson 	XenbusState oestate;
1923843e1988Sjohnlev 
1924843e1988Sjohnlev 	ASSERT(pdp != NULL);
1925843e1988Sjohnlev 
19267eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
1927843e1988Sjohnlev 
1928843e1988Sjohnlev 	if (strcmp(name, XS_OE_STATE) == 0) {
1929843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.otherend != NULL);
1930843e1988Sjohnlev 
1931843e1988Sjohnlev 		cbid = &pdp->xd_oe_ehid;
1932843e1988Sjohnlev 	} else if (strcmp(name, XS_HP_STATE) == 0) {
1933843e1988Sjohnlev 		if (pdp->xd_xsdev.frontend == 1) {
19347eea693dSMark Johnson 			mutex_exit(&pdp->xd_ndi_lk);
1935843e1988Sjohnlev 			return (DDI_FAILURE);
1936843e1988Sjohnlev 		}
1937843e1988Sjohnlev 
1938843e1988Sjohnlev 		ASSERT(pdp->xd_hp_watch.node != NULL);
1939843e1988Sjohnlev 
1940843e1988Sjohnlev 		cbid = &pdp->xd_hp_ehid;
1941843e1988Sjohnlev 	} else {
1942843e1988Sjohnlev 		/* Unsupported watch. */
19437eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1944843e1988Sjohnlev 		return (DDI_FAILURE);
1945843e1988Sjohnlev 	}
1946843e1988Sjohnlev 
1947843e1988Sjohnlev 	/*
1948843e1988Sjohnlev 	 * No event handler provided, take default action to handle
1949843e1988Sjohnlev 	 * event.
1950843e1988Sjohnlev 	 */
1951843e1988Sjohnlev 	if (evthandler == NULL) {
19527eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1953843e1988Sjohnlev 		return (DDI_SUCCESS);
1954843e1988Sjohnlev 	}
1955843e1988Sjohnlev 
1956843e1988Sjohnlev 	ASSERT(*cbid == NULL);
1957843e1988Sjohnlev 
1958843e1988Sjohnlev 	if (ddi_get_eventcookie(dip, name, &ecv) != DDI_SUCCESS) {
1959843e1988Sjohnlev 		cmn_err(CE_WARN, "failed to find %s cookie for %s@%s",
1960843e1988Sjohnlev 		    name, ddi_get_name(dip), ddi_get_name_addr(dip));
19617eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1962843e1988Sjohnlev 		return (DDI_FAILURE);
1963843e1988Sjohnlev 	}
19647eea693dSMark Johnson 	if (ddi_add_event_handler(dip, ecv, evthandler, arg, cbid)
1965843e1988Sjohnlev 	    != DDI_SUCCESS) {
1966843e1988Sjohnlev 		cmn_err(CE_WARN, "failed to add %s event handler for %s@%s",
1967843e1988Sjohnlev 		    name, ddi_get_name(dip), ddi_get_name_addr(dip));
1968843e1988Sjohnlev 		*cbid = NULL;
19697eea693dSMark Johnson 		mutex_exit(&pdp->xd_ndi_lk);
1970843e1988Sjohnlev 		return (DDI_FAILURE);
1971843e1988Sjohnlev 	}
1972843e1988Sjohnlev 
19737eea693dSMark Johnson 	/*
19747eea693dSMark Johnson 	 * if we're adding an oe state callback, and the ring has already
19757eea693dSMark Johnson 	 * transitioned out of Unknown, call the handler after we release
19767eea693dSMark Johnson 	 * the mutex.
19777eea693dSMark Johnson 	 */
19787eea693dSMark Johnson 	call_handler = B_FALSE;
19797eea693dSMark Johnson 	if ((strcmp(name, XS_OE_STATE) == 0) &&
19807eea693dSMark Johnson 	    (pdp->xd_xsdev.otherend_state != XenbusStateUnknown)) {
19817eea693dSMark Johnson 		oestate = pdp->xd_xsdev.otherend_state;
19827eea693dSMark Johnson 		call_handler = B_TRUE;
19837eea693dSMark Johnson 	}
19847eea693dSMark Johnson 
19857eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
19867eea693dSMark Johnson 
19877eea693dSMark Johnson 	if (call_handler) {
19887eea693dSMark Johnson 		evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP);
19897eea693dSMark Johnson 		evt->dip = dip;
19907eea693dSMark Johnson 		evt->state = oestate;
19917eea693dSMark Johnson 		(void) ddi_taskq_dispatch(pdp->xd_oe_taskq,
19927eea693dSMark Johnson 		    i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP);
19937eea693dSMark Johnson 	}
1994843e1988Sjohnlev 
1995843e1988Sjohnlev 	return (DDI_SUCCESS);
1996843e1988Sjohnlev }
1997843e1988Sjohnlev 
1998843e1988Sjohnlev /*
1999843e1988Sjohnlev  * Remove event handler for the leaf driver and unwatch xenstore
2000843e1988Sjohnlev  * so, driver will not be notified when xenstore entry changed later
2001843e1988Sjohnlev  */
2002843e1988Sjohnlev void
2003843e1988Sjohnlev xvdi_remove_event_handler(dev_info_t *dip, char *name)
2004843e1988Sjohnlev {
2005843e1988Sjohnlev 	struct xendev_ppd *pdp;
2006843e1988Sjohnlev 	boolean_t rem_oe = B_FALSE, rem_hp = B_FALSE;
2007843e1988Sjohnlev 	ddi_callback_id_t oeid = NULL, hpid = NULL;
2008843e1988Sjohnlev 
2009843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
2010843e1988Sjohnlev 	ASSERT(pdp != NULL);
2011843e1988Sjohnlev 
2012843e1988Sjohnlev 	if (name == NULL) {
2013843e1988Sjohnlev 		rem_oe = B_TRUE;
2014843e1988Sjohnlev 		rem_hp = B_TRUE;
2015843e1988Sjohnlev 	} else if (strcmp(name, XS_OE_STATE) == 0) {
2016843e1988Sjohnlev 		rem_oe = B_TRUE;
2017843e1988Sjohnlev 	} else if (strcmp(name, XS_HP_STATE) == 0) {
2018843e1988Sjohnlev 		rem_hp = B_TRUE;
2019843e1988Sjohnlev 	} else {
2020843e1988Sjohnlev 		cmn_err(CE_WARN, "event %s not supported, cannot remove", name);
2021843e1988Sjohnlev 		return;
2022843e1988Sjohnlev 	}
2023843e1988Sjohnlev 
20247eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
2025843e1988Sjohnlev 
2026843e1988Sjohnlev 	if (rem_oe && (pdp->xd_oe_ehid != NULL)) {
2027843e1988Sjohnlev 		oeid = pdp->xd_oe_ehid;
2028843e1988Sjohnlev 		pdp->xd_oe_ehid = NULL;
2029843e1988Sjohnlev 	}
2030843e1988Sjohnlev 
2031843e1988Sjohnlev 	if (rem_hp && (pdp->xd_hp_ehid != NULL)) {
2032843e1988Sjohnlev 		hpid = pdp->xd_hp_ehid;
2033843e1988Sjohnlev 		pdp->xd_hp_ehid = NULL;
2034843e1988Sjohnlev 	}
2035843e1988Sjohnlev 
20367eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
2037843e1988Sjohnlev 
2038843e1988Sjohnlev 	if (oeid != NULL)
2039843e1988Sjohnlev 		(void) ddi_remove_event_handler(oeid);
2040843e1988Sjohnlev 	if (hpid != NULL)
2041843e1988Sjohnlev 		(void) ddi_remove_event_handler(hpid);
2042843e1988Sjohnlev }
2043843e1988Sjohnlev 
2044843e1988Sjohnlev 
2045843e1988Sjohnlev /*
2046843e1988Sjohnlev  * common ring interfaces
2047843e1988Sjohnlev  */
2048843e1988Sjohnlev 
2049843e1988Sjohnlev #define	FRONT_RING(_ringp)	(&(_ringp)->xr_sring.fr)
2050843e1988Sjohnlev #define	BACK_RING(_ringp)	(&(_ringp)->xr_sring.br)
2051843e1988Sjohnlev #define	GET_RING_SIZE(_ringp)	RING_SIZE(FRONT_RING(ringp))
2052843e1988Sjohnlev #define	GET_RING_ENTRY_FE(_ringp, _idx)		\
2053843e1988Sjohnlev 	(FRONT_RING(_ringp)->sring->ring +	\
2054843e1988Sjohnlev 	(_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
2055843e1988Sjohnlev #define	GET_RING_ENTRY_BE(_ringp, _idx)		\
2056843e1988Sjohnlev 	(BACK_RING(_ringp)->sring->ring +	\
2057843e1988Sjohnlev 	(_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
2058843e1988Sjohnlev 
2059843e1988Sjohnlev unsigned int
2060843e1988Sjohnlev xvdi_ring_avail_slots(xendev_ring_t *ringp)
2061843e1988Sjohnlev {
2062843e1988Sjohnlev 	comif_ring_fe_t *frp;
2063843e1988Sjohnlev 	comif_ring_be_t *brp;
2064843e1988Sjohnlev 
2065843e1988Sjohnlev 	if (ringp->xr_frontend) {
2066843e1988Sjohnlev 		frp = FRONT_RING(ringp);
2067843e1988Sjohnlev 		return (GET_RING_SIZE(ringp) -
2068843e1988Sjohnlev 		    (frp->req_prod_pvt - frp->rsp_cons));
2069843e1988Sjohnlev 	} else {
2070843e1988Sjohnlev 		brp = BACK_RING(ringp);
2071843e1988Sjohnlev 		return (GET_RING_SIZE(ringp) -
2072843e1988Sjohnlev 		    (brp->rsp_prod_pvt - brp->req_cons));
2073843e1988Sjohnlev 	}
2074843e1988Sjohnlev }
2075843e1988Sjohnlev 
2076843e1988Sjohnlev int
2077843e1988Sjohnlev xvdi_ring_has_unconsumed_requests(xendev_ring_t *ringp)
2078843e1988Sjohnlev {
2079843e1988Sjohnlev 	comif_ring_be_t *brp;
2080843e1988Sjohnlev 
2081843e1988Sjohnlev 	ASSERT(!ringp->xr_frontend);
2082843e1988Sjohnlev 	brp = BACK_RING(ringp);
2083843e1988Sjohnlev 	return ((brp->req_cons !=
2084843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &brp->sring->req_prod)) &&
2085843e1988Sjohnlev 	    ((brp->req_cons - brp->rsp_prod_pvt) != RING_SIZE(brp)));
2086843e1988Sjohnlev }
2087843e1988Sjohnlev 
2088843e1988Sjohnlev int
2089843e1988Sjohnlev xvdi_ring_has_incomp_request(xendev_ring_t *ringp)
2090843e1988Sjohnlev {
2091843e1988Sjohnlev 	comif_ring_fe_t *frp;
2092843e1988Sjohnlev 
2093843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
2094843e1988Sjohnlev 	frp = FRONT_RING(ringp);
2095843e1988Sjohnlev 	return (frp->req_prod_pvt !=
2096843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
2097843e1988Sjohnlev }
2098843e1988Sjohnlev 
2099843e1988Sjohnlev int
2100843e1988Sjohnlev xvdi_ring_has_unconsumed_responses(xendev_ring_t *ringp)
2101843e1988Sjohnlev {
2102843e1988Sjohnlev 	comif_ring_fe_t *frp;
2103843e1988Sjohnlev 
2104843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
2105843e1988Sjohnlev 	frp = FRONT_RING(ringp);
2106843e1988Sjohnlev 	return (frp->rsp_cons !=
2107843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
2108843e1988Sjohnlev }
2109843e1988Sjohnlev 
2110843e1988Sjohnlev /* NOTE: req_event will be increased as needed */
2111843e1988Sjohnlev void *
2112843e1988Sjohnlev xvdi_ring_get_request(xendev_ring_t *ringp)
2113843e1988Sjohnlev {
2114843e1988Sjohnlev 	comif_ring_fe_t *frp;
2115843e1988Sjohnlev 	comif_ring_be_t *brp;
2116843e1988Sjohnlev 
2117843e1988Sjohnlev 	if (ringp->xr_frontend) {
2118843e1988Sjohnlev 		/* for frontend ring */
2119843e1988Sjohnlev 		frp = FRONT_RING(ringp);
2120843e1988Sjohnlev 		if (!RING_FULL(frp))
2121843e1988Sjohnlev 			return (GET_RING_ENTRY_FE(ringp, frp->req_prod_pvt++));
2122843e1988Sjohnlev 		else
2123843e1988Sjohnlev 			return (NULL);
2124843e1988Sjohnlev 	} else {
2125843e1988Sjohnlev 		/* for backend ring */
2126843e1988Sjohnlev 		brp = BACK_RING(ringp);
2127843e1988Sjohnlev 		/* RING_FINAL_CHECK_FOR_REQUESTS() */
2128843e1988Sjohnlev 		if (xvdi_ring_has_unconsumed_requests(ringp))
2129843e1988Sjohnlev 			return (GET_RING_ENTRY_BE(ringp, brp->req_cons++));
2130843e1988Sjohnlev 		else {
2131843e1988Sjohnlev 			ddi_put32(ringp->xr_acc_hdl, &brp->sring->req_event,
2132843e1988Sjohnlev 			    brp->req_cons + 1);
2133843e1988Sjohnlev 			membar_enter();
2134843e1988Sjohnlev 			if (xvdi_ring_has_unconsumed_requests(ringp))
2135843e1988Sjohnlev 				return (GET_RING_ENTRY_BE(ringp,
2136843e1988Sjohnlev 				    brp->req_cons++));
2137843e1988Sjohnlev 			else
2138843e1988Sjohnlev 				return (NULL);
2139843e1988Sjohnlev 		}
2140843e1988Sjohnlev 	}
2141843e1988Sjohnlev }
2142843e1988Sjohnlev 
2143843e1988Sjohnlev int
2144843e1988Sjohnlev xvdi_ring_push_request(xendev_ring_t *ringp)
2145843e1988Sjohnlev {
2146843e1988Sjohnlev 	RING_IDX old, new, reqevt;
2147843e1988Sjohnlev 	comif_ring_fe_t *frp;
2148843e1988Sjohnlev 
2149843e1988Sjohnlev 	/* only frontend should be able to push request */
2150843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
2151843e1988Sjohnlev 
2152843e1988Sjohnlev 	/* RING_PUSH_REQUEST_AND_CHECK_NOTIFY() */
2153843e1988Sjohnlev 	frp = FRONT_RING(ringp);
2154843e1988Sjohnlev 	old = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_prod);
2155843e1988Sjohnlev 	new = frp->req_prod_pvt;
2156843e1988Sjohnlev 	ddi_put32(ringp->xr_acc_hdl, &frp->sring->req_prod, new);
2157843e1988Sjohnlev 	membar_enter();
2158843e1988Sjohnlev 	reqevt = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_event);
2159843e1988Sjohnlev 	return ((RING_IDX)(new - reqevt) < (RING_IDX)(new - old));
2160843e1988Sjohnlev }
2161843e1988Sjohnlev 
2162843e1988Sjohnlev /* NOTE: rsp_event will be increased as needed */
2163843e1988Sjohnlev void *
2164843e1988Sjohnlev xvdi_ring_get_response(xendev_ring_t *ringp)
2165843e1988Sjohnlev {
2166843e1988Sjohnlev 	comif_ring_fe_t *frp;
2167843e1988Sjohnlev 	comif_ring_be_t *brp;
2168843e1988Sjohnlev 
2169843e1988Sjohnlev 	if (!ringp->xr_frontend) {
2170843e1988Sjohnlev 		/* for backend ring */
2171843e1988Sjohnlev 		brp = BACK_RING(ringp);
2172843e1988Sjohnlev 		return (GET_RING_ENTRY_BE(ringp, brp->rsp_prod_pvt++));
2173843e1988Sjohnlev 	} else {
2174843e1988Sjohnlev 		/* for frontend ring */
2175843e1988Sjohnlev 		frp = FRONT_RING(ringp);
2176843e1988Sjohnlev 		/* RING_FINAL_CHECK_FOR_RESPONSES() */
2177843e1988Sjohnlev 		if (xvdi_ring_has_unconsumed_responses(ringp))
2178843e1988Sjohnlev 			return (GET_RING_ENTRY_FE(ringp, frp->rsp_cons++));
2179843e1988Sjohnlev 		else {
2180843e1988Sjohnlev 			ddi_put32(ringp->xr_acc_hdl, &frp->sring->rsp_event,
2181843e1988Sjohnlev 			    frp->rsp_cons + 1);
2182843e1988Sjohnlev 			membar_enter();
2183843e1988Sjohnlev 			if (xvdi_ring_has_unconsumed_responses(ringp))
2184843e1988Sjohnlev 				return (GET_RING_ENTRY_FE(ringp,
2185843e1988Sjohnlev 				    frp->rsp_cons++));
2186843e1988Sjohnlev 			else
2187843e1988Sjohnlev 				return (NULL);
2188843e1988Sjohnlev 		}
2189843e1988Sjohnlev 	}
2190843e1988Sjohnlev }
2191843e1988Sjohnlev 
2192843e1988Sjohnlev int
2193843e1988Sjohnlev xvdi_ring_push_response(xendev_ring_t *ringp)
2194843e1988Sjohnlev {
2195843e1988Sjohnlev 	RING_IDX old, new, rspevt;
2196843e1988Sjohnlev 	comif_ring_be_t *brp;
2197843e1988Sjohnlev 
2198843e1988Sjohnlev 	/* only backend should be able to push response */
2199843e1988Sjohnlev 	ASSERT(!ringp->xr_frontend);
2200843e1988Sjohnlev 
2201843e1988Sjohnlev 	/* RING_PUSH_RESPONSE_AND_CHECK_NOTIFY() */
2202843e1988Sjohnlev 	brp = BACK_RING(ringp);
2203843e1988Sjohnlev 	old = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_prod);
2204843e1988Sjohnlev 	new = brp->rsp_prod_pvt;
2205843e1988Sjohnlev 	ddi_put32(ringp->xr_acc_hdl, &brp->sring->rsp_prod, new);
2206843e1988Sjohnlev 	membar_enter();
2207843e1988Sjohnlev 	rspevt = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_event);
2208843e1988Sjohnlev 	return ((RING_IDX)(new - rspevt) < (RING_IDX)(new - old));
2209843e1988Sjohnlev }
2210843e1988Sjohnlev 
2211843e1988Sjohnlev static void
2212843e1988Sjohnlev xvdi_ring_init_sring(xendev_ring_t *ringp)
2213843e1988Sjohnlev {
2214843e1988Sjohnlev 	ddi_acc_handle_t acchdl;
2215843e1988Sjohnlev 	comif_sring_t *xsrp;
2216843e1988Sjohnlev 	int i;
2217843e1988Sjohnlev 
2218843e1988Sjohnlev 	xsrp = (comif_sring_t *)ringp->xr_vaddr;
2219843e1988Sjohnlev 	acchdl = ringp->xr_acc_hdl;
2220843e1988Sjohnlev 
2221843e1988Sjohnlev 	/* shared ring initialization */
2222843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->req_prod, 0);
2223843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->rsp_prod, 0);
2224843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->req_event, 1);
2225843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->rsp_event, 1);
2226843e1988Sjohnlev 	for (i = 0; i < sizeof (xsrp->pad); i++)
2227843e1988Sjohnlev 		ddi_put8(acchdl, xsrp->pad + i, 0);
2228843e1988Sjohnlev }
2229843e1988Sjohnlev 
2230843e1988Sjohnlev static void
2231843e1988Sjohnlev xvdi_ring_init_front_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
2232843e1988Sjohnlev {
2233843e1988Sjohnlev 	comif_ring_fe_t *xfrp;
2234843e1988Sjohnlev 
2235843e1988Sjohnlev 	xfrp = &ringp->xr_sring.fr;
2236843e1988Sjohnlev 	xfrp->req_prod_pvt = 0;
2237843e1988Sjohnlev 	xfrp->rsp_cons = 0;
2238843e1988Sjohnlev 	xfrp->nr_ents = nentry;
2239843e1988Sjohnlev 	xfrp->sring = (comif_sring_t *)ringp->xr_vaddr;
2240843e1988Sjohnlev 
2241843e1988Sjohnlev 	ringp->xr_frontend = 1;
2242843e1988Sjohnlev 	ringp->xr_entry_size = entrysize;
2243843e1988Sjohnlev }
2244843e1988Sjohnlev 
2245551bc2a6Smrj #ifndef XPV_HVM_DRIVER
2246843e1988Sjohnlev static void
2247843e1988Sjohnlev xvdi_ring_init_back_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
2248843e1988Sjohnlev {
2249843e1988Sjohnlev 	comif_ring_be_t *xbrp;
2250843e1988Sjohnlev 
2251843e1988Sjohnlev 	xbrp = &ringp->xr_sring.br;
2252843e1988Sjohnlev 	xbrp->rsp_prod_pvt = 0;
2253843e1988Sjohnlev 	xbrp->req_cons = 0;
2254843e1988Sjohnlev 	xbrp->nr_ents = nentry;
2255843e1988Sjohnlev 	xbrp->sring = (comif_sring_t *)ringp->xr_vaddr;
2256843e1988Sjohnlev 
2257843e1988Sjohnlev 	ringp->xr_frontend = 0;
2258843e1988Sjohnlev 	ringp->xr_entry_size = entrysize;
2259843e1988Sjohnlev }
2260551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
2261843e1988Sjohnlev 
2262843e1988Sjohnlev static void
2263843e1988Sjohnlev xendev_offline_device(void *arg)
2264843e1988Sjohnlev {
2265843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)arg;
2266843e1988Sjohnlev 	char devname[MAXNAMELEN] = {0};
2267843e1988Sjohnlev 
2268843e1988Sjohnlev 	/*
2269843e1988Sjohnlev 	 * This is currently the only chance to delete a devinfo node, which
2270843e1988Sjohnlev 	 * is _not_ always successful.
2271843e1988Sjohnlev 	 */
2272843e1988Sjohnlev 	(void) ddi_deviname(dip, devname);
2273843e1988Sjohnlev 	(void) devfs_clean(ddi_get_parent(dip), devname + 1, DV_CLEAN_FORCE);
2274843e1988Sjohnlev 	(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
2275843e1988Sjohnlev }
2276843e1988Sjohnlev 
2277843e1988Sjohnlev static void
2278843e1988Sjohnlev i_xvdi_oestate_cb(struct xenbus_device *dev, XenbusState oestate)
2279843e1988Sjohnlev {
2280843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)dev->data;
2281843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
2282eea6c6b9SMax zhen 	i_oestate_evt_t *evt = NULL;
22837eea693dSMark Johnson 	boolean_t call_handler;
2284eea6c6b9SMax zhen 
2285eea6c6b9SMax zhen 	XVDI_DPRINTF(XVDI_DBG_STATE,
2286eea6c6b9SMax zhen 	    "i_xvdi_oestate_cb: %s@%s sees oestate change to %d\n",
2287eea6c6b9SMax zhen 	    ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
2288eea6c6b9SMax zhen 	    ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
2289eea6c6b9SMax zhen 	    oestate);
2290843e1988Sjohnlev 
22917eea693dSMark Johnson 	/* only call the handler if our state has changed */
22927eea693dSMark Johnson 	call_handler = B_FALSE;
22937eea693dSMark Johnson 	mutex_enter(&pdp->xd_ndi_lk);
22947eea693dSMark Johnson 	if (dev->otherend_state != oestate) {
22957eea693dSMark Johnson 		dev->otherend_state = oestate;
22967eea693dSMark Johnson 		call_handler = B_TRUE;
22977eea693dSMark Johnson 	}
22987eea693dSMark Johnson 	mutex_exit(&pdp->xd_ndi_lk);
2299eea6c6b9SMax zhen 
23007eea693dSMark Johnson 	if (call_handler) {
23017eea693dSMark Johnson 		/*
23027eea693dSMark Johnson 		 * Try to deliver the oestate change event to the dip
23037eea693dSMark Johnson 		 */
23047eea693dSMark Johnson 		evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP);
23057eea693dSMark Johnson 		evt->dip = dip;
23067eea693dSMark Johnson 		evt->state = oestate;
23077eea693dSMark Johnson 		(void) ddi_taskq_dispatch(pdp->xd_oe_taskq,
23087eea693dSMark Johnson 		    i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP);
23097eea693dSMark Johnson 	}
2310843e1988Sjohnlev }
2311843e1988Sjohnlev 
2312843e1988Sjohnlev /*ARGSUSED*/
2313843e1988Sjohnlev static void
2314843e1988Sjohnlev i_xvdi_hpstate_cb(struct xenbus_watch *w, const char **vec,
2315843e1988Sjohnlev     unsigned int len)
2316843e1988Sjohnlev {
2317843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)w->dev;
2318843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
2319843e1988Sjohnlev 
2320eea6c6b9SMax zhen #ifdef DEBUG
2321eea6c6b9SMax zhen 	char *hp_status = NULL;
2322eea6c6b9SMax zhen 	unsigned int hpl = 0;
2323eea6c6b9SMax zhen 
2324eea6c6b9SMax zhen 	(void) xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
2325eea6c6b9SMax zhen 	    (void *)&hp_status, &hpl);
2326eea6c6b9SMax zhen 	XVDI_DPRINTF(XVDI_DBG_STATE,
2327eea6c6b9SMax zhen 	    "i_xvdi_hpstate_cb: %s@%s sees hpstate change to %s\n",
2328eea6c6b9SMax zhen 	    ddi_binding_name(dip) == NULL ?  "null" : ddi_binding_name(dip),
2329eea6c6b9SMax zhen 	    ddi_get_name_addr(dip) == NULL ?  "null" : ddi_get_name_addr(dip),
2330eea6c6b9SMax zhen 	    hp_status == NULL ? "null" : hp_status);
2331eea6c6b9SMax zhen 	if (hp_status != NULL)
2332eea6c6b9SMax zhen 		kmem_free(hp_status, hpl);
2333eea6c6b9SMax zhen #endif /* DEBUG */
2334eea6c6b9SMax zhen 
2335843e1988Sjohnlev 	(void) ddi_taskq_dispatch(pdp->xd_hp_taskq,
2336843e1988Sjohnlev 	    i_xvdi_hpstate_handler, (void *)dip, DDI_SLEEP);
2337843e1988Sjohnlev }
2338843e1988Sjohnlev 
2339843e1988Sjohnlev static void
2340843e1988Sjohnlev i_xvdi_probe_path_handler(void *arg)
2341843e1988Sjohnlev {
2342843e1988Sjohnlev 	dev_info_t *parent;
2343843e1988Sjohnlev 	char *path = arg, *p = NULL;
2344843e1988Sjohnlev 	int i, vdev, circ;
2345843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
2346843e1988Sjohnlev 	boolean_t frontend;
2347843e1988Sjohnlev 	domid_t dom;
2348843e1988Sjohnlev 
2349843e1988Sjohnlev 	for (i = 0, xdcp = &xdci[0]; i < NXDC; i++, xdcp++) {
2350843e1988Sjohnlev 
2351843e1988Sjohnlev 		if ((xdcp->xs_path_fe != NULL) &&
2352843e1988Sjohnlev 		    (strncmp(path, xdcp->xs_path_fe, strlen(xdcp->xs_path_fe))
2353843e1988Sjohnlev 		    == 0)) {
2354843e1988Sjohnlev 
2355843e1988Sjohnlev 			frontend = B_TRUE;
2356843e1988Sjohnlev 			p = path + strlen(xdcp->xs_path_fe);
2357843e1988Sjohnlev 			break;
2358843e1988Sjohnlev 		}
2359843e1988Sjohnlev 
2360843e1988Sjohnlev 		if ((xdcp->xs_path_be != NULL) &&
2361843e1988Sjohnlev 		    (strncmp(path, xdcp->xs_path_be, strlen(xdcp->xs_path_be))
2362843e1988Sjohnlev 		    == 0)) {
2363843e1988Sjohnlev 
2364843e1988Sjohnlev 			frontend = B_FALSE;
2365843e1988Sjohnlev 			p = path + strlen(xdcp->xs_path_be);
2366843e1988Sjohnlev 			break;
2367843e1988Sjohnlev 		}
2368843e1988Sjohnlev 
2369843e1988Sjohnlev 	}
2370843e1988Sjohnlev 
2371843e1988Sjohnlev 	if (p == NULL) {
2372843e1988Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
2373843e1988Sjohnlev 		    "unexpected path prefix in %s", path);
2374843e1988Sjohnlev 		goto done;
2375843e1988Sjohnlev 	}
2376843e1988Sjohnlev 
2377843e1988Sjohnlev 	if (frontend) {
2378843e1988Sjohnlev 		dom = DOMID_SELF;
2379843e1988Sjohnlev 		if (sscanf(p, "/%d/", &vdev) != 1) {
2380843e1988Sjohnlev 			XVDI_DPRINTF(XVDI_DBG_PROBE,
2381843e1988Sjohnlev 			    "i_xvdi_probe_path_handler: "
2382843e1988Sjohnlev 			    "cannot parse frontend path %s",
2383843e1988Sjohnlev 			    path);
2384843e1988Sjohnlev 			goto done;
2385843e1988Sjohnlev 		}
2386843e1988Sjohnlev 	} else {
23878793b36bSNick Todd 		if (sscanf(p, "/%hu/%d/", &dom, &vdev) != 2) {
2388843e1988Sjohnlev 			XVDI_DPRINTF(XVDI_DBG_PROBE,
2389843e1988Sjohnlev 			    "i_xvdi_probe_path_handler: "
2390843e1988Sjohnlev 			    "cannot parse backend path %s",
2391843e1988Sjohnlev 			    path);
2392843e1988Sjohnlev 			goto done;
2393843e1988Sjohnlev 		}
2394843e1988Sjohnlev 	}
2395843e1988Sjohnlev 
23961ca30e39Sjohnlev 	/*
23971ca30e39Sjohnlev 	 * This is an oxymoron, so indicates a bogus configuration we
23981ca30e39Sjohnlev 	 * must check for.
23991ca30e39Sjohnlev 	 */
24001ca30e39Sjohnlev 	if (vdev == VDEV_NOXS) {
24011ca30e39Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
24021ca30e39Sjohnlev 		    "invalid path %s", path);
24031ca30e39Sjohnlev 		goto done;
24041ca30e39Sjohnlev 	}
24051ca30e39Sjohnlev 
2406843e1988Sjohnlev 	parent = xendev_dip;
2407843e1988Sjohnlev 	ASSERT(parent != NULL);
2408843e1988Sjohnlev 
2409843e1988Sjohnlev 	ndi_devi_enter(parent, &circ);
2410843e1988Sjohnlev 
2411843e1988Sjohnlev 	if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL) {
2412843e1988Sjohnlev 		XVDI_DPRINTF(XVDI_DBG_PROBE,
2413843e1988Sjohnlev 		    "i_xvdi_probe_path_handler: create for %s", path);
2414843e1988Sjohnlev 		(void) xvdi_create_dev(parent, xdcp->devclass, dom, vdev);
2415843e1988Sjohnlev 	} else {
2416843e1988Sjohnlev 		XVDI_DPRINTF(XVDI_DBG_PROBE,
2417843e1988Sjohnlev 		    "i_xvdi_probe_path_handler: %s already exists", path);
2418843e1988Sjohnlev 	}
2419843e1988Sjohnlev 
2420843e1988Sjohnlev 	ndi_devi_exit(parent, circ);
2421843e1988Sjohnlev 
2422843e1988Sjohnlev done:
2423843e1988Sjohnlev 	kmem_free(path, strlen(path) + 1);
2424843e1988Sjohnlev }
2425