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