15084Sjohnlev /*
25084Sjohnlev * CDDL HEADER START
35084Sjohnlev *
45084Sjohnlev * The contents of this file are subject to the terms of the
55084Sjohnlev * Common Development and Distribution License (the "License").
65084Sjohnlev * You may not use this file except in compliance with the License.
75084Sjohnlev *
85084Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev * See the License for the specific language governing permissions
115084Sjohnlev * and limitations under the License.
125084Sjohnlev *
135084Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev *
195084Sjohnlev * CDDL HEADER END
205084Sjohnlev */
215084Sjohnlev
225084Sjohnlev /*
238863SEdward.Pilatowicz@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
245084Sjohnlev * Use is subject to license terms.
255084Sjohnlev */
265084Sjohnlev
275084Sjohnlev /*
285084Sjohnlev * Xen virtual device driver interfaces
295084Sjohnlev */
305084Sjohnlev
315084Sjohnlev /*
325084Sjohnlev * todo:
335084Sjohnlev * + name space clean up:
345084Sjohnlev * xvdi_* - public xen interfaces, for use by all leaf drivers
355084Sjohnlev * xd_* - public xen data structures
365084Sjohnlev * i_xvdi_* - implementation private functions
375084Sjohnlev * xendev_* - xendev driver interfaces, both internal and in cb_ops/bus_ops
385084Sjohnlev * + add mdb dcmds to dump ring status
395084Sjohnlev * + implement xvdi_xxx to wrap xenbus_xxx read/write function
405084Sjohnlev * + convert (xendev_ring_t *) into xvdi_ring_handle_t
415084Sjohnlev */
425084Sjohnlev #include <sys/conf.h>
435084Sjohnlev #include <sys/param.h>
445084Sjohnlev #include <sys/kmem.h>
455084Sjohnlev #include <vm/seg_kmem.h>
465084Sjohnlev #include <sys/debug.h>
475084Sjohnlev #include <sys/modctl.h>
485084Sjohnlev #include <sys/autoconf.h>
495084Sjohnlev #include <sys/ddi_impldefs.h>
505084Sjohnlev #include <sys/ddi_subrdefs.h>
515084Sjohnlev #include <sys/ddi.h>
525084Sjohnlev #include <sys/sunddi.h>
535084Sjohnlev #include <sys/sunndi.h>
545084Sjohnlev #include <sys/sunldi.h>
555084Sjohnlev #include <sys/fs/dv_node.h>
565084Sjohnlev #include <sys/avintr.h>
575084Sjohnlev #include <sys/psm.h>
585084Sjohnlev #include <sys/spl.h>
595084Sjohnlev #include <sys/promif.h>
605084Sjohnlev #include <sys/list.h>
615084Sjohnlev #include <sys/bootconf.h>
625084Sjohnlev #include <sys/bootsvcs.h>
635084Sjohnlev #include <sys/bootinfo.h>
645084Sjohnlev #include <sys/note.h>
658863SEdward.Pilatowicz@Sun.COM #include <sys/sysmacros.h>
665741Smrj #ifdef XPV_HVM_DRIVER
675741Smrj #include <sys/xpv_support.h>
685741Smrj #include <sys/hypervisor.h>
695741Smrj #include <public/grant_table.h>
705741Smrj #include <public/xen.h>
715741Smrj #include <public/io/xenbus.h>
725741Smrj #include <public/io/xs_wire.h>
735741Smrj #include <public/event_channel.h>
745741Smrj #include <public/io/xenbus.h>
755741Smrj #else /* XPV_HVM_DRIVER */
765741Smrj #include <sys/hypervisor.h>
775084Sjohnlev #include <sys/xen_mmu.h>
785084Sjohnlev #include <xen/sys/xenbus_impl.h>
795741Smrj #include <sys/evtchn_impl.h>
805741Smrj #endif /* XPV_HVM_DRIVER */
815741Smrj #include <sys/gnttab.h>
825084Sjohnlev #include <xen/sys/xendev.h>
835084Sjohnlev #include <vm/hat_i86.h>
845084Sjohnlev #include <sys/scsi/generic/inquiry.h>
855084Sjohnlev #include <util/sscanf.h>
865084Sjohnlev #include <xen/public/io/xs_wire.h>
875084Sjohnlev
885084Sjohnlev
896318Sedp #define isdigit(ch) ((ch) >= '0' && (ch) <= '9')
906318Sedp #define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
916318Sedp ((ch) >= 'A' && (ch) <= 'F'))
926318Sedp
935084Sjohnlev static void xvdi_ring_init_sring(xendev_ring_t *);
945084Sjohnlev static void xvdi_ring_init_front_ring(xendev_ring_t *, size_t, size_t);
955741Smrj #ifndef XPV_HVM_DRIVER
965084Sjohnlev static void xvdi_ring_init_back_ring(xendev_ring_t *, size_t, size_t);
975741Smrj #endif
985084Sjohnlev static void xvdi_reinit_ring(dev_info_t *, grant_ref_t *, xendev_ring_t *);
995084Sjohnlev
1005084Sjohnlev static int i_xvdi_add_watches(dev_info_t *);
1015084Sjohnlev static void i_xvdi_rem_watches(dev_info_t *);
1025084Sjohnlev
1035084Sjohnlev static int i_xvdi_add_watch_oestate(dev_info_t *);
1045084Sjohnlev static void i_xvdi_rem_watch_oestate(dev_info_t *);
1055084Sjohnlev static void i_xvdi_oestate_cb(struct xenbus_device *, XenbusState);
1065084Sjohnlev static void i_xvdi_oestate_handler(void *);
1075084Sjohnlev
1085084Sjohnlev static int i_xvdi_add_watch_hpstate(dev_info_t *);
1095084Sjohnlev static void i_xvdi_rem_watch_hpstate(dev_info_t *);
1105084Sjohnlev static void i_xvdi_hpstate_cb(struct xenbus_watch *, const char **,
1115084Sjohnlev unsigned int);
1125084Sjohnlev static void i_xvdi_hpstate_handler(void *);
1135084Sjohnlev
1145084Sjohnlev static int i_xvdi_add_watch_bepath(dev_info_t *);
1155084Sjohnlev static void i_xvdi_rem_watch_bepath(dev_info_t *);
1165084Sjohnlev static void i_xvdi_bepath_cb(struct xenbus_watch *, const char **,
1175084Sjohnlev unsigned in);
1185084Sjohnlev
1195084Sjohnlev static void xendev_offline_device(void *);
1205084Sjohnlev
1215084Sjohnlev static void i_xvdi_probe_path_cb(struct xenbus_watch *, const char **,
1225084Sjohnlev unsigned int);
1235084Sjohnlev static void i_xvdi_probe_path_handler(void *);
1245084Sjohnlev
1257679SMax.Zhen@Sun.COM typedef struct oestate_evt {
1267679SMax.Zhen@Sun.COM dev_info_t *dip;
1277679SMax.Zhen@Sun.COM XenbusState state;
1287679SMax.Zhen@Sun.COM } i_oestate_evt_t;
1297679SMax.Zhen@Sun.COM
1305084Sjohnlev typedef struct xd_cfg {
1315084Sjohnlev xendev_devclass_t devclass;
1325084Sjohnlev char *xsdev;
1335084Sjohnlev char *xs_path_fe;
1345084Sjohnlev char *xs_path_be;
1355084Sjohnlev char *node_fe;
1365084Sjohnlev char *node_be;
1375084Sjohnlev char *device_type;
1385084Sjohnlev int xd_ipl;
1395084Sjohnlev int flags;
1405084Sjohnlev } i_xd_cfg_t;
1415084Sjohnlev
1425084Sjohnlev #define XD_DOM_ZERO 0x01 /* dom0 only. */
1435084Sjohnlev #define XD_DOM_GUEST 0x02 /* Guest domains (i.e. non-dom0). */
1445084Sjohnlev #define XD_DOM_IO 0x04 /* IO domains. */
1455084Sjohnlev
1465084Sjohnlev #define XD_DOM_ALL (XD_DOM_ZERO | XD_DOM_GUEST)
1475084Sjohnlev
1485084Sjohnlev static i_xd_cfg_t xdci[] = {
1495084Sjohnlev { XEN_CONSOLE, NULL, NULL, NULL, "xencons", NULL,
1505084Sjohnlev "console", IPL_CONS, XD_DOM_ALL, },
1515084Sjohnlev
1525084Sjohnlev { XEN_VNET, "vif", "device/vif", "backend/vif", "xnf", "xnb",
1535084Sjohnlev "network", IPL_VIF, XD_DOM_ALL, },
1545084Sjohnlev
1555084Sjohnlev { XEN_VBLK, "vbd", "device/vbd", "backend/vbd", "xdf", "xdb",
1565084Sjohnlev "block", IPL_VBD, XD_DOM_ALL, },
1575084Sjohnlev
1587756SMark.Johnson@Sun.COM { XEN_BLKTAP, "tap", NULL, "backend/tap", NULL, "xpvtap",
1597756SMark.Johnson@Sun.COM "block", IPL_VBD, XD_DOM_ALL, },
1607756SMark.Johnson@Sun.COM
1615084Sjohnlev { XEN_XENBUS, NULL, NULL, NULL, "xenbus", NULL,
1625084Sjohnlev NULL, 0, XD_DOM_ALL, },
1635084Sjohnlev
1645084Sjohnlev { XEN_DOMCAPS, NULL, NULL, NULL, "domcaps", NULL,
1655084Sjohnlev NULL, 0, XD_DOM_ALL, },
1665084Sjohnlev
1675084Sjohnlev { XEN_BALLOON, NULL, NULL, NULL, "balloon", NULL,
1685084Sjohnlev NULL, 0, XD_DOM_ALL, },
1695084Sjohnlev
1705084Sjohnlev { XEN_EVTCHN, NULL, NULL, NULL, "evtchn", NULL,
1715084Sjohnlev NULL, 0, XD_DOM_ZERO, },
1725084Sjohnlev
1735084Sjohnlev { XEN_PRIVCMD, NULL, NULL, NULL, "privcmd", NULL,
1745084Sjohnlev NULL, 0, XD_DOM_ZERO, },
1755084Sjohnlev };
1765084Sjohnlev #define NXDC (sizeof (xdci) / sizeof (xdci[0]))
1775084Sjohnlev
1785084Sjohnlev static void i_xvdi_enum_fe(dev_info_t *, i_xd_cfg_t *);
1795084Sjohnlev static void i_xvdi_enum_be(dev_info_t *, i_xd_cfg_t *);
1805084Sjohnlev static void i_xvdi_enum_worker(dev_info_t *, i_xd_cfg_t *, char *);
1815084Sjohnlev
1825084Sjohnlev /*
1835084Sjohnlev * Xen device channel device access and DMA attributes
1845084Sjohnlev */
1855084Sjohnlev static ddi_device_acc_attr_t xendev_dc_accattr = {
1865084Sjohnlev DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC
1875084Sjohnlev };
1885084Sjohnlev
1895084Sjohnlev static ddi_dma_attr_t xendev_dc_dmaattr = {
1905084Sjohnlev DMA_ATTR_V0, /* version of this structure */
1915084Sjohnlev 0, /* lowest usable address */
1925084Sjohnlev 0xffffffffffffffffULL, /* highest usable address */
1935084Sjohnlev 0x7fffffff, /* maximum DMAable byte count */
1945084Sjohnlev MMU_PAGESIZE, /* alignment in bytes */
1955084Sjohnlev 0x7ff, /* bitmap of burst sizes */
1965084Sjohnlev 1, /* minimum transfer */
1975084Sjohnlev 0xffffffffU, /* maximum transfer */
1985084Sjohnlev 0xffffffffffffffffULL, /* maximum segment length */
1995084Sjohnlev 1, /* maximum number of segments */
2005084Sjohnlev 1, /* granularity */
2015084Sjohnlev 0, /* flags (reserved) */
2025084Sjohnlev };
2035084Sjohnlev
2045084Sjohnlev static dev_info_t *xendev_dip = NULL;
2055084Sjohnlev
2065084Sjohnlev #define XVDI_DBG_STATE 0x01
2075084Sjohnlev #define XVDI_DBG_PROBE 0x02
2085084Sjohnlev
2095084Sjohnlev #ifdef DEBUG
2105316Sjohnlev int i_xvdi_debug = 0;
2115084Sjohnlev
2125084Sjohnlev #define XVDI_DPRINTF(flag, format, ...) \
2135084Sjohnlev { \
2145084Sjohnlev if (i_xvdi_debug & (flag)) \
2155084Sjohnlev prom_printf((format), __VA_ARGS__); \
2165084Sjohnlev }
2175084Sjohnlev #else
2185084Sjohnlev #define XVDI_DPRINTF(flag, format, ...)
2195084Sjohnlev #endif /* DEBUG */
2205084Sjohnlev
2215084Sjohnlev static i_xd_cfg_t *
i_xvdi_devclass2cfg(xendev_devclass_t devclass)2225084Sjohnlev i_xvdi_devclass2cfg(xendev_devclass_t devclass)
2235084Sjohnlev {
2245084Sjohnlev i_xd_cfg_t *xdcp;
2255084Sjohnlev int i;
2265084Sjohnlev
2275084Sjohnlev for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++)
2285084Sjohnlev if (xdcp->devclass == devclass)
2295084Sjohnlev return (xdcp);
2305084Sjohnlev
2315084Sjohnlev return (NULL);
2325084Sjohnlev }
2335084Sjohnlev
2345084Sjohnlev int
xvdi_init_dev(dev_info_t * dip)2355084Sjohnlev xvdi_init_dev(dev_info_t *dip)
2365084Sjohnlev {
2375084Sjohnlev xendev_devclass_t devcls;
2385084Sjohnlev int vdevnum;
2395084Sjohnlev domid_t domid;
2405084Sjohnlev struct xendev_ppd *pdp;
2415084Sjohnlev i_xd_cfg_t *xdcp;
2425084Sjohnlev boolean_t backend;
2435084Sjohnlev char xsnamebuf[TYPICALMAXPATHLEN];
2445084Sjohnlev char *xsname;
2456500Sjhd void *prop_str;
2466503Sjhd unsigned int prop_len;
2476500Sjhd char unitaddr[8];
2485084Sjohnlev
2495084Sjohnlev devcls = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2505084Sjohnlev DDI_PROP_DONTPASS, "devclass", XEN_INVAL);
2515084Sjohnlev vdevnum = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2526175Sjohnlev DDI_PROP_DONTPASS, "vdev", VDEV_NOXS);
2535084Sjohnlev domid = (domid_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2545084Sjohnlev DDI_PROP_DONTPASS, "domain", DOMID_SELF);
2555084Sjohnlev
2565084Sjohnlev backend = (domid != DOMID_SELF);
2575084Sjohnlev xdcp = i_xvdi_devclass2cfg(devcls);
2585084Sjohnlev if (xdcp->device_type != NULL)
2595084Sjohnlev (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
2605084Sjohnlev "device_type", xdcp->device_type);
2615084Sjohnlev
2625084Sjohnlev pdp = kmem_zalloc(sizeof (*pdp), KM_SLEEP);
2635084Sjohnlev pdp->xd_domain = domid;
2645084Sjohnlev pdp->xd_vdevnum = vdevnum;
2655084Sjohnlev pdp->xd_devclass = devcls;
2665084Sjohnlev pdp->xd_evtchn = INVALID_EVTCHN;
2678863SEdward.Pilatowicz@Sun.COM list_create(&pdp->xd_xb_watches, sizeof (xd_xb_watches_t),
2688863SEdward.Pilatowicz@Sun.COM offsetof(xd_xb_watches_t, xxw_list));
2697756SMark.Johnson@Sun.COM mutex_init(&pdp->xd_evt_lk, NULL, MUTEX_DRIVER, NULL);
2707756SMark.Johnson@Sun.COM mutex_init(&pdp->xd_ndi_lk, NULL, MUTEX_DRIVER, NULL);
2715084Sjohnlev ddi_set_parent_data(dip, pdp);
2725084Sjohnlev
2735084Sjohnlev /*
2745084Sjohnlev * devices that do not need to interact with xenstore
2755084Sjohnlev */
2766175Sjohnlev if (vdevnum == VDEV_NOXS) {
2775084Sjohnlev (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
2785084Sjohnlev "unit-address", "0");
2795084Sjohnlev if (devcls == XEN_CONSOLE)
2805084Sjohnlev (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
2815084Sjohnlev "pm-hardware-state", "needs-suspend-resume");
2825084Sjohnlev return (DDI_SUCCESS);
2835084Sjohnlev }
2845084Sjohnlev
2855084Sjohnlev /*
2865084Sjohnlev * PV devices that need to probe xenstore
2875084Sjohnlev */
2885084Sjohnlev
2895084Sjohnlev (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
2905084Sjohnlev "pm-hardware-state", "needs-suspend-resume");
2915084Sjohnlev
2925084Sjohnlev xsname = xsnamebuf;
2935084Sjohnlev if (!backend)
2945084Sjohnlev (void) snprintf(xsnamebuf, sizeof (xsnamebuf),
2955084Sjohnlev "%s/%d", xdcp->xs_path_fe, vdevnum);
2965084Sjohnlev else
2975084Sjohnlev (void) snprintf(xsnamebuf, sizeof (xsnamebuf),
2985084Sjohnlev "%s/%d/%d", xdcp->xs_path_be, domid, vdevnum);
2995159Sjohnlev if ((xenbus_read_driver_state(xsname) >= XenbusStateClosing)) {
3005159Sjohnlev /* Don't try to init a dev that may be closing */
3017756SMark.Johnson@Sun.COM mutex_destroy(&pdp->xd_ndi_lk);
3027756SMark.Johnson@Sun.COM mutex_destroy(&pdp->xd_evt_lk);
3035159Sjohnlev kmem_free(pdp, sizeof (*pdp));
3045159Sjohnlev ddi_set_parent_data(dip, NULL);
3055159Sjohnlev return (DDI_FAILURE);
3065159Sjohnlev }
3075084Sjohnlev
3085084Sjohnlev pdp->xd_xsdev.nodename = i_ddi_strdup(xsname, KM_SLEEP);
3095084Sjohnlev pdp->xd_xsdev.devicetype = xdcp->xsdev;
3105084Sjohnlev pdp->xd_xsdev.frontend = (backend ? 0 : 1);
3115084Sjohnlev pdp->xd_xsdev.data = dip;
3125084Sjohnlev pdp->xd_xsdev.otherend_id = (backend ? domid : -1);
3135084Sjohnlev if (i_xvdi_add_watches(dip) != DDI_SUCCESS) {
3145084Sjohnlev cmn_err(CE_WARN, "xvdi_init_dev: "
3155084Sjohnlev "cannot add watches for %s", xsname);
3165084Sjohnlev xvdi_uninit_dev(dip);
3175084Sjohnlev return (DDI_FAILURE);
3185084Sjohnlev }
3195084Sjohnlev
3206500Sjhd if (backend)
3216500Sjhd return (DDI_SUCCESS);
3225084Sjohnlev
3236500Sjhd /*
3246500Sjhd * The unit-address for frontend devices is the name of the
3256500Sjhd * of the xenstore node containing the device configuration
3266500Sjhd * and is contained in the 'vdev' property.
3276500Sjhd * VIF devices are named using an incrementing integer.
3286500Sjhd * VBD devices are either named using the 16-bit dev_t value
3296500Sjhd * for linux 'hd' and 'xvd' devices, or a simple integer value
3306500Sjhd * in the range 0..767. 768 is the base value of the linux
3316500Sjhd * dev_t namespace, the dev_t value for 'hda'.
3326500Sjhd */
3336500Sjhd (void) snprintf(unitaddr, sizeof (unitaddr), "%d", vdevnum);
3346500Sjhd (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "unit-address",
3356500Sjhd unitaddr);
3366318Sedp
3376500Sjhd switch (devcls) {
3386500Sjhd case XEN_VNET:
3396500Sjhd if (xenbus_read(XBT_NULL, xsname, "mac", (void *)&prop_str,
3406500Sjhd &prop_len) != 0)
3415084Sjohnlev break;
3426500Sjhd (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "mac",
3436500Sjhd prop_str);
3446500Sjhd kmem_free(prop_str, prop_len);
3456500Sjhd break;
3466500Sjhd case XEN_VBLK:
3476500Sjhd /*
3486500Sjhd * cache a copy of the otherend name
3496500Sjhd * for ease of observeability
3506500Sjhd */
3516500Sjhd if (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend, "dev",
3526500Sjhd &prop_str, &prop_len) != 0)
3535084Sjohnlev break;
3546500Sjhd (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
3556500Sjhd "dev-address", prop_str);
3566500Sjhd kmem_free(prop_str, prop_len);
3576500Sjhd break;
3586500Sjhd default:
3596500Sjhd break;
3605084Sjohnlev }
3615084Sjohnlev
3625084Sjohnlev return (DDI_SUCCESS);
3635084Sjohnlev }
3645084Sjohnlev
3655084Sjohnlev void
xvdi_uninit_dev(dev_info_t * dip)3665084Sjohnlev xvdi_uninit_dev(dev_info_t *dip)
3675084Sjohnlev {
3685084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
3695084Sjohnlev
3705084Sjohnlev if (pdp != NULL) {
3715084Sjohnlev /* Remove any registered callbacks. */
3725084Sjohnlev xvdi_remove_event_handler(dip, NULL);
3735084Sjohnlev
3745084Sjohnlev /* Remove any registered watches. */
3755084Sjohnlev i_xvdi_rem_watches(dip);
3765084Sjohnlev
3775159Sjohnlev /* tell other end to close */
3785741Smrj if (pdp->xd_xsdev.otherend_id != (domid_t)-1)
3795741Smrj (void) xvdi_switch_state(dip, XBT_NULL,
3805741Smrj XenbusStateClosed);
3815159Sjohnlev
3825084Sjohnlev if (pdp->xd_xsdev.nodename != NULL)
3835084Sjohnlev kmem_free((char *)(pdp->xd_xsdev.nodename),
3845084Sjohnlev strlen(pdp->xd_xsdev.nodename) + 1);
3855084Sjohnlev
3865084Sjohnlev ddi_set_parent_data(dip, NULL);
3875084Sjohnlev
3887756SMark.Johnson@Sun.COM mutex_destroy(&pdp->xd_ndi_lk);
3897756SMark.Johnson@Sun.COM mutex_destroy(&pdp->xd_evt_lk);
3905084Sjohnlev kmem_free(pdp, sizeof (*pdp));
3915084Sjohnlev }
3925084Sjohnlev }
3935084Sjohnlev
3945084Sjohnlev /*
3955084Sjohnlev * Bind the event channel for this device instance.
3965084Sjohnlev * Currently we only support one evtchn per device instance.
3975084Sjohnlev */
3985084Sjohnlev int
xvdi_bind_evtchn(dev_info_t * dip,evtchn_port_t evtchn)3995084Sjohnlev xvdi_bind_evtchn(dev_info_t *dip, evtchn_port_t evtchn)
4005084Sjohnlev {
4015084Sjohnlev struct xendev_ppd *pdp;
4025084Sjohnlev domid_t oeid;
4035084Sjohnlev int r;
4045084Sjohnlev
4055084Sjohnlev pdp = ddi_get_parent_data(dip);
4065084Sjohnlev ASSERT(pdp != NULL);
4075084Sjohnlev ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
4085084Sjohnlev
4097756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_evt_lk);
4105084Sjohnlev if (pdp->xd_devclass == XEN_CONSOLE) {
4115084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
4125084Sjohnlev pdp->xd_evtchn = xen_info->console.domU.evtchn;
4135084Sjohnlev } else {
4145084Sjohnlev pdp->xd_evtchn = INVALID_EVTCHN;
4157756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
4165084Sjohnlev return (DDI_SUCCESS);
4175084Sjohnlev }
4185084Sjohnlev } else {
4195084Sjohnlev oeid = pdp->xd_xsdev.otherend_id;
4205084Sjohnlev if (oeid == (domid_t)-1) {
4217756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
4225084Sjohnlev return (DDI_FAILURE);
4235084Sjohnlev }
4245084Sjohnlev
4255084Sjohnlev if ((r = xen_bind_interdomain(oeid, evtchn, &pdp->xd_evtchn))) {
4265084Sjohnlev xvdi_dev_error(dip, r, "bind event channel");
4277756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
4285084Sjohnlev return (DDI_FAILURE);
4295084Sjohnlev }
4305084Sjohnlev }
4315741Smrj #ifndef XPV_HVM_DRIVER
4325084Sjohnlev pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
4335741Smrj #endif
4347756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
4355084Sjohnlev
4365084Sjohnlev return (DDI_SUCCESS);
4375084Sjohnlev }
4385084Sjohnlev
4395084Sjohnlev /*
4405084Sjohnlev * Allocate an event channel for this device instance.
4415084Sjohnlev * Currently we only support one evtchn per device instance.
4425084Sjohnlev */
4435084Sjohnlev int
xvdi_alloc_evtchn(dev_info_t * dip)4445084Sjohnlev xvdi_alloc_evtchn(dev_info_t *dip)
4455084Sjohnlev {
4465084Sjohnlev struct xendev_ppd *pdp;
4475084Sjohnlev domid_t oeid;
4485084Sjohnlev int rv;
4495084Sjohnlev
4505084Sjohnlev pdp = ddi_get_parent_data(dip);
4515084Sjohnlev ASSERT(pdp != NULL);
4525084Sjohnlev ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
4535084Sjohnlev
4547756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_evt_lk);
4555084Sjohnlev if (pdp->xd_devclass == XEN_CONSOLE) {
4565084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
4575084Sjohnlev pdp->xd_evtchn = xen_info->console.domU.evtchn;
4585084Sjohnlev } else {
4595084Sjohnlev pdp->xd_evtchn = INVALID_EVTCHN;
4607756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
4615084Sjohnlev return (DDI_SUCCESS);
4625084Sjohnlev }
4635084Sjohnlev } else {
4645084Sjohnlev oeid = pdp->xd_xsdev.otherend_id;
4655084Sjohnlev if (oeid == (domid_t)-1) {
4667756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
4675084Sjohnlev return (DDI_FAILURE);
4685084Sjohnlev }
4695084Sjohnlev
4705084Sjohnlev if ((rv = xen_alloc_unbound_evtchn(oeid, &pdp->xd_evtchn))) {
4715084Sjohnlev xvdi_dev_error(dip, rv, "bind event channel");
4727756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
4735084Sjohnlev return (DDI_FAILURE);
4745084Sjohnlev }
4755084Sjohnlev }
4765741Smrj #ifndef XPV_HVM_DRIVER
4775084Sjohnlev pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
4785741Smrj #endif
4797756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
4805084Sjohnlev
4815084Sjohnlev return (DDI_SUCCESS);
4825084Sjohnlev }
4835084Sjohnlev
4845084Sjohnlev /*
4855084Sjohnlev * Unbind the event channel for this device instance.
4865084Sjohnlev * Currently we only support one evtchn per device instance.
4875084Sjohnlev */
4885084Sjohnlev void
xvdi_free_evtchn(dev_info_t * dip)4895084Sjohnlev xvdi_free_evtchn(dev_info_t *dip)
4905084Sjohnlev {
4915084Sjohnlev struct xendev_ppd *pdp;
4925084Sjohnlev
4935084Sjohnlev pdp = ddi_get_parent_data(dip);
4945084Sjohnlev ASSERT(pdp != NULL);
4955084Sjohnlev
4967756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_evt_lk);
4975084Sjohnlev if (pdp->xd_evtchn != INVALID_EVTCHN) {
4985741Smrj #ifndef XPV_HVM_DRIVER
4995084Sjohnlev ec_unbind_irq(pdp->xd_ispec.intrspec_vec);
5005741Smrj pdp->xd_ispec.intrspec_vec = 0;
5015741Smrj #endif
5025084Sjohnlev pdp->xd_evtchn = INVALID_EVTCHN;
5035084Sjohnlev }
5047756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_evt_lk);
5055084Sjohnlev }
5065084Sjohnlev
5075741Smrj #ifndef XPV_HVM_DRIVER
5085084Sjohnlev /*
5095084Sjohnlev * Map an inter-domain communication ring for a virtual device.
5105084Sjohnlev * This is used by backend drivers.
5115084Sjohnlev */
5125084Sjohnlev int
xvdi_map_ring(dev_info_t * dip,size_t nentry,size_t entrysize,grant_ref_t gref,xendev_ring_t ** ringpp)5135084Sjohnlev xvdi_map_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
5145084Sjohnlev grant_ref_t gref, xendev_ring_t **ringpp)
5155084Sjohnlev {
5165084Sjohnlev domid_t oeid;
5175084Sjohnlev gnttab_map_grant_ref_t mapop;
5185084Sjohnlev gnttab_unmap_grant_ref_t unmapop;
5195084Sjohnlev caddr_t ringva;
5205084Sjohnlev ddi_acc_hdl_t *ap;
5215084Sjohnlev ddi_acc_impl_t *iap;
5225084Sjohnlev xendev_ring_t *ring;
5235084Sjohnlev int err;
5245084Sjohnlev char errstr[] = "mapping in ring buffer";
5255084Sjohnlev
5265084Sjohnlev ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
5275084Sjohnlev oeid = xvdi_get_oeid(dip);
5285084Sjohnlev
5295084Sjohnlev /* alloc va in backend dom for ring buffer */
5305084Sjohnlev ringva = vmem_xalloc(heap_arena, PAGESIZE, PAGESIZE,
5315084Sjohnlev 0, 0, 0, 0, VM_SLEEP);
5325084Sjohnlev
5335084Sjohnlev /* map in ring page */
5347756SMark.Johnson@Sun.COM hat_prepare_mapping(kas.a_hat, ringva, NULL);
5355084Sjohnlev mapop.host_addr = (uint64_t)(uintptr_t)ringva;
5365084Sjohnlev mapop.flags = GNTMAP_host_map;
5375084Sjohnlev mapop.ref = gref;
5385084Sjohnlev mapop.dom = oeid;
5397756SMark.Johnson@Sun.COM err = xen_map_gref(GNTTABOP_map_grant_ref, &mapop, 1, B_FALSE);
5405084Sjohnlev if (err) {
5415084Sjohnlev xvdi_fatal_error(dip, err, errstr);
5425084Sjohnlev goto errout1;
5435084Sjohnlev }
5445084Sjohnlev
5455084Sjohnlev if (mapop.status != 0) {
5465084Sjohnlev xvdi_fatal_error(dip, err, errstr);
5475084Sjohnlev goto errout2;
5485084Sjohnlev }
5495084Sjohnlev ring->xr_vaddr = ringva;
5505084Sjohnlev ring->xr_grant_hdl = mapop.handle;
5515084Sjohnlev ring->xr_gref = gref;
5525084Sjohnlev
5535084Sjohnlev /*
5545084Sjohnlev * init an acc handle and associate it w/ this ring
5555084Sjohnlev * this is only for backend drivers. we get the memory by calling
5565084Sjohnlev * vmem_xalloc(), instead of calling any ddi function, so we have
5575084Sjohnlev * to init an acc handle by ourselves
5585084Sjohnlev */
5595084Sjohnlev ring->xr_acc_hdl = impl_acc_hdl_alloc(KM_SLEEP, NULL);
5605084Sjohnlev ap = impl_acc_hdl_get(ring->xr_acc_hdl);
5615084Sjohnlev ap->ah_vers = VERS_ACCHDL;
5625084Sjohnlev ap->ah_dip = dip;
5635084Sjohnlev ap->ah_xfermodes = DDI_DMA_CONSISTENT;
5645084Sjohnlev ap->ah_acc = xendev_dc_accattr;
5655084Sjohnlev iap = (ddi_acc_impl_t *)ap->ah_platform_private;
5665084Sjohnlev iap->ahi_acc_attr |= DDI_ACCATTR_CPU_VADDR;
5675084Sjohnlev impl_acc_hdl_init(ap);
5685084Sjohnlev ap->ah_offset = 0;
5695084Sjohnlev ap->ah_len = (off_t)PAGESIZE;
5705084Sjohnlev ap->ah_addr = ring->xr_vaddr;
5715084Sjohnlev
5725084Sjohnlev /* init backend ring */
5735084Sjohnlev xvdi_ring_init_back_ring(ring, nentry, entrysize);
5745084Sjohnlev
5755084Sjohnlev *ringpp = ring;
5765084Sjohnlev
5775084Sjohnlev return (DDI_SUCCESS);
5785084Sjohnlev
5795084Sjohnlev errout2:
5805084Sjohnlev /* unmap ring page */
5815084Sjohnlev unmapop.host_addr = (uint64_t)(uintptr_t)ringva;
5825084Sjohnlev unmapop.handle = ring->xr_grant_hdl;
5835084Sjohnlev unmapop.dev_bus_addr = NULL;
5845084Sjohnlev (void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
5855084Sjohnlev hat_release_mapping(kas.a_hat, ringva);
5865084Sjohnlev errout1:
5875084Sjohnlev vmem_xfree(heap_arena, ringva, PAGESIZE);
5885084Sjohnlev kmem_free(ring, sizeof (xendev_ring_t));
5895084Sjohnlev return (DDI_FAILURE);
5905084Sjohnlev }
5915084Sjohnlev
5925084Sjohnlev /*
5935084Sjohnlev * Unmap a ring for a virtual device.
5945084Sjohnlev * This is used by backend drivers.
5955084Sjohnlev */
5965084Sjohnlev void
xvdi_unmap_ring(xendev_ring_t * ring)5975084Sjohnlev xvdi_unmap_ring(xendev_ring_t *ring)
5985084Sjohnlev {
5995084Sjohnlev gnttab_unmap_grant_ref_t unmapop;
6005084Sjohnlev
6015084Sjohnlev ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
6025084Sjohnlev
6035084Sjohnlev impl_acc_hdl_free(ring->xr_acc_hdl);
6045084Sjohnlev unmapop.host_addr = (uint64_t)(uintptr_t)ring->xr_vaddr;
6055084Sjohnlev unmapop.handle = ring->xr_grant_hdl;
6065084Sjohnlev unmapop.dev_bus_addr = NULL;
6075084Sjohnlev (void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
6085084Sjohnlev hat_release_mapping(kas.a_hat, ring->xr_vaddr);
6095084Sjohnlev vmem_xfree(heap_arena, ring->xr_vaddr, PAGESIZE);
6105084Sjohnlev kmem_free(ring, sizeof (xendev_ring_t));
6115084Sjohnlev }
6125741Smrj #endif /* XPV_HVM_DRIVER */
6135084Sjohnlev
6145084Sjohnlev /*
6155084Sjohnlev * Re-initialise an inter-domain communications ring for the backend domain.
6165084Sjohnlev * ring will be re-initialized after re-grant succeed
6175084Sjohnlev * ring will be freed if fails to re-grant access to backend domain
6185084Sjohnlev * so, don't keep useful data in the ring
6195084Sjohnlev * used only in frontend driver
6205084Sjohnlev */
6215084Sjohnlev static void
xvdi_reinit_ring(dev_info_t * dip,grant_ref_t * gref,xendev_ring_t * ringp)6225084Sjohnlev xvdi_reinit_ring(dev_info_t *dip, grant_ref_t *gref, xendev_ring_t *ringp)
6235084Sjohnlev {
6245084Sjohnlev paddr_t rpaddr;
6255084Sjohnlev maddr_t rmaddr;
6265084Sjohnlev
6275084Sjohnlev ASSERT((ringp != NULL) && (ringp->xr_paddr != 0));
6285084Sjohnlev rpaddr = ringp->xr_paddr;
6295084Sjohnlev
6305084Sjohnlev rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? rpaddr : pa_to_ma(rpaddr);
6315084Sjohnlev gnttab_grant_foreign_access_ref(ringp->xr_gref, xvdi_get_oeid(dip),
6325084Sjohnlev rmaddr >> PAGESHIFT, 0);
6335084Sjohnlev *gref = ringp->xr_gref;
6345084Sjohnlev
6355084Sjohnlev /* init frontend ring */
6365084Sjohnlev xvdi_ring_init_sring(ringp);
6375084Sjohnlev xvdi_ring_init_front_ring(ringp, ringp->xr_sring.fr.nr_ents,
6385084Sjohnlev ringp->xr_entry_size);
6395084Sjohnlev }
6405084Sjohnlev
6415084Sjohnlev /*
6425084Sjohnlev * allocate Xen inter-domain communications ring for Xen virtual devices
6435084Sjohnlev * used only in frontend driver
6445084Sjohnlev * if *ringpp is not NULL, we'll simply re-init it
6455084Sjohnlev */
6465084Sjohnlev int
xvdi_alloc_ring(dev_info_t * dip,size_t nentry,size_t entrysize,grant_ref_t * gref,xendev_ring_t ** ringpp)6475084Sjohnlev xvdi_alloc_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
6485084Sjohnlev grant_ref_t *gref, xendev_ring_t **ringpp)
6495084Sjohnlev {
6505084Sjohnlev size_t len;
6515084Sjohnlev xendev_ring_t *ring;
6525084Sjohnlev ddi_dma_cookie_t dma_cookie;
6535084Sjohnlev uint_t ncookies;
6545084Sjohnlev grant_ref_t ring_gref;
6555084Sjohnlev domid_t oeid;
6565084Sjohnlev maddr_t rmaddr;
6575084Sjohnlev
6585084Sjohnlev if (*ringpp) {
6595084Sjohnlev xvdi_reinit_ring(dip, gref, *ringpp);
6605084Sjohnlev return (DDI_SUCCESS);
6615084Sjohnlev }
6625084Sjohnlev
6635084Sjohnlev *ringpp = ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
6645084Sjohnlev oeid = xvdi_get_oeid(dip);
6655084Sjohnlev
6665084Sjohnlev /*
6675084Sjohnlev * Allocate page for this ring buffer
6685084Sjohnlev */
6695084Sjohnlev if (ddi_dma_alloc_handle(dip, &xendev_dc_dmaattr, DDI_DMA_SLEEP,
6705084Sjohnlev 0, &ring->xr_dma_hdl) != DDI_SUCCESS)
6715084Sjohnlev goto err;
6725084Sjohnlev
6735084Sjohnlev if (ddi_dma_mem_alloc(ring->xr_dma_hdl, PAGESIZE,
6745084Sjohnlev &xendev_dc_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
6755084Sjohnlev &ring->xr_vaddr, &len, &ring->xr_acc_hdl) != DDI_SUCCESS) {
6765084Sjohnlev ddi_dma_free_handle(&ring->xr_dma_hdl);
6775084Sjohnlev goto err;
6785084Sjohnlev }
6795084Sjohnlev
6805084Sjohnlev if (ddi_dma_addr_bind_handle(ring->xr_dma_hdl, NULL,
6815084Sjohnlev ring->xr_vaddr, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
6825084Sjohnlev DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies) != DDI_DMA_MAPPED) {
6835084Sjohnlev ddi_dma_mem_free(&ring->xr_acc_hdl);
6845084Sjohnlev ring->xr_vaddr = NULL;
6855084Sjohnlev ddi_dma_free_handle(&ring->xr_dma_hdl);
6865084Sjohnlev goto err;
6875084Sjohnlev }
6885084Sjohnlev ASSERT(ncookies == 1);
6895084Sjohnlev ring->xr_paddr = dma_cookie.dmac_laddress;
6905084Sjohnlev rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? ring->xr_paddr :
6915084Sjohnlev pa_to_ma(ring->xr_paddr);
6925084Sjohnlev
6935084Sjohnlev if ((ring_gref = gnttab_grant_foreign_access(oeid,
6945084Sjohnlev rmaddr >> PAGESHIFT, 0)) == (grant_ref_t)-1) {
6955084Sjohnlev (void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
6965084Sjohnlev ddi_dma_mem_free(&ring->xr_acc_hdl);
6975084Sjohnlev ring->xr_vaddr = NULL;
6985084Sjohnlev ddi_dma_free_handle(&ring->xr_dma_hdl);
6995084Sjohnlev goto err;
7005084Sjohnlev }
7015084Sjohnlev *gref = ring->xr_gref = ring_gref;
7025084Sjohnlev
7035084Sjohnlev /* init frontend ring */
7045084Sjohnlev xvdi_ring_init_sring(ring);
7055084Sjohnlev xvdi_ring_init_front_ring(ring, nentry, entrysize);
7065084Sjohnlev
7075084Sjohnlev return (DDI_SUCCESS);
7085084Sjohnlev
7095084Sjohnlev err:
7105084Sjohnlev kmem_free(ring, sizeof (xendev_ring_t));
7115084Sjohnlev return (DDI_FAILURE);
7125084Sjohnlev }
7135084Sjohnlev
7145084Sjohnlev /*
7155084Sjohnlev * Release ring buffers allocated for Xen devices
7165084Sjohnlev * used for frontend driver
7175084Sjohnlev */
7185084Sjohnlev void
xvdi_free_ring(xendev_ring_t * ring)7195084Sjohnlev xvdi_free_ring(xendev_ring_t *ring)
7205084Sjohnlev {
7215084Sjohnlev ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
7225084Sjohnlev
7235084Sjohnlev (void) gnttab_end_foreign_access_ref(ring->xr_gref, 0);
7245084Sjohnlev (void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
7255084Sjohnlev ddi_dma_mem_free(&ring->xr_acc_hdl);
7265084Sjohnlev ddi_dma_free_handle(&ring->xr_dma_hdl);
7275084Sjohnlev kmem_free(ring, sizeof (xendev_ring_t));
7285084Sjohnlev }
7295084Sjohnlev
7305084Sjohnlev dev_info_t *
xvdi_create_dev(dev_info_t * parent,xendev_devclass_t devclass,domid_t dom,int vdev)7315084Sjohnlev xvdi_create_dev(dev_info_t *parent, xendev_devclass_t devclass,
7325084Sjohnlev domid_t dom, int vdev)
7335084Sjohnlev {
7345084Sjohnlev dev_info_t *dip;
7355084Sjohnlev boolean_t backend;
7365084Sjohnlev i_xd_cfg_t *xdcp;
7375084Sjohnlev char xsnamebuf[TYPICALMAXPATHLEN];
7385084Sjohnlev char *type, *node = NULL, *xsname = NULL;
7395084Sjohnlev unsigned int tlen;
7405159Sjohnlev int ret;
7415084Sjohnlev
7425084Sjohnlev ASSERT(DEVI_BUSY_OWNED(parent));
7435084Sjohnlev
7445084Sjohnlev backend = (dom != DOMID_SELF);
7455084Sjohnlev xdcp = i_xvdi_devclass2cfg(devclass);
7465084Sjohnlev ASSERT(xdcp != NULL);
7475084Sjohnlev
7486175Sjohnlev if (vdev != VDEV_NOXS) {
7495084Sjohnlev if (!backend) {
7505084Sjohnlev (void) snprintf(xsnamebuf, sizeof (xsnamebuf),
7515084Sjohnlev "%s/%d", xdcp->xs_path_fe, vdev);
7525084Sjohnlev xsname = xsnamebuf;
7535084Sjohnlev node = xdcp->node_fe;
7545084Sjohnlev } else {
7555084Sjohnlev (void) snprintf(xsnamebuf, sizeof (xsnamebuf),
7565084Sjohnlev "%s/%d/%d", xdcp->xs_path_be, dom, vdev);
7575084Sjohnlev xsname = xsnamebuf;
7585084Sjohnlev node = xdcp->node_be;
7595084Sjohnlev }
7605084Sjohnlev } else {
7615084Sjohnlev node = xdcp->node_fe;
7625084Sjohnlev }
7635084Sjohnlev
7645084Sjohnlev /* Must have a driver to use. */
7655084Sjohnlev if (node == NULL)
7665084Sjohnlev return (NULL);
7675084Sjohnlev
7685084Sjohnlev /*
7695084Sjohnlev * We need to check the state of this device before we go
7705084Sjohnlev * further, otherwise we'll end up with a dead loop if
7715084Sjohnlev * anything goes wrong.
7725084Sjohnlev */
7735084Sjohnlev if ((xsname != NULL) &&
7745084Sjohnlev (xenbus_read_driver_state(xsname) >= XenbusStateClosing))
7755084Sjohnlev return (NULL);
7765084Sjohnlev
7775084Sjohnlev ndi_devi_alloc_sleep(parent, node, DEVI_SID_NODEID, &dip);
7785084Sjohnlev
7795084Sjohnlev /*
7805084Sjohnlev * Driver binding uses the compatible property _before_ the
7815084Sjohnlev * node name, so we set the node name to the 'model' of the
7825084Sjohnlev * device (i.e. 'xnb' or 'xdb') and, if 'type' is present,
7835084Sjohnlev * encode both the model and the type in a compatible property
7845084Sjohnlev * (i.e. 'xnb,netfront' or 'xnb,SUNW_mac'). This allows a
7855084Sjohnlev * driver binding based on the <model,type> pair _before_ a
7865084Sjohnlev * binding based on the node name.
7875084Sjohnlev */
7885084Sjohnlev if ((xsname != NULL) &&
7895084Sjohnlev (xenbus_read(XBT_NULL, xsname, "type", (void *)&type, &tlen)
7905084Sjohnlev == 0)) {
7915084Sjohnlev size_t clen;
7925084Sjohnlev char *c[1];
7935084Sjohnlev
7945084Sjohnlev clen = strlen(node) + strlen(type) + 2;
7955084Sjohnlev c[0] = kmem_alloc(clen, KM_SLEEP);
7965084Sjohnlev (void) snprintf(c[0], clen, "%s,%s", node, type);
7975084Sjohnlev
7985084Sjohnlev (void) ndi_prop_update_string_array(DDI_DEV_T_NONE,
7995084Sjohnlev dip, "compatible", (char **)c, 1);
8005084Sjohnlev
8015084Sjohnlev kmem_free(c[0], clen);
8025084Sjohnlev kmem_free(type, tlen);
8035084Sjohnlev }
8045084Sjohnlev
8055084Sjohnlev (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "devclass", devclass);
8065084Sjohnlev (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "domain", dom);
8075084Sjohnlev (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "vdev", vdev);
8085084Sjohnlev
8095084Sjohnlev if (i_ddi_devi_attached(parent))
8105159Sjohnlev ret = ndi_devi_online(dip, 0);
8115084Sjohnlev else
8125159Sjohnlev ret = ndi_devi_bind_driver(dip, 0);
8135159Sjohnlev if (ret != NDI_SUCCESS)
8145159Sjohnlev (void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
8155084Sjohnlev
8165084Sjohnlev return (dip);
8175084Sjohnlev }
8185084Sjohnlev
8195084Sjohnlev /*
8205084Sjohnlev * xendev_enum_class()
8215084Sjohnlev */
8225084Sjohnlev void
xendev_enum_class(dev_info_t * parent,xendev_devclass_t devclass)8235084Sjohnlev xendev_enum_class(dev_info_t *parent, xendev_devclass_t devclass)
8245084Sjohnlev {
8255910Smrj boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
8265910Smrj boolean_t domU = !dom0;
8275084Sjohnlev i_xd_cfg_t *xdcp;
8285084Sjohnlev
8295084Sjohnlev xdcp = i_xvdi_devclass2cfg(devclass);
8305084Sjohnlev ASSERT(xdcp != NULL);
8315084Sjohnlev
8325910Smrj if (dom0 && !(xdcp->flags & XD_DOM_ZERO))
8335910Smrj return;
8345910Smrj
8355910Smrj if (domU && !(xdcp->flags & XD_DOM_GUEST))
8365910Smrj return;
8375910Smrj
8385084Sjohnlev if (xdcp->xsdev == NULL) {
8395084Sjohnlev int circ;
8405084Sjohnlev
8415084Sjohnlev /*
8425084Sjohnlev * Don't need to probe this kind of device from the
8435084Sjohnlev * store, just create one if it doesn't exist.
8445084Sjohnlev */
8455084Sjohnlev
8465084Sjohnlev ndi_devi_enter(parent, &circ);
8476175Sjohnlev if (xvdi_find_dev(parent, devclass, DOMID_SELF, VDEV_NOXS)
8485084Sjohnlev == NULL)
8495084Sjohnlev (void) xvdi_create_dev(parent, devclass,
8506175Sjohnlev DOMID_SELF, VDEV_NOXS);
8515084Sjohnlev ndi_devi_exit(parent, circ);
8525084Sjohnlev } else {
8535084Sjohnlev /*
8545084Sjohnlev * Probe this kind of device from the store, both
8555084Sjohnlev * frontend and backend.
8565084Sjohnlev */
8577756SMark.Johnson@Sun.COM if (xdcp->node_fe != NULL) {
8587756SMark.Johnson@Sun.COM i_xvdi_enum_fe(parent, xdcp);
8597756SMark.Johnson@Sun.COM }
8607756SMark.Johnson@Sun.COM if (xdcp->node_be != NULL) {
8617756SMark.Johnson@Sun.COM i_xvdi_enum_be(parent, xdcp);
8627756SMark.Johnson@Sun.COM }
8635084Sjohnlev }
8645084Sjohnlev }
8655084Sjohnlev
8665084Sjohnlev /*
8675084Sjohnlev * xendev_enum_all()
8685084Sjohnlev */
8695084Sjohnlev void
xendev_enum_all(dev_info_t * parent,boolean_t store_unavailable)8705084Sjohnlev xendev_enum_all(dev_info_t *parent, boolean_t store_unavailable)
8715084Sjohnlev {
8725084Sjohnlev int i;
8735084Sjohnlev i_xd_cfg_t *xdcp;
8745084Sjohnlev boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
8755084Sjohnlev
8765084Sjohnlev for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
8775084Sjohnlev /*
8785084Sjohnlev * Dom0 relies on watchpoints to create non-soft
8795084Sjohnlev * devices - don't attempt to iterate over the store.
8805084Sjohnlev */
8815084Sjohnlev if (dom0 && (xdcp->xsdev != NULL))
8825084Sjohnlev continue;
8835084Sjohnlev
8845084Sjohnlev /*
8855084Sjohnlev * If the store is not yet available, don't attempt to
8865084Sjohnlev * iterate.
8875084Sjohnlev */
8885084Sjohnlev if (store_unavailable && (xdcp->xsdev != NULL))
8895084Sjohnlev continue;
8905084Sjohnlev
8915084Sjohnlev xendev_enum_class(parent, xdcp->devclass);
8925084Sjohnlev }
8935084Sjohnlev }
8945084Sjohnlev
8955084Sjohnlev xendev_devclass_t
xendev_nodename_to_devclass(char * nodename)8965084Sjohnlev xendev_nodename_to_devclass(char *nodename)
8975084Sjohnlev {
8985084Sjohnlev int i;
8995084Sjohnlev i_xd_cfg_t *xdcp;
9005084Sjohnlev
9015084Sjohnlev /*
9025084Sjohnlev * This relies on the convention that variants of a base
9035084Sjohnlev * driver share the same prefix and that there are no drivers
9045084Sjohnlev * which share a common prefix with the name of any other base
9055084Sjohnlev * drivers.
9065084Sjohnlev *
9075084Sjohnlev * So for a base driver 'xnb' (which is the name listed in
9085084Sjohnlev * xdci) the variants all begin with the string 'xnb' (in fact
9095084Sjohnlev * they are 'xnbe', 'xnbo' and 'xnbu') and there are no other
9105084Sjohnlev * base drivers which have the prefix 'xnb'.
9115084Sjohnlev */
9125084Sjohnlev ASSERT(nodename != NULL);
9135084Sjohnlev for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
9145084Sjohnlev if (((xdcp->node_fe != NULL) &&
9155084Sjohnlev (strncmp(nodename, xdcp->node_fe,
9165084Sjohnlev strlen(xdcp->node_fe)) == 0)) ||
9175084Sjohnlev ((xdcp->node_be != NULL) &&
9185084Sjohnlev (strncmp(nodename, xdcp->node_be,
9195084Sjohnlev strlen(xdcp->node_be)) == 0)))
9205084Sjohnlev
9215084Sjohnlev return (xdcp->devclass);
9225084Sjohnlev }
9235084Sjohnlev return (XEN_INVAL);
9245084Sjohnlev }
9255084Sjohnlev
9265084Sjohnlev int
xendev_devclass_ipl(xendev_devclass_t devclass)9275084Sjohnlev xendev_devclass_ipl(xendev_devclass_t devclass)
9285084Sjohnlev {
9295084Sjohnlev i_xd_cfg_t *xdcp;
9305084Sjohnlev
9315084Sjohnlev xdcp = i_xvdi_devclass2cfg(devclass);
9325084Sjohnlev ASSERT(xdcp != NULL);
9335084Sjohnlev
9345084Sjohnlev return (xdcp->xd_ipl);
9355084Sjohnlev }
9365084Sjohnlev
9375084Sjohnlev /*
9385084Sjohnlev * Determine if a devinfo instance exists of a particular device
9395084Sjohnlev * class, domain and xenstore virtual device number.
9405084Sjohnlev */
9415084Sjohnlev dev_info_t *
xvdi_find_dev(dev_info_t * parent,xendev_devclass_t devclass,domid_t dom,int vdev)9425084Sjohnlev xvdi_find_dev(dev_info_t *parent, xendev_devclass_t devclass,
9435084Sjohnlev domid_t dom, int vdev)
9445084Sjohnlev {
9455084Sjohnlev dev_info_t *dip;
9465084Sjohnlev
9475084Sjohnlev ASSERT(DEVI_BUSY_OWNED(parent));
9485084Sjohnlev
9495084Sjohnlev switch (devclass) {
9505084Sjohnlev case XEN_CONSOLE:
9515084Sjohnlev case XEN_XENBUS:
9525084Sjohnlev case XEN_DOMCAPS:
9535084Sjohnlev case XEN_BALLOON:
9545084Sjohnlev case XEN_EVTCHN:
9555084Sjohnlev case XEN_PRIVCMD:
9565084Sjohnlev /* Console and soft devices have no vdev. */
9576175Sjohnlev vdev = VDEV_NOXS;
9585084Sjohnlev break;
9595084Sjohnlev default:
9605084Sjohnlev break;
9615084Sjohnlev }
9625084Sjohnlev
9635084Sjohnlev for (dip = ddi_get_child(parent); dip != NULL;
9645084Sjohnlev dip = ddi_get_next_sibling(dip)) {
9655084Sjohnlev int *vdevnump, *domidp, *devclsp, vdevnum;
9665084Sjohnlev uint_t ndomid, nvdevnum, ndevcls;
9675084Sjohnlev xendev_devclass_t devcls;
9685084Sjohnlev domid_t domid;
9695084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
9705084Sjohnlev
9715084Sjohnlev if (pdp == NULL) {
9725084Sjohnlev if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
9735084Sjohnlev DDI_PROP_DONTPASS, "domain", &domidp, &ndomid) !=
9745084Sjohnlev DDI_PROP_SUCCESS)
9755084Sjohnlev continue;
9765084Sjohnlev ASSERT(ndomid == 1);
9775084Sjohnlev domid = (domid_t)*domidp;
9785084Sjohnlev ddi_prop_free(domidp);
9795084Sjohnlev
9805084Sjohnlev if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
9815084Sjohnlev DDI_PROP_DONTPASS, "vdev", &vdevnump, &nvdevnum) !=
9825084Sjohnlev DDI_PROP_SUCCESS)
9835084Sjohnlev continue;
9845084Sjohnlev ASSERT(nvdevnum == 1);
9855084Sjohnlev vdevnum = *vdevnump;
9865084Sjohnlev ddi_prop_free(vdevnump);
9875084Sjohnlev
9885084Sjohnlev if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
9895084Sjohnlev DDI_PROP_DONTPASS, "devclass", &devclsp,
9905084Sjohnlev &ndevcls) != DDI_PROP_SUCCESS)
9915084Sjohnlev continue;
9925084Sjohnlev ASSERT(ndevcls == 1);
9935084Sjohnlev devcls = (xendev_devclass_t)*devclsp;
9945084Sjohnlev ddi_prop_free(devclsp);
9955084Sjohnlev } else {
9965084Sjohnlev domid = pdp->xd_domain;
9975084Sjohnlev vdevnum = pdp->xd_vdevnum;
9985084Sjohnlev devcls = pdp->xd_devclass;
9995084Sjohnlev }
10005084Sjohnlev
10015084Sjohnlev if ((domid == dom) && (vdevnum == vdev) && (devcls == devclass))
10025084Sjohnlev return (dip);
10035084Sjohnlev }
10045084Sjohnlev return (NULL);
10055084Sjohnlev }
10065084Sjohnlev
10075084Sjohnlev int
xvdi_get_evtchn(dev_info_t * xdip)10085084Sjohnlev xvdi_get_evtchn(dev_info_t *xdip)
10095084Sjohnlev {
10105084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10115084Sjohnlev
10125084Sjohnlev ASSERT(pdp != NULL);
10135084Sjohnlev return (pdp->xd_evtchn);
10145084Sjohnlev }
10155084Sjohnlev
10165084Sjohnlev int
xvdi_get_vdevnum(dev_info_t * xdip)10175084Sjohnlev xvdi_get_vdevnum(dev_info_t *xdip)
10185084Sjohnlev {
10195084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10205084Sjohnlev
10215084Sjohnlev ASSERT(pdp != NULL);
10225084Sjohnlev return (pdp->xd_vdevnum);
10235084Sjohnlev }
10245084Sjohnlev
10255084Sjohnlev char *
xvdi_get_xsname(dev_info_t * xdip)10265084Sjohnlev xvdi_get_xsname(dev_info_t *xdip)
10275084Sjohnlev {
10285084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10295084Sjohnlev
10305084Sjohnlev ASSERT(pdp != NULL);
10315084Sjohnlev return ((char *)(pdp->xd_xsdev.nodename));
10325084Sjohnlev }
10335084Sjohnlev
10345084Sjohnlev char *
xvdi_get_oename(dev_info_t * xdip)10355084Sjohnlev xvdi_get_oename(dev_info_t *xdip)
10365084Sjohnlev {
10375084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10385084Sjohnlev
10395084Sjohnlev ASSERT(pdp != NULL);
10405084Sjohnlev if (pdp->xd_devclass == XEN_CONSOLE)
10415084Sjohnlev return (NULL);
10425084Sjohnlev return ((char *)(pdp->xd_xsdev.otherend));
10435084Sjohnlev }
10445084Sjohnlev
10455084Sjohnlev struct xenbus_device *
xvdi_get_xsd(dev_info_t * xdip)10465084Sjohnlev xvdi_get_xsd(dev_info_t *xdip)
10475084Sjohnlev {
10485084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10495084Sjohnlev
10505084Sjohnlev ASSERT(pdp != NULL);
10515084Sjohnlev return (&pdp->xd_xsdev);
10525084Sjohnlev }
10535084Sjohnlev
10545084Sjohnlev domid_t
xvdi_get_oeid(dev_info_t * xdip)10555084Sjohnlev xvdi_get_oeid(dev_info_t *xdip)
10565084Sjohnlev {
10575084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10585084Sjohnlev
10595084Sjohnlev ASSERT(pdp != NULL);
10605084Sjohnlev if (pdp->xd_devclass == XEN_CONSOLE)
10615084Sjohnlev return ((domid_t)-1);
10625084Sjohnlev return ((domid_t)(pdp->xd_xsdev.otherend_id));
10635084Sjohnlev }
10645084Sjohnlev
10655084Sjohnlev void
xvdi_dev_error(dev_info_t * dip,int errno,char * errstr)10665084Sjohnlev xvdi_dev_error(dev_info_t *dip, int errno, char *errstr)
10675084Sjohnlev {
10685084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
10695084Sjohnlev
10705084Sjohnlev ASSERT(pdp != NULL);
10715084Sjohnlev xenbus_dev_error(&pdp->xd_xsdev, errno, errstr);
10725084Sjohnlev }
10735084Sjohnlev
10745084Sjohnlev void
xvdi_fatal_error(dev_info_t * dip,int errno,char * errstr)10755084Sjohnlev xvdi_fatal_error(dev_info_t *dip, int errno, char *errstr)
10765084Sjohnlev {
10775084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
10785084Sjohnlev
10795084Sjohnlev ASSERT(pdp != NULL);
10805084Sjohnlev xenbus_dev_fatal(&pdp->xd_xsdev, errno, errstr);
10815084Sjohnlev }
10825084Sjohnlev
10835084Sjohnlev static void
i_xvdi_oestate_handler(void * arg)10845084Sjohnlev i_xvdi_oestate_handler(void *arg)
10855084Sjohnlev {
10867679SMax.Zhen@Sun.COM i_oestate_evt_t *evt = (i_oestate_evt_t *)arg;
10877679SMax.Zhen@Sun.COM dev_info_t *dip = evt->dip;
10885084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
10895084Sjohnlev XenbusState oestate = pdp->xd_xsdev.otherend_state;
10907679SMax.Zhen@Sun.COM XenbusState curr_oestate = evt->state;
10915084Sjohnlev ddi_eventcookie_t evc;
10925084Sjohnlev
10937679SMax.Zhen@Sun.COM /* evt is alloc'ed in i_xvdi_oestate_cb */
10947679SMax.Zhen@Sun.COM kmem_free(evt, sizeof (i_oestate_evt_t));
10957679SMax.Zhen@Sun.COM
10967679SMax.Zhen@Sun.COM /*
10977679SMax.Zhen@Sun.COM * If the oestate we're handling is not the latest one,
10987679SMax.Zhen@Sun.COM * it does not make any sense to continue handling it.
10997679SMax.Zhen@Sun.COM */
11007679SMax.Zhen@Sun.COM if (curr_oestate != oestate)
11017679SMax.Zhen@Sun.COM return;
11027679SMax.Zhen@Sun.COM
11037756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
11045084Sjohnlev
11055084Sjohnlev if (pdp->xd_oe_ehid != NULL) {
11065084Sjohnlev /* send notification to driver */
11075084Sjohnlev if (ddi_get_eventcookie(dip, XS_OE_STATE,
11085084Sjohnlev &evc) == DDI_SUCCESS) {
11097756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
11105084Sjohnlev (void) ndi_post_event(dip, dip, evc, &oestate);
11117756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
11125084Sjohnlev }
11135084Sjohnlev } else {
11145084Sjohnlev /*
11155084Sjohnlev * take default action, if driver hasn't registered its
11165084Sjohnlev * event handler yet
11175084Sjohnlev */
11185084Sjohnlev if (oestate == XenbusStateClosing) {
11195084Sjohnlev (void) xvdi_switch_state(dip, XBT_NULL,
11205084Sjohnlev XenbusStateClosed);
11215084Sjohnlev } else if (oestate == XenbusStateClosed) {
11225084Sjohnlev (void) xvdi_switch_state(dip, XBT_NULL,
11235084Sjohnlev XenbusStateClosed);
11245084Sjohnlev (void) xvdi_post_event(dip, XEN_HP_REMOVE);
11255084Sjohnlev }
11265084Sjohnlev }
11275084Sjohnlev
11287756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
11295084Sjohnlev
11305084Sjohnlev /*
11315084Sjohnlev * We'll try to remove the devinfo node of this device if the
11325084Sjohnlev * other end has closed.
11335084Sjohnlev */
11345084Sjohnlev if (oestate == XenbusStateClosed)
11355084Sjohnlev (void) ddi_taskq_dispatch(DEVI(ddi_get_parent(dip))->devi_taskq,
11365084Sjohnlev xendev_offline_device, dip, DDI_SLEEP);
11375084Sjohnlev }
11385084Sjohnlev
11395084Sjohnlev static void
i_xvdi_hpstate_handler(void * arg)11405084Sjohnlev i_xvdi_hpstate_handler(void *arg)
11415084Sjohnlev {
11425084Sjohnlev dev_info_t *dip = (dev_info_t *)arg;
11435084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
11445084Sjohnlev ddi_eventcookie_t evc;
11455084Sjohnlev char *hp_status;
11465084Sjohnlev unsigned int hpl;
11475084Sjohnlev
11487756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
11495084Sjohnlev if ((ddi_get_eventcookie(dip, XS_HP_STATE, &evc) == DDI_SUCCESS) &&
11505084Sjohnlev (xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
11515084Sjohnlev (void *)&hp_status, &hpl) == 0)) {
11525084Sjohnlev
11535084Sjohnlev xendev_hotplug_state_t new_state = Unrecognized;
11545084Sjohnlev
11555084Sjohnlev if (strcmp(hp_status, "connected") == 0)
11565084Sjohnlev new_state = Connected;
11575084Sjohnlev
11587756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
11595084Sjohnlev
11605084Sjohnlev (void) ndi_post_event(dip, dip, evc, &new_state);
11615084Sjohnlev kmem_free(hp_status, hpl);
11625084Sjohnlev return;
11635084Sjohnlev }
11647756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
11655084Sjohnlev }
11665084Sjohnlev
11675084Sjohnlev void
xvdi_notify_oe(dev_info_t * dip)11685084Sjohnlev xvdi_notify_oe(dev_info_t *dip)
11695084Sjohnlev {
11705084Sjohnlev struct xendev_ppd *pdp;
11715084Sjohnlev
11725084Sjohnlev pdp = ddi_get_parent_data(dip);
11735084Sjohnlev ASSERT(pdp->xd_evtchn != INVALID_EVTCHN);
11745084Sjohnlev ec_notify_via_evtchn(pdp->xd_evtchn);
11755084Sjohnlev }
11765084Sjohnlev
11775084Sjohnlev static void
i_xvdi_bepath_cb(struct xenbus_watch * w,const char ** vec,unsigned int len)11785084Sjohnlev i_xvdi_bepath_cb(struct xenbus_watch *w, const char **vec, unsigned int len)
11795084Sjohnlev {
11805084Sjohnlev dev_info_t *dip = (dev_info_t *)w->dev;
11815084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
11825084Sjohnlev char *be = NULL;
11835084Sjohnlev unsigned int bel;
11845084Sjohnlev
11855084Sjohnlev ASSERT(len > XS_WATCH_PATH);
11865084Sjohnlev ASSERT(vec[XS_WATCH_PATH] != NULL);
11875084Sjohnlev
11885084Sjohnlev /*
11895084Sjohnlev * If the backend is not the same as that we already stored,
11905084Sjohnlev * re-set our watch for its' state.
11915084Sjohnlev */
11925084Sjohnlev if ((xenbus_read(XBT_NULL, "", vec[XS_WATCH_PATH], (void *)be, &bel)
11935084Sjohnlev == 0) && (strcmp(be, pdp->xd_xsdev.otherend) != 0))
11945084Sjohnlev (void) i_xvdi_add_watch_oestate(dip);
11955084Sjohnlev
11965084Sjohnlev if (be != NULL) {
11975084Sjohnlev ASSERT(bel > 0);
11985084Sjohnlev kmem_free(be, bel);
11995084Sjohnlev }
12005084Sjohnlev }
12015084Sjohnlev
12028863SEdward.Pilatowicz@Sun.COM static void
i_xvdi_xb_watch_free(xd_xb_watches_t * xxwp)12038863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_free(xd_xb_watches_t *xxwp)
12048863SEdward.Pilatowicz@Sun.COM {
12058863SEdward.Pilatowicz@Sun.COM ASSERT(xxwp->xxw_ref == 0);
12068863SEdward.Pilatowicz@Sun.COM strfree((char *)xxwp->xxw_watch.node);
12078863SEdward.Pilatowicz@Sun.COM kmem_free(xxwp, sizeof (*xxwp));
12088863SEdward.Pilatowicz@Sun.COM }
12098863SEdward.Pilatowicz@Sun.COM
12108863SEdward.Pilatowicz@Sun.COM static void
i_xvdi_xb_watch_release(xd_xb_watches_t * xxwp)12118863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xd_xb_watches_t *xxwp)
12128863SEdward.Pilatowicz@Sun.COM {
12138863SEdward.Pilatowicz@Sun.COM ASSERT(MUTEX_HELD(&xxwp->xxw_xppd->xd_ndi_lk));
12148863SEdward.Pilatowicz@Sun.COM ASSERT(xxwp->xxw_ref > 0);
12158863SEdward.Pilatowicz@Sun.COM if (--xxwp->xxw_ref == 0)
12168863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_free(xxwp);
12178863SEdward.Pilatowicz@Sun.COM }
12188863SEdward.Pilatowicz@Sun.COM
12198863SEdward.Pilatowicz@Sun.COM static void
i_xvdi_xb_watch_hold(xd_xb_watches_t * xxwp)12208863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_hold(xd_xb_watches_t *xxwp)
12218863SEdward.Pilatowicz@Sun.COM {
12228863SEdward.Pilatowicz@Sun.COM ASSERT(MUTEX_HELD(&xxwp->xxw_xppd->xd_ndi_lk));
12238863SEdward.Pilatowicz@Sun.COM ASSERT(xxwp->xxw_ref > 0);
12248863SEdward.Pilatowicz@Sun.COM xxwp->xxw_ref++;
12258863SEdward.Pilatowicz@Sun.COM }
12268863SEdward.Pilatowicz@Sun.COM
12278863SEdward.Pilatowicz@Sun.COM static void
i_xvdi_xb_watch_cb_tq(void * arg)12288863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_cb_tq(void *arg)
12298863SEdward.Pilatowicz@Sun.COM {
12308863SEdward.Pilatowicz@Sun.COM xd_xb_watches_t *xxwp = (xd_xb_watches_t *)arg;
12318863SEdward.Pilatowicz@Sun.COM dev_info_t *dip = (dev_info_t *)xxwp->xxw_watch.dev;
12328863SEdward.Pilatowicz@Sun.COM struct xendev_ppd *pdp = xxwp->xxw_xppd;
12338863SEdward.Pilatowicz@Sun.COM
12348863SEdward.Pilatowicz@Sun.COM xxwp->xxw_cb(dip, xxwp->xxw_watch.node, xxwp->xxw_arg);
12358863SEdward.Pilatowicz@Sun.COM
12368863SEdward.Pilatowicz@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
12378863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxwp);
12388863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
12398863SEdward.Pilatowicz@Sun.COM }
12408863SEdward.Pilatowicz@Sun.COM
12418863SEdward.Pilatowicz@Sun.COM static void
i_xvdi_xb_watch_cb(struct xenbus_watch * w,const char ** vec,unsigned int len)12428863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_cb(struct xenbus_watch *w, const char **vec, unsigned int len)
12438863SEdward.Pilatowicz@Sun.COM {
12448863SEdward.Pilatowicz@Sun.COM dev_info_t *dip = (dev_info_t *)w->dev;
12458863SEdward.Pilatowicz@Sun.COM struct xendev_ppd *pdp = ddi_get_parent_data(dip);
12468863SEdward.Pilatowicz@Sun.COM xd_xb_watches_t *xxwp;
12478863SEdward.Pilatowicz@Sun.COM
12488863SEdward.Pilatowicz@Sun.COM ASSERT(len > XS_WATCH_PATH);
12498863SEdward.Pilatowicz@Sun.COM ASSERT(vec[XS_WATCH_PATH] != NULL);
12508863SEdward.Pilatowicz@Sun.COM
12518863SEdward.Pilatowicz@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
12528863SEdward.Pilatowicz@Sun.COM for (xxwp = list_head(&pdp->xd_xb_watches); xxwp != NULL;
12538863SEdward.Pilatowicz@Sun.COM xxwp = list_next(&pdp->xd_xb_watches, xxwp)) {
12548863SEdward.Pilatowicz@Sun.COM if (w == &xxwp->xxw_watch)
12558863SEdward.Pilatowicz@Sun.COM break;
12568863SEdward.Pilatowicz@Sun.COM }
12578863SEdward.Pilatowicz@Sun.COM
12588863SEdward.Pilatowicz@Sun.COM if (xxwp == NULL) {
12598863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
12608863SEdward.Pilatowicz@Sun.COM return;
12618863SEdward.Pilatowicz@Sun.COM }
12628863SEdward.Pilatowicz@Sun.COM
12638863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_hold(xxwp);
12648863SEdward.Pilatowicz@Sun.COM (void) ddi_taskq_dispatch(pdp->xd_xb_watch_taskq,
12658863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_cb_tq, xxwp, DDI_SLEEP);
12668863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
12678863SEdward.Pilatowicz@Sun.COM }
12688863SEdward.Pilatowicz@Sun.COM
12698863SEdward.Pilatowicz@Sun.COM /*
12708863SEdward.Pilatowicz@Sun.COM * Any watches registered with xvdi_add_xb_watch_handler() get torn down during
12718863SEdward.Pilatowicz@Sun.COM * a suspend operation. So if a frontend driver want's to use these interfaces,
12728863SEdward.Pilatowicz@Sun.COM * that driver is responsible for re-registering any watches it had before
12738863SEdward.Pilatowicz@Sun.COM * the suspend operation.
12748863SEdward.Pilatowicz@Sun.COM */
12758863SEdward.Pilatowicz@Sun.COM int
xvdi_add_xb_watch_handler(dev_info_t * dip,const char * dir,const char * node,xvdi_xb_watch_cb_t cb,void * arg)12768863SEdward.Pilatowicz@Sun.COM xvdi_add_xb_watch_handler(dev_info_t *dip, const char *dir, const char *node,
12778863SEdward.Pilatowicz@Sun.COM xvdi_xb_watch_cb_t cb, void *arg)
12788863SEdward.Pilatowicz@Sun.COM {
12798863SEdward.Pilatowicz@Sun.COM struct xendev_ppd *pdp = ddi_get_parent_data(dip);
12808863SEdward.Pilatowicz@Sun.COM xd_xb_watches_t *xxw_new, *xxwp;
12818863SEdward.Pilatowicz@Sun.COM char *path;
12828863SEdward.Pilatowicz@Sun.COM int n;
12838863SEdward.Pilatowicz@Sun.COM
12848863SEdward.Pilatowicz@Sun.COM ASSERT((dip != NULL) && (dir != NULL) && (node != NULL));
12858863SEdward.Pilatowicz@Sun.COM ASSERT(cb != NULL);
12868863SEdward.Pilatowicz@Sun.COM
12878863SEdward.Pilatowicz@Sun.COM n = strlen(dir) + 1 + strlen(node) + 1;
12888863SEdward.Pilatowicz@Sun.COM path = kmem_zalloc(n, KM_SLEEP);
12898863SEdward.Pilatowicz@Sun.COM (void) strlcat(path, dir, n);
12908863SEdward.Pilatowicz@Sun.COM (void) strlcat(path, "/", n);
12918863SEdward.Pilatowicz@Sun.COM (void) strlcat(path, node, n);
12928863SEdward.Pilatowicz@Sun.COM ASSERT((strlen(path) + 1) == n);
12938863SEdward.Pilatowicz@Sun.COM
12948863SEdward.Pilatowicz@Sun.COM xxw_new = kmem_zalloc(sizeof (*xxw_new), KM_SLEEP);
12958863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_ref = 1;
12968863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_watch.node = path;
12978863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_watch.callback = i_xvdi_xb_watch_cb;
12988863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_watch.dev = (struct xenbus_device *)dip;
12998863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_xppd = pdp;
13008863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_cb = cb;
13018863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_arg = arg;
13028863SEdward.Pilatowicz@Sun.COM
13038863SEdward.Pilatowicz@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
13048863SEdward.Pilatowicz@Sun.COM
13058863SEdward.Pilatowicz@Sun.COM /*
13068863SEdward.Pilatowicz@Sun.COM * If this is the first watch we're setting up, create a taskq
13078863SEdward.Pilatowicz@Sun.COM * to dispatch watch events and initialize the watch list.
13088863SEdward.Pilatowicz@Sun.COM */
13098863SEdward.Pilatowicz@Sun.COM if (pdp->xd_xb_watch_taskq == NULL) {
13108863SEdward.Pilatowicz@Sun.COM char tq_name[TASKQ_NAMELEN];
13118863SEdward.Pilatowicz@Sun.COM
13128863SEdward.Pilatowicz@Sun.COM ASSERT(list_is_empty(&pdp->xd_xb_watches));
13138863SEdward.Pilatowicz@Sun.COM
13148863SEdward.Pilatowicz@Sun.COM (void) snprintf(tq_name, sizeof (tq_name),
13158863SEdward.Pilatowicz@Sun.COM "%s_xb_watch_tq", ddi_get_name(dip));
13168863SEdward.Pilatowicz@Sun.COM
13178863SEdward.Pilatowicz@Sun.COM if ((pdp->xd_xb_watch_taskq = ddi_taskq_create(dip, tq_name,
13188863SEdward.Pilatowicz@Sun.COM 1, TASKQ_DEFAULTPRI, 0)) == NULL) {
13198863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxw_new);
13208863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
13218863SEdward.Pilatowicz@Sun.COM return (DDI_FAILURE);
13228863SEdward.Pilatowicz@Sun.COM }
13238863SEdward.Pilatowicz@Sun.COM }
13248863SEdward.Pilatowicz@Sun.COM
13258863SEdward.Pilatowicz@Sun.COM /* Don't allow duplicate watches to be registered */
13268863SEdward.Pilatowicz@Sun.COM for (xxwp = list_head(&pdp->xd_xb_watches); xxwp != NULL;
13278863SEdward.Pilatowicz@Sun.COM xxwp = list_next(&pdp->xd_xb_watches, xxwp)) {
13288863SEdward.Pilatowicz@Sun.COM
13298863SEdward.Pilatowicz@Sun.COM ASSERT(strcmp(xxwp->xxw_watch.node, path) != 0);
13308863SEdward.Pilatowicz@Sun.COM if (strcmp(xxwp->xxw_watch.node, path) != 0)
13318863SEdward.Pilatowicz@Sun.COM continue;
13328863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxw_new);
13338863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
13348863SEdward.Pilatowicz@Sun.COM return (DDI_FAILURE);
13358863SEdward.Pilatowicz@Sun.COM }
13368863SEdward.Pilatowicz@Sun.COM
13378863SEdward.Pilatowicz@Sun.COM if (register_xenbus_watch(&xxw_new->xxw_watch) != 0) {
13388863SEdward.Pilatowicz@Sun.COM if (list_is_empty(&pdp->xd_xb_watches)) {
13398863SEdward.Pilatowicz@Sun.COM ddi_taskq_destroy(pdp->xd_xb_watch_taskq);
13408863SEdward.Pilatowicz@Sun.COM pdp->xd_xb_watch_taskq = NULL;
13418863SEdward.Pilatowicz@Sun.COM }
13428863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxw_new);
13438863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
13448863SEdward.Pilatowicz@Sun.COM return (DDI_FAILURE);
13458863SEdward.Pilatowicz@Sun.COM }
13468863SEdward.Pilatowicz@Sun.COM
13478863SEdward.Pilatowicz@Sun.COM list_insert_head(&pdp->xd_xb_watches, xxw_new);
13488863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
13498863SEdward.Pilatowicz@Sun.COM return (DDI_SUCCESS);
13508863SEdward.Pilatowicz@Sun.COM }
13518863SEdward.Pilatowicz@Sun.COM
13528863SEdward.Pilatowicz@Sun.COM /*
13538863SEdward.Pilatowicz@Sun.COM * Tear down all xenbus watches registered by the specified dip.
13548863SEdward.Pilatowicz@Sun.COM */
13558863SEdward.Pilatowicz@Sun.COM void
xvdi_remove_xb_watch_handlers(dev_info_t * dip)13568863SEdward.Pilatowicz@Sun.COM xvdi_remove_xb_watch_handlers(dev_info_t *dip)
13578863SEdward.Pilatowicz@Sun.COM {
13588863SEdward.Pilatowicz@Sun.COM struct xendev_ppd *pdp = ddi_get_parent_data(dip);
13598863SEdward.Pilatowicz@Sun.COM xd_xb_watches_t *xxwp;
13608863SEdward.Pilatowicz@Sun.COM ddi_taskq_t *tq;
13618863SEdward.Pilatowicz@Sun.COM
13628863SEdward.Pilatowicz@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
13638863SEdward.Pilatowicz@Sun.COM
13648863SEdward.Pilatowicz@Sun.COM while ((xxwp = list_remove_head(&pdp->xd_xb_watches)) != NULL) {
1365*9243SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
13668863SEdward.Pilatowicz@Sun.COM unregister_xenbus_watch(&xxwp->xxw_watch);
1367*9243SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
13688863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxwp);
13698863SEdward.Pilatowicz@Sun.COM }
13708863SEdward.Pilatowicz@Sun.COM ASSERT(list_is_empty(&pdp->xd_xb_watches));
13718863SEdward.Pilatowicz@Sun.COM
13728863SEdward.Pilatowicz@Sun.COM /*
13738863SEdward.Pilatowicz@Sun.COM * We can't hold xd_ndi_lk while we destroy the xd_xb_watch_taskq.
13748863SEdward.Pilatowicz@Sun.COM * This is because if there are currently any executing taskq threads,
13758863SEdward.Pilatowicz@Sun.COM * we will block until they are finished, and to finish they need
13768863SEdward.Pilatowicz@Sun.COM * to aquire xd_ndi_lk in i_xvdi_xb_watch_cb_tq() so they can release
13778863SEdward.Pilatowicz@Sun.COM * their reference on their corresponding xxwp structure.
13788863SEdward.Pilatowicz@Sun.COM */
13798863SEdward.Pilatowicz@Sun.COM tq = pdp->xd_xb_watch_taskq;
13808863SEdward.Pilatowicz@Sun.COM pdp->xd_xb_watch_taskq = NULL;
13818863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
13828863SEdward.Pilatowicz@Sun.COM if (tq != NULL)
13838863SEdward.Pilatowicz@Sun.COM ddi_taskq_destroy(tq);
13848863SEdward.Pilatowicz@Sun.COM }
13858863SEdward.Pilatowicz@Sun.COM
13865084Sjohnlev static int
i_xvdi_add_watch_oestate(dev_info_t * dip)13875084Sjohnlev i_xvdi_add_watch_oestate(dev_info_t *dip)
13885084Sjohnlev {
13895084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
13905084Sjohnlev
13915084Sjohnlev ASSERT(pdp != NULL);
13925084Sjohnlev ASSERT(pdp->xd_xsdev.nodename != NULL);
13937756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk));
13945084Sjohnlev
13955084Sjohnlev /*
13965084Sjohnlev * Create taskq for delivering other end state change event to
13975084Sjohnlev * this device later.
13985084Sjohnlev *
13995084Sjohnlev * Set nthreads to 1 to make sure that events can be delivered
14005084Sjohnlev * in order.
14015084Sjohnlev *
14025084Sjohnlev * Note: It is _not_ guaranteed that driver can see every
14035084Sjohnlev * xenstore change under the path that it is watching. If two
14045084Sjohnlev * changes happen consecutively in a very short amount of
14055084Sjohnlev * time, it is likely that the driver will see only the last
14065084Sjohnlev * one.
14075084Sjohnlev */
14085084Sjohnlev if (pdp->xd_oe_taskq == NULL)
14095084Sjohnlev if ((pdp->xd_oe_taskq = ddi_taskq_create(dip,
14105084Sjohnlev "xendev_oe_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
14115084Sjohnlev return (DDI_FAILURE);
14125084Sjohnlev
14135084Sjohnlev /*
14145084Sjohnlev * Watch for changes to the XenbusState of otherend.
14155084Sjohnlev */
14165084Sjohnlev pdp->xd_xsdev.otherend_state = XenbusStateUnknown;
14175084Sjohnlev pdp->xd_xsdev.otherend_changed = i_xvdi_oestate_cb;
14185084Sjohnlev
14195084Sjohnlev if (talk_to_otherend(&pdp->xd_xsdev) != 0) {
14205084Sjohnlev i_xvdi_rem_watch_oestate(dip);
14215084Sjohnlev return (DDI_FAILURE);
14225084Sjohnlev }
14235084Sjohnlev
14245084Sjohnlev return (DDI_SUCCESS);
14255084Sjohnlev }
14265084Sjohnlev
14275084Sjohnlev static void
i_xvdi_rem_watch_oestate(dev_info_t * dip)14285084Sjohnlev i_xvdi_rem_watch_oestate(dev_info_t *dip)
14295084Sjohnlev {
14305084Sjohnlev struct xendev_ppd *pdp;
14315084Sjohnlev struct xenbus_device *dev;
14325084Sjohnlev
14335084Sjohnlev pdp = ddi_get_parent_data(dip);
14345084Sjohnlev ASSERT(pdp != NULL);
14357756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk));
14365084Sjohnlev
14375084Sjohnlev dev = &pdp->xd_xsdev;
14385084Sjohnlev
14395084Sjohnlev /* Unwatch for changes to XenbusState of otherend */
14405084Sjohnlev if (dev->otherend_watch.node != NULL) {
14417756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
14425084Sjohnlev unregister_xenbus_watch(&dev->otherend_watch);
14437756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
14445084Sjohnlev }
14455084Sjohnlev
14465084Sjohnlev /* make sure no event handler is running */
14475084Sjohnlev if (pdp->xd_oe_taskq != NULL) {
14487756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
14495084Sjohnlev ddi_taskq_destroy(pdp->xd_oe_taskq);
14507756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
14515084Sjohnlev pdp->xd_oe_taskq = NULL;
14525084Sjohnlev }
14535084Sjohnlev
14545084Sjohnlev /* clean up */
14555084Sjohnlev dev->otherend_state = XenbusStateUnknown;
14565084Sjohnlev dev->otherend_id = (domid_t)-1;
14575084Sjohnlev if (dev->otherend_watch.node != NULL)
14585084Sjohnlev kmem_free((void *)dev->otherend_watch.node,
14595084Sjohnlev strlen(dev->otherend_watch.node) + 1);
14605084Sjohnlev dev->otherend_watch.node = NULL;
14615084Sjohnlev if (dev->otherend != NULL)
14625084Sjohnlev kmem_free((void *)dev->otherend, strlen(dev->otherend) + 1);
14635084Sjohnlev dev->otherend = NULL;
14645084Sjohnlev }
14655084Sjohnlev
14665084Sjohnlev static int
i_xvdi_add_watch_hpstate(dev_info_t * dip)14675084Sjohnlev i_xvdi_add_watch_hpstate(dev_info_t *dip)
14685084Sjohnlev {
14695084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
14705084Sjohnlev
14715084Sjohnlev ASSERT(pdp != NULL);
14725084Sjohnlev ASSERT(pdp->xd_xsdev.frontend == 0);
14737756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk));
14745084Sjohnlev
14755084Sjohnlev /*
14765084Sjohnlev * Create taskq for delivering hotplug status change event to
14775084Sjohnlev * this device later.
14785084Sjohnlev *
14795084Sjohnlev * Set nthreads to 1 to make sure that events can be delivered
14805084Sjohnlev * in order.
14815084Sjohnlev *
14825084Sjohnlev * Note: It is _not_ guaranteed that driver can see every
14835084Sjohnlev * hotplug status change under the path that it is
14845084Sjohnlev * watching. If two changes happen consecutively in a very
14855084Sjohnlev * short amount of time, it is likely that the driver only
14865084Sjohnlev * sees the last one.
14875084Sjohnlev */
14885084Sjohnlev if (pdp->xd_hp_taskq == NULL)
14895084Sjohnlev if ((pdp->xd_hp_taskq = ddi_taskq_create(dip,
14905084Sjohnlev "xendev_hp_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
14915084Sjohnlev return (DDI_FAILURE);
14925084Sjohnlev
14935084Sjohnlev if (pdp->xd_hp_watch.node == NULL) {
14945084Sjohnlev size_t len;
14955084Sjohnlev char *path;
14965084Sjohnlev
14975084Sjohnlev ASSERT(pdp->xd_xsdev.nodename != NULL);
14985084Sjohnlev
14995084Sjohnlev len = strlen(pdp->xd_xsdev.nodename) +
15005084Sjohnlev strlen("/hotplug-status") + 1;
15015084Sjohnlev path = kmem_alloc(len, KM_SLEEP);
15025084Sjohnlev (void) snprintf(path, len, "%s/hotplug-status",
15035084Sjohnlev pdp->xd_xsdev.nodename);
15045084Sjohnlev
15055084Sjohnlev pdp->xd_hp_watch.node = path;
15065084Sjohnlev pdp->xd_hp_watch.callback = i_xvdi_hpstate_cb;
15075084Sjohnlev pdp->xd_hp_watch.dev = (struct xenbus_device *)dip; /* yuck! */
15085084Sjohnlev if (register_xenbus_watch(&pdp->xd_hp_watch) != 0) {
15095084Sjohnlev i_xvdi_rem_watch_hpstate(dip);
15105084Sjohnlev return (DDI_FAILURE);
15115084Sjohnlev }
15125084Sjohnlev }
15135084Sjohnlev
15145084Sjohnlev return (DDI_SUCCESS);
15155084Sjohnlev }
15165084Sjohnlev
15175084Sjohnlev static void
i_xvdi_rem_watch_hpstate(dev_info_t * dip)15185084Sjohnlev i_xvdi_rem_watch_hpstate(dev_info_t *dip)
15195084Sjohnlev {
15205084Sjohnlev struct xendev_ppd *pdp;
15215084Sjohnlev pdp = ddi_get_parent_data(dip);
15225084Sjohnlev
15235084Sjohnlev ASSERT(pdp != NULL);
15245084Sjohnlev ASSERT(pdp->xd_xsdev.frontend == 0);
15257756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk));
15265084Sjohnlev
15275084Sjohnlev /* Unwatch for changes to "hotplug-status" node for backend device. */
15285084Sjohnlev if (pdp->xd_hp_watch.node != NULL) {
15297756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
15305084Sjohnlev unregister_xenbus_watch(&pdp->xd_hp_watch);
15317756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
15325084Sjohnlev }
15335084Sjohnlev
15345084Sjohnlev /* Make sure no event handler is running. */
15355084Sjohnlev if (pdp->xd_hp_taskq != NULL) {
15367756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
15375084Sjohnlev ddi_taskq_destroy(pdp->xd_hp_taskq);
15387756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
15395084Sjohnlev pdp->xd_hp_taskq = NULL;
15405084Sjohnlev }
15415084Sjohnlev
15425084Sjohnlev /* Clean up. */
15435084Sjohnlev if (pdp->xd_hp_watch.node != NULL) {
15445084Sjohnlev kmem_free((void *)pdp->xd_hp_watch.node,
15455084Sjohnlev strlen(pdp->xd_hp_watch.node) + 1);
15465084Sjohnlev pdp->xd_hp_watch.node = NULL;
15475084Sjohnlev }
15485084Sjohnlev }
15495084Sjohnlev
15505084Sjohnlev static int
i_xvdi_add_watches(dev_info_t * dip)15515084Sjohnlev i_xvdi_add_watches(dev_info_t *dip)
15525084Sjohnlev {
15535084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
15545084Sjohnlev
15555084Sjohnlev ASSERT(pdp != NULL);
15565084Sjohnlev
15577756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
15585084Sjohnlev
15595084Sjohnlev if (i_xvdi_add_watch_oestate(dip) != DDI_SUCCESS) {
15607756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
15615084Sjohnlev return (DDI_FAILURE);
15625084Sjohnlev }
15635084Sjohnlev
15645084Sjohnlev if (pdp->xd_xsdev.frontend == 1) {
15655084Sjohnlev /*
15665084Sjohnlev * Frontend devices must watch for the backend path
15675084Sjohnlev * changing.
15685084Sjohnlev */
15695084Sjohnlev if (i_xvdi_add_watch_bepath(dip) != DDI_SUCCESS)
15705084Sjohnlev goto unwatch_and_fail;
15715084Sjohnlev } else {
15725084Sjohnlev /*
15735084Sjohnlev * Backend devices must watch for hotplug events.
15745084Sjohnlev */
15755084Sjohnlev if (i_xvdi_add_watch_hpstate(dip) != DDI_SUCCESS)
15765084Sjohnlev goto unwatch_and_fail;
15775084Sjohnlev }
15785084Sjohnlev
15797756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
15805084Sjohnlev
15815084Sjohnlev return (DDI_SUCCESS);
15825084Sjohnlev
15835084Sjohnlev unwatch_and_fail:
15845084Sjohnlev i_xvdi_rem_watch_oestate(dip);
15857756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
15865084Sjohnlev
15875084Sjohnlev return (DDI_FAILURE);
15885084Sjohnlev }
15895084Sjohnlev
15905084Sjohnlev static void
i_xvdi_rem_watches(dev_info_t * dip)15915084Sjohnlev i_xvdi_rem_watches(dev_info_t *dip)
15925084Sjohnlev {
15935084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
15945084Sjohnlev
15955084Sjohnlev ASSERT(pdp != NULL);
15965084Sjohnlev
15977756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
15985084Sjohnlev
15995084Sjohnlev i_xvdi_rem_watch_oestate(dip);
16005084Sjohnlev
16015084Sjohnlev if (pdp->xd_xsdev.frontend == 1)
16025084Sjohnlev i_xvdi_rem_watch_bepath(dip);
16035084Sjohnlev else
16045084Sjohnlev i_xvdi_rem_watch_hpstate(dip);
16055084Sjohnlev
16067756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
16078863SEdward.Pilatowicz@Sun.COM
16088863SEdward.Pilatowicz@Sun.COM xvdi_remove_xb_watch_handlers(dip);
16095084Sjohnlev }
16105084Sjohnlev
16115084Sjohnlev static int
i_xvdi_add_watch_bepath(dev_info_t * dip)16125084Sjohnlev i_xvdi_add_watch_bepath(dev_info_t *dip)
16135084Sjohnlev {
16145084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
16155084Sjohnlev
16165084Sjohnlev ASSERT(pdp != NULL);
16175084Sjohnlev ASSERT(pdp->xd_xsdev.frontend == 1);
16185084Sjohnlev
16195084Sjohnlev /*
16205084Sjohnlev * Frontend devices need to watch for the backend path changing.
16215084Sjohnlev */
16225084Sjohnlev if (pdp->xd_bepath_watch.node == NULL) {
16235084Sjohnlev size_t len;
16245084Sjohnlev char *path;
16255084Sjohnlev
16265084Sjohnlev ASSERT(pdp->xd_xsdev.nodename != NULL);
16275084Sjohnlev
16285084Sjohnlev len = strlen(pdp->xd_xsdev.nodename) + strlen("/backend") + 1;
16295084Sjohnlev path = kmem_alloc(len, KM_SLEEP);
16305084Sjohnlev (void) snprintf(path, len, "%s/backend",
16315084Sjohnlev pdp->xd_xsdev.nodename);
16325084Sjohnlev
16335084Sjohnlev pdp->xd_bepath_watch.node = path;
16345084Sjohnlev pdp->xd_bepath_watch.callback = i_xvdi_bepath_cb;
16355084Sjohnlev pdp->xd_bepath_watch.dev = (struct xenbus_device *)dip;
16365084Sjohnlev if (register_xenbus_watch(&pdp->xd_bepath_watch) != 0) {
16375084Sjohnlev kmem_free(path, len);
16385084Sjohnlev pdp->xd_bepath_watch.node = NULL;
16395084Sjohnlev return (DDI_FAILURE);
16405084Sjohnlev }
16415084Sjohnlev }
16425084Sjohnlev
16435084Sjohnlev return (DDI_SUCCESS);
16445084Sjohnlev }
16455084Sjohnlev
16465084Sjohnlev static void
i_xvdi_rem_watch_bepath(dev_info_t * dip)16475084Sjohnlev i_xvdi_rem_watch_bepath(dev_info_t *dip)
16485084Sjohnlev {
16495084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
16505084Sjohnlev
16515084Sjohnlev ASSERT(pdp != NULL);
16525084Sjohnlev ASSERT(pdp->xd_xsdev.frontend == 1);
16537756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk));
16545084Sjohnlev
16555084Sjohnlev if (pdp->xd_bepath_watch.node != NULL) {
16567756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
16575084Sjohnlev unregister_xenbus_watch(&pdp->xd_bepath_watch);
16587756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
16595084Sjohnlev
16605084Sjohnlev kmem_free((void *)(pdp->xd_bepath_watch.node),
16615084Sjohnlev strlen(pdp->xd_bepath_watch.node) + 1);
16625084Sjohnlev pdp->xd_bepath_watch.node = NULL;
16635084Sjohnlev }
16645084Sjohnlev }
16655084Sjohnlev
16665084Sjohnlev int
xvdi_switch_state(dev_info_t * dip,xenbus_transaction_t xbt,XenbusState newState)16675084Sjohnlev xvdi_switch_state(dev_info_t *dip, xenbus_transaction_t xbt,
16685084Sjohnlev XenbusState newState)
16695084Sjohnlev {
16705084Sjohnlev int rv;
16715084Sjohnlev struct xendev_ppd *pdp;
16725084Sjohnlev
16735084Sjohnlev pdp = ddi_get_parent_data(dip);
16745084Sjohnlev ASSERT(pdp != NULL);
16755084Sjohnlev
16765084Sjohnlev XVDI_DPRINTF(XVDI_DBG_STATE,
16777679SMax.Zhen@Sun.COM "xvdi_switch_state: %s@%s's xenbus state moves to %d\n",
16787679SMax.Zhen@Sun.COM ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
16797679SMax.Zhen@Sun.COM ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
16807679SMax.Zhen@Sun.COM newState);
16815084Sjohnlev
16825084Sjohnlev rv = xenbus_switch_state(&pdp->xd_xsdev, xbt, newState);
16835084Sjohnlev if (rv > 0)
16845084Sjohnlev cmn_err(CE_WARN, "xvdi_switch_state: change state failed");
16855084Sjohnlev
16865084Sjohnlev return (rv);
16875084Sjohnlev }
16885084Sjohnlev
16895084Sjohnlev /*
16905084Sjohnlev * Notify hotplug script running in userland
16915084Sjohnlev */
16925084Sjohnlev int
xvdi_post_event(dev_info_t * dip,xendev_hotplug_cmd_t hpc)16935084Sjohnlev xvdi_post_event(dev_info_t *dip, xendev_hotplug_cmd_t hpc)
16945084Sjohnlev {
16955084Sjohnlev struct xendev_ppd *pdp;
16965084Sjohnlev nvlist_t *attr_list = NULL;
16975084Sjohnlev i_xd_cfg_t *xdcp;
16985084Sjohnlev sysevent_id_t eid;
16995084Sjohnlev int err;
17005084Sjohnlev char devname[256]; /* XXPV dme: ? */
17015084Sjohnlev
17025084Sjohnlev pdp = ddi_get_parent_data(dip);
17035084Sjohnlev ASSERT(pdp != NULL);
17045084Sjohnlev
17055084Sjohnlev xdcp = i_xvdi_devclass2cfg(pdp->xd_devclass);
17065084Sjohnlev ASSERT(xdcp != NULL);
17075084Sjohnlev
17085084Sjohnlev (void) snprintf(devname, sizeof (devname) - 1, "%s%d",
17095084Sjohnlev ddi_driver_name(dip), ddi_get_instance(dip));
17105084Sjohnlev
17115084Sjohnlev err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME, KM_NOSLEEP);
17125084Sjohnlev if (err != DDI_SUCCESS)
17135084Sjohnlev goto failure;
17145084Sjohnlev
17155084Sjohnlev err = nvlist_add_int32(attr_list, "domain", pdp->xd_domain);
17165084Sjohnlev if (err != DDI_SUCCESS)
17175084Sjohnlev goto failure;
17185084Sjohnlev err = nvlist_add_int32(attr_list, "vdev", pdp->xd_vdevnum);
17195084Sjohnlev if (err != DDI_SUCCESS)
17205084Sjohnlev goto failure;
17215084Sjohnlev err = nvlist_add_string(attr_list, "devclass", xdcp->xsdev);
17225084Sjohnlev if (err != DDI_SUCCESS)
17235084Sjohnlev goto failure;
17245084Sjohnlev err = nvlist_add_string(attr_list, "device", devname);
17255084Sjohnlev if (err != DDI_SUCCESS)
17265084Sjohnlev goto failure;
17275084Sjohnlev err = nvlist_add_string(attr_list, "fob",
17285084Sjohnlev ((pdp->xd_xsdev.frontend == 1) ? "frontend" : "backend"));
17295084Sjohnlev if (err != DDI_SUCCESS)
17305084Sjohnlev goto failure;
17315084Sjohnlev
17325084Sjohnlev switch (hpc) {
17335084Sjohnlev case XEN_HP_ADD:
17345084Sjohnlev err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
17355084Sjohnlev "add", attr_list, &eid, DDI_NOSLEEP);
17365084Sjohnlev break;
17375084Sjohnlev case XEN_HP_REMOVE:
17385084Sjohnlev err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
17395084Sjohnlev "remove", attr_list, &eid, DDI_NOSLEEP);
17405084Sjohnlev break;
17415084Sjohnlev default:
17425084Sjohnlev err = DDI_FAILURE;
17435084Sjohnlev goto failure;
17445084Sjohnlev }
17455084Sjohnlev
17465084Sjohnlev failure:
17475084Sjohnlev if (attr_list != NULL)
17485084Sjohnlev nvlist_free(attr_list);
17495084Sjohnlev
17505084Sjohnlev return (err);
17515084Sjohnlev }
17525084Sjohnlev
17535084Sjohnlev /* ARGSUSED */
17545084Sjohnlev static void
i_xvdi_probe_path_cb(struct xenbus_watch * w,const char ** vec,unsigned int len)17555084Sjohnlev i_xvdi_probe_path_cb(struct xenbus_watch *w, const char **vec,
17565084Sjohnlev unsigned int len)
17575084Sjohnlev {
17585084Sjohnlev char *path;
17595084Sjohnlev
17605084Sjohnlev if (xendev_dip == NULL)
17615084Sjohnlev xendev_dip = ddi_find_devinfo("xpvd", -1, 0);
17625084Sjohnlev
17635084Sjohnlev path = i_ddi_strdup((char *)vec[XS_WATCH_PATH], KM_SLEEP);
17645084Sjohnlev
17655084Sjohnlev (void) ddi_taskq_dispatch(DEVI(xendev_dip)->devi_taskq,
17665084Sjohnlev i_xvdi_probe_path_handler, (void *)path, DDI_SLEEP);
17675084Sjohnlev }
17685084Sjohnlev
17695084Sjohnlev static void
i_xvdi_watch_device(char * path)17705084Sjohnlev i_xvdi_watch_device(char *path)
17715084Sjohnlev {
17725084Sjohnlev struct xenbus_watch *w;
17735084Sjohnlev
17745084Sjohnlev ASSERT(path != NULL);
17755084Sjohnlev
17765084Sjohnlev w = kmem_zalloc(sizeof (*w), KM_SLEEP);
17775084Sjohnlev w->node = path;
17785084Sjohnlev w->callback = &i_xvdi_probe_path_cb;
17795084Sjohnlev w->dev = NULL;
17805084Sjohnlev
17815084Sjohnlev if (register_xenbus_watch(w) != 0) {
17825084Sjohnlev cmn_err(CE_WARN, "i_xvdi_watch_device: "
17835084Sjohnlev "cannot set watch on %s", path);
17845084Sjohnlev kmem_free(w, sizeof (*w));
17855084Sjohnlev return;
17865084Sjohnlev }
17875084Sjohnlev }
17885084Sjohnlev
17895084Sjohnlev void
xvdi_watch_devices(int newstate)17905084Sjohnlev xvdi_watch_devices(int newstate)
17915084Sjohnlev {
17925084Sjohnlev int devclass;
17935084Sjohnlev
17945084Sjohnlev /*
17955084Sjohnlev * Watch for devices being created in the store.
17965084Sjohnlev */
17975084Sjohnlev if (newstate == XENSTORE_DOWN)
17985084Sjohnlev return;
17995084Sjohnlev for (devclass = 0; devclass < NXDC; devclass++) {
18005084Sjohnlev if (xdci[devclass].xs_path_fe != NULL)
18015084Sjohnlev i_xvdi_watch_device(xdci[devclass].xs_path_fe);
18025084Sjohnlev if (xdci[devclass].xs_path_be != NULL)
18035084Sjohnlev i_xvdi_watch_device(xdci[devclass].xs_path_be);
18045084Sjohnlev }
18055084Sjohnlev }
18065084Sjohnlev
18075084Sjohnlev /*
18085084Sjohnlev * Iterate over the store looking for backend devices to create.
18095084Sjohnlev */
18105084Sjohnlev static void
i_xvdi_enum_be(dev_info_t * parent,i_xd_cfg_t * xdcp)18115084Sjohnlev i_xvdi_enum_be(dev_info_t *parent, i_xd_cfg_t *xdcp)
18125084Sjohnlev {
18135084Sjohnlev char **domains;
18145084Sjohnlev unsigned int ndomains;
18155084Sjohnlev int ldomains, i;
18165084Sjohnlev
18175084Sjohnlev if ((domains = xenbus_directory(XBT_NULL, xdcp->xs_path_be, "",
18185084Sjohnlev &ndomains)) == NULL)
18195084Sjohnlev return;
18205084Sjohnlev
18215084Sjohnlev for (i = 0, ldomains = 0; i < ndomains; i++) {
18225084Sjohnlev ldomains += strlen(domains[i]) + 1 + sizeof (char *);
18235084Sjohnlev
18245084Sjohnlev i_xvdi_enum_worker(parent, xdcp, domains[i]);
18255084Sjohnlev }
18265084Sjohnlev kmem_free(domains, ldomains);
18275084Sjohnlev }
18285084Sjohnlev
18295084Sjohnlev /*
18305084Sjohnlev * Iterate over the store looking for frontend devices to create.
18315084Sjohnlev */
18325084Sjohnlev static void
i_xvdi_enum_fe(dev_info_t * parent,i_xd_cfg_t * xdcp)18335084Sjohnlev i_xvdi_enum_fe(dev_info_t *parent, i_xd_cfg_t *xdcp)
18345084Sjohnlev {
18355084Sjohnlev i_xvdi_enum_worker(parent, xdcp, NULL);
18365084Sjohnlev }
18375084Sjohnlev
18385084Sjohnlev static void
i_xvdi_enum_worker(dev_info_t * parent,i_xd_cfg_t * xdcp,char * domain)18395084Sjohnlev i_xvdi_enum_worker(dev_info_t *parent, i_xd_cfg_t *xdcp,
18405084Sjohnlev char *domain)
18415084Sjohnlev {
18425084Sjohnlev char *path, *domain_path, *ep;
18435084Sjohnlev char **devices;
18445084Sjohnlev unsigned int ndevices;
18455084Sjohnlev int ldevices, j, circ;
18465084Sjohnlev domid_t dom;
18476460Sedp long tmplong;
18485084Sjohnlev
18495084Sjohnlev if (domain == NULL) {
18505084Sjohnlev dom = DOMID_SELF;
18515084Sjohnlev path = xdcp->xs_path_fe;
18525084Sjohnlev domain_path = "";
18535084Sjohnlev } else {
18546460Sedp (void) ddi_strtol(domain, &ep, 0, &tmplong);
18556460Sedp dom = tmplong;
18565084Sjohnlev path = xdcp->xs_path_be;
18575084Sjohnlev domain_path = domain;
18585084Sjohnlev }
18595084Sjohnlev
18605084Sjohnlev if ((devices = xenbus_directory(XBT_NULL, path, domain_path,
18615084Sjohnlev &ndevices)) == NULL)
18625084Sjohnlev return;
18635084Sjohnlev
18645084Sjohnlev for (j = 0, ldevices = 0; j < ndevices; j++) {
18655084Sjohnlev int vdev;
18665084Sjohnlev
18675084Sjohnlev ldevices += strlen(devices[j]) + 1 + sizeof (char *);
18686460Sedp (void) ddi_strtol(devices[j], &ep, 0, &tmplong);
18696460Sedp vdev = tmplong;
18705084Sjohnlev
18715084Sjohnlev ndi_devi_enter(parent, &circ);
18725084Sjohnlev
18736460Sedp if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL)
18745084Sjohnlev (void) xvdi_create_dev(parent, xdcp->devclass,
18755084Sjohnlev dom, vdev);
18765084Sjohnlev
18775084Sjohnlev ndi_devi_exit(parent, circ);
18785084Sjohnlev }
18795084Sjohnlev kmem_free(devices, ldevices);
18805084Sjohnlev }
18815084Sjohnlev
18825084Sjohnlev /*
18835084Sjohnlev * Leaf drivers should call this in their detach() routine during suspend.
18845084Sjohnlev */
18855084Sjohnlev void
xvdi_suspend(dev_info_t * dip)18865084Sjohnlev xvdi_suspend(dev_info_t *dip)
18875084Sjohnlev {
18885084Sjohnlev i_xvdi_rem_watches(dip);
18895084Sjohnlev }
18905084Sjohnlev
18915084Sjohnlev /*
18925084Sjohnlev * Leaf drivers should call this in their attach() routine during resume.
18935084Sjohnlev */
18945084Sjohnlev int
xvdi_resume(dev_info_t * dip)18955084Sjohnlev xvdi_resume(dev_info_t *dip)
18965084Sjohnlev {
18975084Sjohnlev return (i_xvdi_add_watches(dip));
18985084Sjohnlev }
18995084Sjohnlev
19005084Sjohnlev /*
19015084Sjohnlev * Add event handler for the leaf driver
19025084Sjohnlev * to handle event triggered by the change in xenstore
19035084Sjohnlev */
19045084Sjohnlev int
xvdi_add_event_handler(dev_info_t * dip,char * name,void (* evthandler)(dev_info_t *,ddi_eventcookie_t,void *,void *),void * arg)19055084Sjohnlev xvdi_add_event_handler(dev_info_t *dip, char *name,
19067756SMark.Johnson@Sun.COM void (*evthandler)(dev_info_t *, ddi_eventcookie_t, void *, void *),
19077756SMark.Johnson@Sun.COM void *arg)
19085084Sjohnlev {
19095084Sjohnlev ddi_eventcookie_t ecv;
19105084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
19115084Sjohnlev ddi_callback_id_t *cbid;
19127756SMark.Johnson@Sun.COM boolean_t call_handler;
19137756SMark.Johnson@Sun.COM i_oestate_evt_t *evt = NULL;
19147756SMark.Johnson@Sun.COM XenbusState oestate;
19155084Sjohnlev
19165084Sjohnlev ASSERT(pdp != NULL);
19175084Sjohnlev
19187756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
19195084Sjohnlev
19205084Sjohnlev if (strcmp(name, XS_OE_STATE) == 0) {
19215084Sjohnlev ASSERT(pdp->xd_xsdev.otherend != NULL);
19225084Sjohnlev
19235084Sjohnlev cbid = &pdp->xd_oe_ehid;
19245084Sjohnlev } else if (strcmp(name, XS_HP_STATE) == 0) {
19255084Sjohnlev if (pdp->xd_xsdev.frontend == 1) {
19267756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
19275084Sjohnlev return (DDI_FAILURE);
19285084Sjohnlev }
19295084Sjohnlev
19305084Sjohnlev ASSERT(pdp->xd_hp_watch.node != NULL);
19315084Sjohnlev
19325084Sjohnlev cbid = &pdp->xd_hp_ehid;
19335084Sjohnlev } else {
19345084Sjohnlev /* Unsupported watch. */
19357756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
19365084Sjohnlev return (DDI_FAILURE);
19375084Sjohnlev }
19385084Sjohnlev
19395084Sjohnlev /*
19405084Sjohnlev * No event handler provided, take default action to handle
19415084Sjohnlev * event.
19425084Sjohnlev */
19435084Sjohnlev if (evthandler == NULL) {
19447756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
19455084Sjohnlev return (DDI_SUCCESS);
19465084Sjohnlev }
19475084Sjohnlev
19485084Sjohnlev ASSERT(*cbid == NULL);
19495084Sjohnlev
19505084Sjohnlev if (ddi_get_eventcookie(dip, name, &ecv) != DDI_SUCCESS) {
19515084Sjohnlev cmn_err(CE_WARN, "failed to find %s cookie for %s@%s",
19525084Sjohnlev name, ddi_get_name(dip), ddi_get_name_addr(dip));
19537756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
19545084Sjohnlev return (DDI_FAILURE);
19555084Sjohnlev }
19567756SMark.Johnson@Sun.COM if (ddi_add_event_handler(dip, ecv, evthandler, arg, cbid)
19575084Sjohnlev != DDI_SUCCESS) {
19585084Sjohnlev cmn_err(CE_WARN, "failed to add %s event handler for %s@%s",
19595084Sjohnlev name, ddi_get_name(dip), ddi_get_name_addr(dip));
19605084Sjohnlev *cbid = NULL;
19617756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
19625084Sjohnlev return (DDI_FAILURE);
19635084Sjohnlev }
19645084Sjohnlev
19657756SMark.Johnson@Sun.COM /*
19667756SMark.Johnson@Sun.COM * if we're adding an oe state callback, and the ring has already
19677756SMark.Johnson@Sun.COM * transitioned out of Unknown, call the handler after we release
19687756SMark.Johnson@Sun.COM * the mutex.
19697756SMark.Johnson@Sun.COM */
19707756SMark.Johnson@Sun.COM call_handler = B_FALSE;
19717756SMark.Johnson@Sun.COM if ((strcmp(name, XS_OE_STATE) == 0) &&
19727756SMark.Johnson@Sun.COM (pdp->xd_xsdev.otherend_state != XenbusStateUnknown)) {
19737756SMark.Johnson@Sun.COM oestate = pdp->xd_xsdev.otherend_state;
19747756SMark.Johnson@Sun.COM call_handler = B_TRUE;
19757756SMark.Johnson@Sun.COM }
19767756SMark.Johnson@Sun.COM
19777756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
19787756SMark.Johnson@Sun.COM
19797756SMark.Johnson@Sun.COM if (call_handler) {
19807756SMark.Johnson@Sun.COM evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP);
19817756SMark.Johnson@Sun.COM evt->dip = dip;
19827756SMark.Johnson@Sun.COM evt->state = oestate;
19837756SMark.Johnson@Sun.COM (void) ddi_taskq_dispatch(pdp->xd_oe_taskq,
19847756SMark.Johnson@Sun.COM i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP);
19857756SMark.Johnson@Sun.COM }
19865084Sjohnlev
19875084Sjohnlev return (DDI_SUCCESS);
19885084Sjohnlev }
19895084Sjohnlev
19905084Sjohnlev /*
19915084Sjohnlev * Remove event handler for the leaf driver and unwatch xenstore
19925084Sjohnlev * so, driver will not be notified when xenstore entry changed later
19935084Sjohnlev */
19945084Sjohnlev void
xvdi_remove_event_handler(dev_info_t * dip,char * name)19955084Sjohnlev xvdi_remove_event_handler(dev_info_t *dip, char *name)
19965084Sjohnlev {
19975084Sjohnlev struct xendev_ppd *pdp;
19985084Sjohnlev boolean_t rem_oe = B_FALSE, rem_hp = B_FALSE;
19995084Sjohnlev ddi_callback_id_t oeid = NULL, hpid = NULL;
20005084Sjohnlev
20015084Sjohnlev pdp = ddi_get_parent_data(dip);
20025084Sjohnlev ASSERT(pdp != NULL);
20035084Sjohnlev
20045084Sjohnlev if (name == NULL) {
20055084Sjohnlev rem_oe = B_TRUE;
20065084Sjohnlev rem_hp = B_TRUE;
20075084Sjohnlev } else if (strcmp(name, XS_OE_STATE) == 0) {
20085084Sjohnlev rem_oe = B_TRUE;
20095084Sjohnlev } else if (strcmp(name, XS_HP_STATE) == 0) {
20105084Sjohnlev rem_hp = B_TRUE;
20115084Sjohnlev } else {
20125084Sjohnlev cmn_err(CE_WARN, "event %s not supported, cannot remove", name);
20135084Sjohnlev return;
20145084Sjohnlev }
20155084Sjohnlev
20167756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
20175084Sjohnlev
20185084Sjohnlev if (rem_oe && (pdp->xd_oe_ehid != NULL)) {
20195084Sjohnlev oeid = pdp->xd_oe_ehid;
20205084Sjohnlev pdp->xd_oe_ehid = NULL;
20215084Sjohnlev }
20225084Sjohnlev
20235084Sjohnlev if (rem_hp && (pdp->xd_hp_ehid != NULL)) {
20245084Sjohnlev hpid = pdp->xd_hp_ehid;
20255084Sjohnlev pdp->xd_hp_ehid = NULL;
20265084Sjohnlev }
20275084Sjohnlev
20287756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
20295084Sjohnlev
20305084Sjohnlev if (oeid != NULL)
20315084Sjohnlev (void) ddi_remove_event_handler(oeid);
20325084Sjohnlev if (hpid != NULL)
20335084Sjohnlev (void) ddi_remove_event_handler(hpid);
20345084Sjohnlev }
20355084Sjohnlev
20365084Sjohnlev
20375084Sjohnlev /*
20385084Sjohnlev * common ring interfaces
20395084Sjohnlev */
20405084Sjohnlev
20415084Sjohnlev #define FRONT_RING(_ringp) (&(_ringp)->xr_sring.fr)
20425084Sjohnlev #define BACK_RING(_ringp) (&(_ringp)->xr_sring.br)
20435084Sjohnlev #define GET_RING_SIZE(_ringp) RING_SIZE(FRONT_RING(ringp))
20445084Sjohnlev #define GET_RING_ENTRY_FE(_ringp, _idx) \
20455084Sjohnlev (FRONT_RING(_ringp)->sring->ring + \
20465084Sjohnlev (_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
20475084Sjohnlev #define GET_RING_ENTRY_BE(_ringp, _idx) \
20485084Sjohnlev (BACK_RING(_ringp)->sring->ring + \
20495084Sjohnlev (_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
20505084Sjohnlev
20515084Sjohnlev unsigned int
xvdi_ring_avail_slots(xendev_ring_t * ringp)20525084Sjohnlev xvdi_ring_avail_slots(xendev_ring_t *ringp)
20535084Sjohnlev {
20545084Sjohnlev comif_ring_fe_t *frp;
20555084Sjohnlev comif_ring_be_t *brp;
20565084Sjohnlev
20575084Sjohnlev if (ringp->xr_frontend) {
20585084Sjohnlev frp = FRONT_RING(ringp);
20595084Sjohnlev return (GET_RING_SIZE(ringp) -
20605084Sjohnlev (frp->req_prod_pvt - frp->rsp_cons));
20615084Sjohnlev } else {
20625084Sjohnlev brp = BACK_RING(ringp);
20635084Sjohnlev return (GET_RING_SIZE(ringp) -
20645084Sjohnlev (brp->rsp_prod_pvt - brp->req_cons));
20655084Sjohnlev }
20665084Sjohnlev }
20675084Sjohnlev
20685084Sjohnlev int
xvdi_ring_has_unconsumed_requests(xendev_ring_t * ringp)20695084Sjohnlev xvdi_ring_has_unconsumed_requests(xendev_ring_t *ringp)
20705084Sjohnlev {
20715084Sjohnlev comif_ring_be_t *brp;
20725084Sjohnlev
20735084Sjohnlev ASSERT(!ringp->xr_frontend);
20745084Sjohnlev brp = BACK_RING(ringp);
20755084Sjohnlev return ((brp->req_cons !=
20765084Sjohnlev ddi_get32(ringp->xr_acc_hdl, &brp->sring->req_prod)) &&
20775084Sjohnlev ((brp->req_cons - brp->rsp_prod_pvt) != RING_SIZE(brp)));
20785084Sjohnlev }
20795084Sjohnlev
20805084Sjohnlev int
xvdi_ring_has_incomp_request(xendev_ring_t * ringp)20815084Sjohnlev xvdi_ring_has_incomp_request(xendev_ring_t *ringp)
20825084Sjohnlev {
20835084Sjohnlev comif_ring_fe_t *frp;
20845084Sjohnlev
20855084Sjohnlev ASSERT(ringp->xr_frontend);
20865084Sjohnlev frp = FRONT_RING(ringp);
20875084Sjohnlev return (frp->req_prod_pvt !=
20885084Sjohnlev ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
20895084Sjohnlev }
20905084Sjohnlev
20915084Sjohnlev int
xvdi_ring_has_unconsumed_responses(xendev_ring_t * ringp)20925084Sjohnlev xvdi_ring_has_unconsumed_responses(xendev_ring_t *ringp)
20935084Sjohnlev {
20945084Sjohnlev comif_ring_fe_t *frp;
20955084Sjohnlev
20965084Sjohnlev ASSERT(ringp->xr_frontend);
20975084Sjohnlev frp = FRONT_RING(ringp);
20985084Sjohnlev return (frp->rsp_cons !=
20995084Sjohnlev ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
21005084Sjohnlev }
21015084Sjohnlev
21025084Sjohnlev /* NOTE: req_event will be increased as needed */
21035084Sjohnlev void *
xvdi_ring_get_request(xendev_ring_t * ringp)21045084Sjohnlev xvdi_ring_get_request(xendev_ring_t *ringp)
21055084Sjohnlev {
21065084Sjohnlev comif_ring_fe_t *frp;
21075084Sjohnlev comif_ring_be_t *brp;
21085084Sjohnlev
21095084Sjohnlev if (ringp->xr_frontend) {
21105084Sjohnlev /* for frontend ring */
21115084Sjohnlev frp = FRONT_RING(ringp);
21125084Sjohnlev if (!RING_FULL(frp))
21135084Sjohnlev return (GET_RING_ENTRY_FE(ringp, frp->req_prod_pvt++));
21145084Sjohnlev else
21155084Sjohnlev return (NULL);
21165084Sjohnlev } else {
21175084Sjohnlev /* for backend ring */
21185084Sjohnlev brp = BACK_RING(ringp);
21195084Sjohnlev /* RING_FINAL_CHECK_FOR_REQUESTS() */
21205084Sjohnlev if (xvdi_ring_has_unconsumed_requests(ringp))
21215084Sjohnlev return (GET_RING_ENTRY_BE(ringp, brp->req_cons++));
21225084Sjohnlev else {
21235084Sjohnlev ddi_put32(ringp->xr_acc_hdl, &brp->sring->req_event,
21245084Sjohnlev brp->req_cons + 1);
21255084Sjohnlev membar_enter();
21265084Sjohnlev if (xvdi_ring_has_unconsumed_requests(ringp))
21275084Sjohnlev return (GET_RING_ENTRY_BE(ringp,
21285084Sjohnlev brp->req_cons++));
21295084Sjohnlev else
21305084Sjohnlev return (NULL);
21315084Sjohnlev }
21325084Sjohnlev }
21335084Sjohnlev }
21345084Sjohnlev
21355084Sjohnlev int
xvdi_ring_push_request(xendev_ring_t * ringp)21365084Sjohnlev xvdi_ring_push_request(xendev_ring_t *ringp)
21375084Sjohnlev {
21385084Sjohnlev RING_IDX old, new, reqevt;
21395084Sjohnlev comif_ring_fe_t *frp;
21405084Sjohnlev
21415084Sjohnlev /* only frontend should be able to push request */
21425084Sjohnlev ASSERT(ringp->xr_frontend);
21435084Sjohnlev
21445084Sjohnlev /* RING_PUSH_REQUEST_AND_CHECK_NOTIFY() */
21455084Sjohnlev frp = FRONT_RING(ringp);
21465084Sjohnlev old = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_prod);
21475084Sjohnlev new = frp->req_prod_pvt;
21485084Sjohnlev ddi_put32(ringp->xr_acc_hdl, &frp->sring->req_prod, new);
21495084Sjohnlev membar_enter();
21505084Sjohnlev reqevt = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_event);
21515084Sjohnlev return ((RING_IDX)(new - reqevt) < (RING_IDX)(new - old));
21525084Sjohnlev }
21535084Sjohnlev
21545084Sjohnlev /* NOTE: rsp_event will be increased as needed */
21555084Sjohnlev void *
xvdi_ring_get_response(xendev_ring_t * ringp)21565084Sjohnlev xvdi_ring_get_response(xendev_ring_t *ringp)
21575084Sjohnlev {
21585084Sjohnlev comif_ring_fe_t *frp;
21595084Sjohnlev comif_ring_be_t *brp;
21605084Sjohnlev
21615084Sjohnlev if (!ringp->xr_frontend) {
21625084Sjohnlev /* for backend ring */
21635084Sjohnlev brp = BACK_RING(ringp);
21645084Sjohnlev return (GET_RING_ENTRY_BE(ringp, brp->rsp_prod_pvt++));
21655084Sjohnlev } else {
21665084Sjohnlev /* for frontend ring */
21675084Sjohnlev frp = FRONT_RING(ringp);
21685084Sjohnlev /* RING_FINAL_CHECK_FOR_RESPONSES() */
21695084Sjohnlev if (xvdi_ring_has_unconsumed_responses(ringp))
21705084Sjohnlev return (GET_RING_ENTRY_FE(ringp, frp->rsp_cons++));
21715084Sjohnlev else {
21725084Sjohnlev ddi_put32(ringp->xr_acc_hdl, &frp->sring->rsp_event,
21735084Sjohnlev frp->rsp_cons + 1);
21745084Sjohnlev membar_enter();
21755084Sjohnlev if (xvdi_ring_has_unconsumed_responses(ringp))
21765084Sjohnlev return (GET_RING_ENTRY_FE(ringp,
21775084Sjohnlev frp->rsp_cons++));
21785084Sjohnlev else
21795084Sjohnlev return (NULL);
21805084Sjohnlev }
21815084Sjohnlev }
21825084Sjohnlev }
21835084Sjohnlev
21845084Sjohnlev int
xvdi_ring_push_response(xendev_ring_t * ringp)21855084Sjohnlev xvdi_ring_push_response(xendev_ring_t *ringp)
21865084Sjohnlev {
21875084Sjohnlev RING_IDX old, new, rspevt;
21885084Sjohnlev comif_ring_be_t *brp;
21895084Sjohnlev
21905084Sjohnlev /* only backend should be able to push response */
21915084Sjohnlev ASSERT(!ringp->xr_frontend);
21925084Sjohnlev
21935084Sjohnlev /* RING_PUSH_RESPONSE_AND_CHECK_NOTIFY() */
21945084Sjohnlev brp = BACK_RING(ringp);
21955084Sjohnlev old = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_prod);
21965084Sjohnlev new = brp->rsp_prod_pvt;
21975084Sjohnlev ddi_put32(ringp->xr_acc_hdl, &brp->sring->rsp_prod, new);
21985084Sjohnlev membar_enter();
21995084Sjohnlev rspevt = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_event);
22005084Sjohnlev return ((RING_IDX)(new - rspevt) < (RING_IDX)(new - old));
22015084Sjohnlev }
22025084Sjohnlev
22035084Sjohnlev static void
xvdi_ring_init_sring(xendev_ring_t * ringp)22045084Sjohnlev xvdi_ring_init_sring(xendev_ring_t *ringp)
22055084Sjohnlev {
22065084Sjohnlev ddi_acc_handle_t acchdl;
22075084Sjohnlev comif_sring_t *xsrp;
22085084Sjohnlev int i;
22095084Sjohnlev
22105084Sjohnlev xsrp = (comif_sring_t *)ringp->xr_vaddr;
22115084Sjohnlev acchdl = ringp->xr_acc_hdl;
22125084Sjohnlev
22135084Sjohnlev /* shared ring initialization */
22145084Sjohnlev ddi_put32(acchdl, &xsrp->req_prod, 0);
22155084Sjohnlev ddi_put32(acchdl, &xsrp->rsp_prod, 0);
22165084Sjohnlev ddi_put32(acchdl, &xsrp->req_event, 1);
22175084Sjohnlev ddi_put32(acchdl, &xsrp->rsp_event, 1);
22185084Sjohnlev for (i = 0; i < sizeof (xsrp->pad); i++)
22195084Sjohnlev ddi_put8(acchdl, xsrp->pad + i, 0);
22205084Sjohnlev }
22215084Sjohnlev
22225084Sjohnlev static void
xvdi_ring_init_front_ring(xendev_ring_t * ringp,size_t nentry,size_t entrysize)22235084Sjohnlev xvdi_ring_init_front_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
22245084Sjohnlev {
22255084Sjohnlev comif_ring_fe_t *xfrp;
22265084Sjohnlev
22275084Sjohnlev xfrp = &ringp->xr_sring.fr;
22285084Sjohnlev xfrp->req_prod_pvt = 0;
22295084Sjohnlev xfrp->rsp_cons = 0;
22305084Sjohnlev xfrp->nr_ents = nentry;
22315084Sjohnlev xfrp->sring = (comif_sring_t *)ringp->xr_vaddr;
22325084Sjohnlev
22335084Sjohnlev ringp->xr_frontend = 1;
22345084Sjohnlev ringp->xr_entry_size = entrysize;
22355084Sjohnlev }
22365084Sjohnlev
22375741Smrj #ifndef XPV_HVM_DRIVER
22385084Sjohnlev static void
xvdi_ring_init_back_ring(xendev_ring_t * ringp,size_t nentry,size_t entrysize)22395084Sjohnlev xvdi_ring_init_back_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
22405084Sjohnlev {
22415084Sjohnlev comif_ring_be_t *xbrp;
22425084Sjohnlev
22435084Sjohnlev xbrp = &ringp->xr_sring.br;
22445084Sjohnlev xbrp->rsp_prod_pvt = 0;
22455084Sjohnlev xbrp->req_cons = 0;
22465084Sjohnlev xbrp->nr_ents = nentry;
22475084Sjohnlev xbrp->sring = (comif_sring_t *)ringp->xr_vaddr;
22485084Sjohnlev
22495084Sjohnlev ringp->xr_frontend = 0;
22505084Sjohnlev ringp->xr_entry_size = entrysize;
22515084Sjohnlev }
22525741Smrj #endif /* XPV_HVM_DRIVER */
22535084Sjohnlev
22545084Sjohnlev static void
xendev_offline_device(void * arg)22555084Sjohnlev xendev_offline_device(void *arg)
22565084Sjohnlev {
22575084Sjohnlev dev_info_t *dip = (dev_info_t *)arg;
22585084Sjohnlev char devname[MAXNAMELEN] = {0};
22595084Sjohnlev
22605084Sjohnlev /*
22615084Sjohnlev * This is currently the only chance to delete a devinfo node, which
22625084Sjohnlev * is _not_ always successful.
22635084Sjohnlev */
22645084Sjohnlev (void) ddi_deviname(dip, devname);
22655084Sjohnlev (void) devfs_clean(ddi_get_parent(dip), devname + 1, DV_CLEAN_FORCE);
22665084Sjohnlev (void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
22675084Sjohnlev }
22685084Sjohnlev
22695084Sjohnlev static void
i_xvdi_oestate_cb(struct xenbus_device * dev,XenbusState oestate)22705084Sjohnlev i_xvdi_oestate_cb(struct xenbus_device *dev, XenbusState oestate)
22715084Sjohnlev {
22725084Sjohnlev dev_info_t *dip = (dev_info_t *)dev->data;
22735084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
22747679SMax.Zhen@Sun.COM i_oestate_evt_t *evt = NULL;
22757756SMark.Johnson@Sun.COM boolean_t call_handler;
22767679SMax.Zhen@Sun.COM
22777679SMax.Zhen@Sun.COM XVDI_DPRINTF(XVDI_DBG_STATE,
22787679SMax.Zhen@Sun.COM "i_xvdi_oestate_cb: %s@%s sees oestate change to %d\n",
22797679SMax.Zhen@Sun.COM ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
22807679SMax.Zhen@Sun.COM ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
22817679SMax.Zhen@Sun.COM oestate);
22825084Sjohnlev
22837756SMark.Johnson@Sun.COM /* only call the handler if our state has changed */
22847756SMark.Johnson@Sun.COM call_handler = B_FALSE;
22857756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk);
22867756SMark.Johnson@Sun.COM if (dev->otherend_state != oestate) {
22877756SMark.Johnson@Sun.COM dev->otherend_state = oestate;
22887756SMark.Johnson@Sun.COM call_handler = B_TRUE;
22897756SMark.Johnson@Sun.COM }
22907756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk);
22915084Sjohnlev
22927756SMark.Johnson@Sun.COM if (call_handler) {
22937756SMark.Johnson@Sun.COM /*
22947756SMark.Johnson@Sun.COM * Try to deliver the oestate change event to the dip
22957756SMark.Johnson@Sun.COM */
22967756SMark.Johnson@Sun.COM evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP);
22977756SMark.Johnson@Sun.COM evt->dip = dip;
22987756SMark.Johnson@Sun.COM evt->state = oestate;
22997756SMark.Johnson@Sun.COM (void) ddi_taskq_dispatch(pdp->xd_oe_taskq,
23007756SMark.Johnson@Sun.COM i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP);
23017756SMark.Johnson@Sun.COM }
23025084Sjohnlev }
23035084Sjohnlev
23045084Sjohnlev /*ARGSUSED*/
23055084Sjohnlev static void
i_xvdi_hpstate_cb(struct xenbus_watch * w,const char ** vec,unsigned int len)23065084Sjohnlev i_xvdi_hpstate_cb(struct xenbus_watch *w, const char **vec,
23075084Sjohnlev unsigned int len)
23085084Sjohnlev {
23095084Sjohnlev dev_info_t *dip = (dev_info_t *)w->dev;
23105084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip);
23115084Sjohnlev
23127679SMax.Zhen@Sun.COM #ifdef DEBUG
23137679SMax.Zhen@Sun.COM char *hp_status = NULL;
23147679SMax.Zhen@Sun.COM unsigned int hpl = 0;
23157679SMax.Zhen@Sun.COM
23167679SMax.Zhen@Sun.COM (void) xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
23177679SMax.Zhen@Sun.COM (void *)&hp_status, &hpl);
23187679SMax.Zhen@Sun.COM XVDI_DPRINTF(XVDI_DBG_STATE,
23197679SMax.Zhen@Sun.COM "i_xvdi_hpstate_cb: %s@%s sees hpstate change to %s\n",
23207679SMax.Zhen@Sun.COM ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
23217679SMax.Zhen@Sun.COM ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
23227679SMax.Zhen@Sun.COM hp_status == NULL ? "null" : hp_status);
23237679SMax.Zhen@Sun.COM if (hp_status != NULL)
23247679SMax.Zhen@Sun.COM kmem_free(hp_status, hpl);
23257679SMax.Zhen@Sun.COM #endif /* DEBUG */
23267679SMax.Zhen@Sun.COM
23275084Sjohnlev (void) ddi_taskq_dispatch(pdp->xd_hp_taskq,
23285084Sjohnlev i_xvdi_hpstate_handler, (void *)dip, DDI_SLEEP);
23295084Sjohnlev }
23305084Sjohnlev
23315084Sjohnlev static void
i_xvdi_probe_path_handler(void * arg)23325084Sjohnlev i_xvdi_probe_path_handler(void *arg)
23335084Sjohnlev {
23345084Sjohnlev dev_info_t *parent;
23355084Sjohnlev char *path = arg, *p = NULL;
23365084Sjohnlev int i, vdev, circ;
23375084Sjohnlev i_xd_cfg_t *xdcp;
23385084Sjohnlev boolean_t frontend;
23395084Sjohnlev domid_t dom;
23405084Sjohnlev
23415084Sjohnlev for (i = 0, xdcp = &xdci[0]; i < NXDC; i++, xdcp++) {
23425084Sjohnlev
23435084Sjohnlev if ((xdcp->xs_path_fe != NULL) &&
23445084Sjohnlev (strncmp(path, xdcp->xs_path_fe, strlen(xdcp->xs_path_fe))
23455084Sjohnlev == 0)) {
23465084Sjohnlev
23475084Sjohnlev frontend = B_TRUE;
23485084Sjohnlev p = path + strlen(xdcp->xs_path_fe);
23495084Sjohnlev break;
23505084Sjohnlev }
23515084Sjohnlev
23525084Sjohnlev if ((xdcp->xs_path_be != NULL) &&
23535084Sjohnlev (strncmp(path, xdcp->xs_path_be, strlen(xdcp->xs_path_be))
23545084Sjohnlev == 0)) {
23555084Sjohnlev
23565084Sjohnlev frontend = B_FALSE;
23575084Sjohnlev p = path + strlen(xdcp->xs_path_be);
23585084Sjohnlev break;
23595084Sjohnlev }
23605084Sjohnlev
23615084Sjohnlev }
23625084Sjohnlev
23635084Sjohnlev if (p == NULL) {
23645084Sjohnlev cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
23655084Sjohnlev "unexpected path prefix in %s", path);
23665084Sjohnlev goto done;
23675084Sjohnlev }
23685084Sjohnlev
23695084Sjohnlev if (frontend) {
23705084Sjohnlev dom = DOMID_SELF;
23715084Sjohnlev if (sscanf(p, "/%d/", &vdev) != 1) {
23725084Sjohnlev XVDI_DPRINTF(XVDI_DBG_PROBE,
23735084Sjohnlev "i_xvdi_probe_path_handler: "
23745084Sjohnlev "cannot parse frontend path %s",
23755084Sjohnlev path);
23765084Sjohnlev goto done;
23775084Sjohnlev }
23785084Sjohnlev } else {
23797632SNick.Todd@Sun.COM if (sscanf(p, "/%hu/%d/", &dom, &vdev) != 2) {
23805084Sjohnlev XVDI_DPRINTF(XVDI_DBG_PROBE,
23815084Sjohnlev "i_xvdi_probe_path_handler: "
23825084Sjohnlev "cannot parse backend path %s",
23835084Sjohnlev path);
23845084Sjohnlev goto done;
23855084Sjohnlev }
23865084Sjohnlev }
23875084Sjohnlev
23886175Sjohnlev /*
23896175Sjohnlev * This is an oxymoron, so indicates a bogus configuration we
23906175Sjohnlev * must check for.
23916175Sjohnlev */
23926175Sjohnlev if (vdev == VDEV_NOXS) {
23936175Sjohnlev cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
23946175Sjohnlev "invalid path %s", path);
23956175Sjohnlev goto done;
23966175Sjohnlev }
23976175Sjohnlev
23985084Sjohnlev parent = xendev_dip;
23995084Sjohnlev ASSERT(parent != NULL);
24005084Sjohnlev
24015084Sjohnlev ndi_devi_enter(parent, &circ);
24025084Sjohnlev
24035084Sjohnlev if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL) {
24045084Sjohnlev XVDI_DPRINTF(XVDI_DBG_PROBE,
24055084Sjohnlev "i_xvdi_probe_path_handler: create for %s", path);
24065084Sjohnlev (void) xvdi_create_dev(parent, xdcp->devclass, dom, vdev);
24075084Sjohnlev } else {
24085084Sjohnlev XVDI_DPRINTF(XVDI_DBG_PROBE,
24095084Sjohnlev "i_xvdi_probe_path_handler: %s already exists", path);
24105084Sjohnlev }
24115084Sjohnlev
24125084Sjohnlev ndi_devi_exit(parent, circ);
24135084Sjohnlev
24145084Sjohnlev done:
24155084Sjohnlev kmem_free(path, strlen(path) + 1);
24165084Sjohnlev }
2417