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 /* 23*8863SEdward.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> 65*8863SEdward.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 * 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 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; 267*8863SEdward.Pilatowicz@Sun.COM list_create(&pdp->xd_xb_watches, sizeof (xd_xb_watches_t), 268*8863SEdward.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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 * 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 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 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 * 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 * 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 * 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 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 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 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 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 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 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 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 1202*8863SEdward.Pilatowicz@Sun.COM static void 1203*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_free(xd_xb_watches_t *xxwp) 1204*8863SEdward.Pilatowicz@Sun.COM { 1205*8863SEdward.Pilatowicz@Sun.COM ASSERT(xxwp->xxw_ref == 0); 1206*8863SEdward.Pilatowicz@Sun.COM strfree((char *)xxwp->xxw_watch.node); 1207*8863SEdward.Pilatowicz@Sun.COM kmem_free(xxwp, sizeof (*xxwp)); 1208*8863SEdward.Pilatowicz@Sun.COM } 1209*8863SEdward.Pilatowicz@Sun.COM 1210*8863SEdward.Pilatowicz@Sun.COM static void 1211*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xd_xb_watches_t *xxwp) 1212*8863SEdward.Pilatowicz@Sun.COM { 1213*8863SEdward.Pilatowicz@Sun.COM ASSERT(MUTEX_HELD(&xxwp->xxw_xppd->xd_ndi_lk)); 1214*8863SEdward.Pilatowicz@Sun.COM ASSERT(xxwp->xxw_ref > 0); 1215*8863SEdward.Pilatowicz@Sun.COM if (--xxwp->xxw_ref == 0) 1216*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_free(xxwp); 1217*8863SEdward.Pilatowicz@Sun.COM } 1218*8863SEdward.Pilatowicz@Sun.COM 1219*8863SEdward.Pilatowicz@Sun.COM static void 1220*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_hold(xd_xb_watches_t *xxwp) 1221*8863SEdward.Pilatowicz@Sun.COM { 1222*8863SEdward.Pilatowicz@Sun.COM ASSERT(MUTEX_HELD(&xxwp->xxw_xppd->xd_ndi_lk)); 1223*8863SEdward.Pilatowicz@Sun.COM ASSERT(xxwp->xxw_ref > 0); 1224*8863SEdward.Pilatowicz@Sun.COM xxwp->xxw_ref++; 1225*8863SEdward.Pilatowicz@Sun.COM } 1226*8863SEdward.Pilatowicz@Sun.COM 1227*8863SEdward.Pilatowicz@Sun.COM static void 1228*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_cb_tq(void *arg) 1229*8863SEdward.Pilatowicz@Sun.COM { 1230*8863SEdward.Pilatowicz@Sun.COM xd_xb_watches_t *xxwp = (xd_xb_watches_t *)arg; 1231*8863SEdward.Pilatowicz@Sun.COM dev_info_t *dip = (dev_info_t *)xxwp->xxw_watch.dev; 1232*8863SEdward.Pilatowicz@Sun.COM struct xendev_ppd *pdp = xxwp->xxw_xppd; 1233*8863SEdward.Pilatowicz@Sun.COM 1234*8863SEdward.Pilatowicz@Sun.COM xxwp->xxw_cb(dip, xxwp->xxw_watch.node, xxwp->xxw_arg); 1235*8863SEdward.Pilatowicz@Sun.COM 1236*8863SEdward.Pilatowicz@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 1237*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxwp); 1238*8863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1239*8863SEdward.Pilatowicz@Sun.COM } 1240*8863SEdward.Pilatowicz@Sun.COM 1241*8863SEdward.Pilatowicz@Sun.COM static void 1242*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_cb(struct xenbus_watch *w, const char **vec, unsigned int len) 1243*8863SEdward.Pilatowicz@Sun.COM { 1244*8863SEdward.Pilatowicz@Sun.COM dev_info_t *dip = (dev_info_t *)w->dev; 1245*8863SEdward.Pilatowicz@Sun.COM struct xendev_ppd *pdp = ddi_get_parent_data(dip); 1246*8863SEdward.Pilatowicz@Sun.COM xd_xb_watches_t *xxwp; 1247*8863SEdward.Pilatowicz@Sun.COM 1248*8863SEdward.Pilatowicz@Sun.COM ASSERT(len > XS_WATCH_PATH); 1249*8863SEdward.Pilatowicz@Sun.COM ASSERT(vec[XS_WATCH_PATH] != NULL); 1250*8863SEdward.Pilatowicz@Sun.COM 1251*8863SEdward.Pilatowicz@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 1252*8863SEdward.Pilatowicz@Sun.COM for (xxwp = list_head(&pdp->xd_xb_watches); xxwp != NULL; 1253*8863SEdward.Pilatowicz@Sun.COM xxwp = list_next(&pdp->xd_xb_watches, xxwp)) { 1254*8863SEdward.Pilatowicz@Sun.COM if (w == &xxwp->xxw_watch) 1255*8863SEdward.Pilatowicz@Sun.COM break; 1256*8863SEdward.Pilatowicz@Sun.COM } 1257*8863SEdward.Pilatowicz@Sun.COM 1258*8863SEdward.Pilatowicz@Sun.COM if (xxwp == NULL) { 1259*8863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1260*8863SEdward.Pilatowicz@Sun.COM return; 1261*8863SEdward.Pilatowicz@Sun.COM } 1262*8863SEdward.Pilatowicz@Sun.COM 1263*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_hold(xxwp); 1264*8863SEdward.Pilatowicz@Sun.COM (void) ddi_taskq_dispatch(pdp->xd_xb_watch_taskq, 1265*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_cb_tq, xxwp, DDI_SLEEP); 1266*8863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1267*8863SEdward.Pilatowicz@Sun.COM } 1268*8863SEdward.Pilatowicz@Sun.COM 1269*8863SEdward.Pilatowicz@Sun.COM /* 1270*8863SEdward.Pilatowicz@Sun.COM * Any watches registered with xvdi_add_xb_watch_handler() get torn down during 1271*8863SEdward.Pilatowicz@Sun.COM * a suspend operation. So if a frontend driver want's to use these interfaces, 1272*8863SEdward.Pilatowicz@Sun.COM * that driver is responsible for re-registering any watches it had before 1273*8863SEdward.Pilatowicz@Sun.COM * the suspend operation. 1274*8863SEdward.Pilatowicz@Sun.COM */ 1275*8863SEdward.Pilatowicz@Sun.COM int 1276*8863SEdward.Pilatowicz@Sun.COM xvdi_add_xb_watch_handler(dev_info_t *dip, const char *dir, const char *node, 1277*8863SEdward.Pilatowicz@Sun.COM xvdi_xb_watch_cb_t cb, void *arg) 1278*8863SEdward.Pilatowicz@Sun.COM { 1279*8863SEdward.Pilatowicz@Sun.COM struct xendev_ppd *pdp = ddi_get_parent_data(dip); 1280*8863SEdward.Pilatowicz@Sun.COM xd_xb_watches_t *xxw_new, *xxwp; 1281*8863SEdward.Pilatowicz@Sun.COM char *path; 1282*8863SEdward.Pilatowicz@Sun.COM int n; 1283*8863SEdward.Pilatowicz@Sun.COM 1284*8863SEdward.Pilatowicz@Sun.COM ASSERT((dip != NULL) && (dir != NULL) && (node != NULL)); 1285*8863SEdward.Pilatowicz@Sun.COM ASSERT(cb != NULL); 1286*8863SEdward.Pilatowicz@Sun.COM 1287*8863SEdward.Pilatowicz@Sun.COM n = strlen(dir) + 1 + strlen(node) + 1; 1288*8863SEdward.Pilatowicz@Sun.COM path = kmem_zalloc(n, KM_SLEEP); 1289*8863SEdward.Pilatowicz@Sun.COM (void) strlcat(path, dir, n); 1290*8863SEdward.Pilatowicz@Sun.COM (void) strlcat(path, "/", n); 1291*8863SEdward.Pilatowicz@Sun.COM (void) strlcat(path, node, n); 1292*8863SEdward.Pilatowicz@Sun.COM ASSERT((strlen(path) + 1) == n); 1293*8863SEdward.Pilatowicz@Sun.COM 1294*8863SEdward.Pilatowicz@Sun.COM xxw_new = kmem_zalloc(sizeof (*xxw_new), KM_SLEEP); 1295*8863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_ref = 1; 1296*8863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_watch.node = path; 1297*8863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_watch.callback = i_xvdi_xb_watch_cb; 1298*8863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_watch.dev = (struct xenbus_device *)dip; 1299*8863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_xppd = pdp; 1300*8863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_cb = cb; 1301*8863SEdward.Pilatowicz@Sun.COM xxw_new->xxw_arg = arg; 1302*8863SEdward.Pilatowicz@Sun.COM 1303*8863SEdward.Pilatowicz@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 1304*8863SEdward.Pilatowicz@Sun.COM 1305*8863SEdward.Pilatowicz@Sun.COM /* 1306*8863SEdward.Pilatowicz@Sun.COM * If this is the first watch we're setting up, create a taskq 1307*8863SEdward.Pilatowicz@Sun.COM * to dispatch watch events and initialize the watch list. 1308*8863SEdward.Pilatowicz@Sun.COM */ 1309*8863SEdward.Pilatowicz@Sun.COM if (pdp->xd_xb_watch_taskq == NULL) { 1310*8863SEdward.Pilatowicz@Sun.COM char tq_name[TASKQ_NAMELEN]; 1311*8863SEdward.Pilatowicz@Sun.COM 1312*8863SEdward.Pilatowicz@Sun.COM ASSERT(list_is_empty(&pdp->xd_xb_watches)); 1313*8863SEdward.Pilatowicz@Sun.COM 1314*8863SEdward.Pilatowicz@Sun.COM (void) snprintf(tq_name, sizeof (tq_name), 1315*8863SEdward.Pilatowicz@Sun.COM "%s_xb_watch_tq", ddi_get_name(dip)); 1316*8863SEdward.Pilatowicz@Sun.COM 1317*8863SEdward.Pilatowicz@Sun.COM if ((pdp->xd_xb_watch_taskq = ddi_taskq_create(dip, tq_name, 1318*8863SEdward.Pilatowicz@Sun.COM 1, TASKQ_DEFAULTPRI, 0)) == NULL) { 1319*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxw_new); 1320*8863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1321*8863SEdward.Pilatowicz@Sun.COM return (DDI_FAILURE); 1322*8863SEdward.Pilatowicz@Sun.COM } 1323*8863SEdward.Pilatowicz@Sun.COM } 1324*8863SEdward.Pilatowicz@Sun.COM 1325*8863SEdward.Pilatowicz@Sun.COM /* Don't allow duplicate watches to be registered */ 1326*8863SEdward.Pilatowicz@Sun.COM for (xxwp = list_head(&pdp->xd_xb_watches); xxwp != NULL; 1327*8863SEdward.Pilatowicz@Sun.COM xxwp = list_next(&pdp->xd_xb_watches, xxwp)) { 1328*8863SEdward.Pilatowicz@Sun.COM 1329*8863SEdward.Pilatowicz@Sun.COM ASSERT(strcmp(xxwp->xxw_watch.node, path) != 0); 1330*8863SEdward.Pilatowicz@Sun.COM if (strcmp(xxwp->xxw_watch.node, path) != 0) 1331*8863SEdward.Pilatowicz@Sun.COM continue; 1332*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxw_new); 1333*8863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1334*8863SEdward.Pilatowicz@Sun.COM return (DDI_FAILURE); 1335*8863SEdward.Pilatowicz@Sun.COM } 1336*8863SEdward.Pilatowicz@Sun.COM 1337*8863SEdward.Pilatowicz@Sun.COM if (register_xenbus_watch(&xxw_new->xxw_watch) != 0) { 1338*8863SEdward.Pilatowicz@Sun.COM if (list_is_empty(&pdp->xd_xb_watches)) { 1339*8863SEdward.Pilatowicz@Sun.COM ddi_taskq_destroy(pdp->xd_xb_watch_taskq); 1340*8863SEdward.Pilatowicz@Sun.COM pdp->xd_xb_watch_taskq = NULL; 1341*8863SEdward.Pilatowicz@Sun.COM } 1342*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxw_new); 1343*8863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1344*8863SEdward.Pilatowicz@Sun.COM return (DDI_FAILURE); 1345*8863SEdward.Pilatowicz@Sun.COM } 1346*8863SEdward.Pilatowicz@Sun.COM 1347*8863SEdward.Pilatowicz@Sun.COM list_insert_head(&pdp->xd_xb_watches, xxw_new); 1348*8863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1349*8863SEdward.Pilatowicz@Sun.COM return (DDI_SUCCESS); 1350*8863SEdward.Pilatowicz@Sun.COM } 1351*8863SEdward.Pilatowicz@Sun.COM 1352*8863SEdward.Pilatowicz@Sun.COM /* 1353*8863SEdward.Pilatowicz@Sun.COM * Tear down all xenbus watches registered by the specified dip. 1354*8863SEdward.Pilatowicz@Sun.COM */ 1355*8863SEdward.Pilatowicz@Sun.COM void 1356*8863SEdward.Pilatowicz@Sun.COM xvdi_remove_xb_watch_handlers(dev_info_t *dip) 1357*8863SEdward.Pilatowicz@Sun.COM { 1358*8863SEdward.Pilatowicz@Sun.COM struct xendev_ppd *pdp = ddi_get_parent_data(dip); 1359*8863SEdward.Pilatowicz@Sun.COM xd_xb_watches_t *xxwp; 1360*8863SEdward.Pilatowicz@Sun.COM ddi_taskq_t *tq; 1361*8863SEdward.Pilatowicz@Sun.COM 1362*8863SEdward.Pilatowicz@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 1363*8863SEdward.Pilatowicz@Sun.COM 1364*8863SEdward.Pilatowicz@Sun.COM while ((xxwp = list_remove_head(&pdp->xd_xb_watches)) != NULL) { 1365*8863SEdward.Pilatowicz@Sun.COM unregister_xenbus_watch(&xxwp->xxw_watch); 1366*8863SEdward.Pilatowicz@Sun.COM i_xvdi_xb_watch_release(xxwp); 1367*8863SEdward.Pilatowicz@Sun.COM } 1368*8863SEdward.Pilatowicz@Sun.COM ASSERT(list_is_empty(&pdp->xd_xb_watches)); 1369*8863SEdward.Pilatowicz@Sun.COM 1370*8863SEdward.Pilatowicz@Sun.COM /* 1371*8863SEdward.Pilatowicz@Sun.COM * We can't hold xd_ndi_lk while we destroy the xd_xb_watch_taskq. 1372*8863SEdward.Pilatowicz@Sun.COM * This is because if there are currently any executing taskq threads, 1373*8863SEdward.Pilatowicz@Sun.COM * we will block until they are finished, and to finish they need 1374*8863SEdward.Pilatowicz@Sun.COM * to aquire xd_ndi_lk in i_xvdi_xb_watch_cb_tq() so they can release 1375*8863SEdward.Pilatowicz@Sun.COM * their reference on their corresponding xxwp structure. 1376*8863SEdward.Pilatowicz@Sun.COM */ 1377*8863SEdward.Pilatowicz@Sun.COM tq = pdp->xd_xb_watch_taskq; 1378*8863SEdward.Pilatowicz@Sun.COM pdp->xd_xb_watch_taskq = NULL; 1379*8863SEdward.Pilatowicz@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1380*8863SEdward.Pilatowicz@Sun.COM if (tq != NULL) 1381*8863SEdward.Pilatowicz@Sun.COM ddi_taskq_destroy(tq); 1382*8863SEdward.Pilatowicz@Sun.COM } 1383*8863SEdward.Pilatowicz@Sun.COM 13845084Sjohnlev static int 13855084Sjohnlev i_xvdi_add_watch_oestate(dev_info_t *dip) 13865084Sjohnlev { 13875084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 13885084Sjohnlev 13895084Sjohnlev ASSERT(pdp != NULL); 13905084Sjohnlev ASSERT(pdp->xd_xsdev.nodename != NULL); 13917756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk)); 13925084Sjohnlev 13935084Sjohnlev /* 13945084Sjohnlev * Create taskq for delivering other end state change event to 13955084Sjohnlev * this device later. 13965084Sjohnlev * 13975084Sjohnlev * Set nthreads to 1 to make sure that events can be delivered 13985084Sjohnlev * in order. 13995084Sjohnlev * 14005084Sjohnlev * Note: It is _not_ guaranteed that driver can see every 14015084Sjohnlev * xenstore change under the path that it is watching. If two 14025084Sjohnlev * changes happen consecutively in a very short amount of 14035084Sjohnlev * time, it is likely that the driver will see only the last 14045084Sjohnlev * one. 14055084Sjohnlev */ 14065084Sjohnlev if (pdp->xd_oe_taskq == NULL) 14075084Sjohnlev if ((pdp->xd_oe_taskq = ddi_taskq_create(dip, 14085084Sjohnlev "xendev_oe_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL) 14095084Sjohnlev return (DDI_FAILURE); 14105084Sjohnlev 14115084Sjohnlev /* 14125084Sjohnlev * Watch for changes to the XenbusState of otherend. 14135084Sjohnlev */ 14145084Sjohnlev pdp->xd_xsdev.otherend_state = XenbusStateUnknown; 14155084Sjohnlev pdp->xd_xsdev.otherend_changed = i_xvdi_oestate_cb; 14165084Sjohnlev 14175084Sjohnlev if (talk_to_otherend(&pdp->xd_xsdev) != 0) { 14185084Sjohnlev i_xvdi_rem_watch_oestate(dip); 14195084Sjohnlev return (DDI_FAILURE); 14205084Sjohnlev } 14215084Sjohnlev 14225084Sjohnlev return (DDI_SUCCESS); 14235084Sjohnlev } 14245084Sjohnlev 14255084Sjohnlev static void 14265084Sjohnlev i_xvdi_rem_watch_oestate(dev_info_t *dip) 14275084Sjohnlev { 14285084Sjohnlev struct xendev_ppd *pdp; 14295084Sjohnlev struct xenbus_device *dev; 14305084Sjohnlev 14315084Sjohnlev pdp = ddi_get_parent_data(dip); 14325084Sjohnlev ASSERT(pdp != NULL); 14337756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk)); 14345084Sjohnlev 14355084Sjohnlev dev = &pdp->xd_xsdev; 14365084Sjohnlev 14375084Sjohnlev /* Unwatch for changes to XenbusState of otherend */ 14385084Sjohnlev if (dev->otherend_watch.node != NULL) { 14397756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 14405084Sjohnlev unregister_xenbus_watch(&dev->otherend_watch); 14417756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 14425084Sjohnlev } 14435084Sjohnlev 14445084Sjohnlev /* make sure no event handler is running */ 14455084Sjohnlev if (pdp->xd_oe_taskq != NULL) { 14467756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 14475084Sjohnlev ddi_taskq_destroy(pdp->xd_oe_taskq); 14487756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 14495084Sjohnlev pdp->xd_oe_taskq = NULL; 14505084Sjohnlev } 14515084Sjohnlev 14525084Sjohnlev /* clean up */ 14535084Sjohnlev dev->otherend_state = XenbusStateUnknown; 14545084Sjohnlev dev->otherend_id = (domid_t)-1; 14555084Sjohnlev if (dev->otherend_watch.node != NULL) 14565084Sjohnlev kmem_free((void *)dev->otherend_watch.node, 14575084Sjohnlev strlen(dev->otherend_watch.node) + 1); 14585084Sjohnlev dev->otherend_watch.node = NULL; 14595084Sjohnlev if (dev->otherend != NULL) 14605084Sjohnlev kmem_free((void *)dev->otherend, strlen(dev->otherend) + 1); 14615084Sjohnlev dev->otherend = NULL; 14625084Sjohnlev } 14635084Sjohnlev 14645084Sjohnlev static int 14655084Sjohnlev i_xvdi_add_watch_hpstate(dev_info_t *dip) 14665084Sjohnlev { 14675084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 14685084Sjohnlev 14695084Sjohnlev ASSERT(pdp != NULL); 14705084Sjohnlev ASSERT(pdp->xd_xsdev.frontend == 0); 14717756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk)); 14725084Sjohnlev 14735084Sjohnlev /* 14745084Sjohnlev * Create taskq for delivering hotplug status change event to 14755084Sjohnlev * this device later. 14765084Sjohnlev * 14775084Sjohnlev * Set nthreads to 1 to make sure that events can be delivered 14785084Sjohnlev * in order. 14795084Sjohnlev * 14805084Sjohnlev * Note: It is _not_ guaranteed that driver can see every 14815084Sjohnlev * hotplug status change under the path that it is 14825084Sjohnlev * watching. If two changes happen consecutively in a very 14835084Sjohnlev * short amount of time, it is likely that the driver only 14845084Sjohnlev * sees the last one. 14855084Sjohnlev */ 14865084Sjohnlev if (pdp->xd_hp_taskq == NULL) 14875084Sjohnlev if ((pdp->xd_hp_taskq = ddi_taskq_create(dip, 14885084Sjohnlev "xendev_hp_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL) 14895084Sjohnlev return (DDI_FAILURE); 14905084Sjohnlev 14915084Sjohnlev if (pdp->xd_hp_watch.node == NULL) { 14925084Sjohnlev size_t len; 14935084Sjohnlev char *path; 14945084Sjohnlev 14955084Sjohnlev ASSERT(pdp->xd_xsdev.nodename != NULL); 14965084Sjohnlev 14975084Sjohnlev len = strlen(pdp->xd_xsdev.nodename) + 14985084Sjohnlev strlen("/hotplug-status") + 1; 14995084Sjohnlev path = kmem_alloc(len, KM_SLEEP); 15005084Sjohnlev (void) snprintf(path, len, "%s/hotplug-status", 15015084Sjohnlev pdp->xd_xsdev.nodename); 15025084Sjohnlev 15035084Sjohnlev pdp->xd_hp_watch.node = path; 15045084Sjohnlev pdp->xd_hp_watch.callback = i_xvdi_hpstate_cb; 15055084Sjohnlev pdp->xd_hp_watch.dev = (struct xenbus_device *)dip; /* yuck! */ 15065084Sjohnlev if (register_xenbus_watch(&pdp->xd_hp_watch) != 0) { 15075084Sjohnlev i_xvdi_rem_watch_hpstate(dip); 15085084Sjohnlev return (DDI_FAILURE); 15095084Sjohnlev } 15105084Sjohnlev } 15115084Sjohnlev 15125084Sjohnlev return (DDI_SUCCESS); 15135084Sjohnlev } 15145084Sjohnlev 15155084Sjohnlev static void 15165084Sjohnlev i_xvdi_rem_watch_hpstate(dev_info_t *dip) 15175084Sjohnlev { 15185084Sjohnlev struct xendev_ppd *pdp; 15195084Sjohnlev pdp = ddi_get_parent_data(dip); 15205084Sjohnlev 15215084Sjohnlev ASSERT(pdp != NULL); 15225084Sjohnlev ASSERT(pdp->xd_xsdev.frontend == 0); 15237756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk)); 15245084Sjohnlev 15255084Sjohnlev /* Unwatch for changes to "hotplug-status" node for backend device. */ 15265084Sjohnlev if (pdp->xd_hp_watch.node != NULL) { 15277756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 15285084Sjohnlev unregister_xenbus_watch(&pdp->xd_hp_watch); 15297756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 15305084Sjohnlev } 15315084Sjohnlev 15325084Sjohnlev /* Make sure no event handler is running. */ 15335084Sjohnlev if (pdp->xd_hp_taskq != NULL) { 15347756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 15355084Sjohnlev ddi_taskq_destroy(pdp->xd_hp_taskq); 15367756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 15375084Sjohnlev pdp->xd_hp_taskq = NULL; 15385084Sjohnlev } 15395084Sjohnlev 15405084Sjohnlev /* Clean up. */ 15415084Sjohnlev if (pdp->xd_hp_watch.node != NULL) { 15425084Sjohnlev kmem_free((void *)pdp->xd_hp_watch.node, 15435084Sjohnlev strlen(pdp->xd_hp_watch.node) + 1); 15445084Sjohnlev pdp->xd_hp_watch.node = NULL; 15455084Sjohnlev } 15465084Sjohnlev } 15475084Sjohnlev 15485084Sjohnlev static int 15495084Sjohnlev i_xvdi_add_watches(dev_info_t *dip) 15505084Sjohnlev { 15515084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 15525084Sjohnlev 15535084Sjohnlev ASSERT(pdp != NULL); 15545084Sjohnlev 15557756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 15565084Sjohnlev 15575084Sjohnlev if (i_xvdi_add_watch_oestate(dip) != DDI_SUCCESS) { 15587756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 15595084Sjohnlev return (DDI_FAILURE); 15605084Sjohnlev } 15615084Sjohnlev 15625084Sjohnlev if (pdp->xd_xsdev.frontend == 1) { 15635084Sjohnlev /* 15645084Sjohnlev * Frontend devices must watch for the backend path 15655084Sjohnlev * changing. 15665084Sjohnlev */ 15675084Sjohnlev if (i_xvdi_add_watch_bepath(dip) != DDI_SUCCESS) 15685084Sjohnlev goto unwatch_and_fail; 15695084Sjohnlev } else { 15705084Sjohnlev /* 15715084Sjohnlev * Backend devices must watch for hotplug events. 15725084Sjohnlev */ 15735084Sjohnlev if (i_xvdi_add_watch_hpstate(dip) != DDI_SUCCESS) 15745084Sjohnlev goto unwatch_and_fail; 15755084Sjohnlev } 15765084Sjohnlev 15777756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 15785084Sjohnlev 15795084Sjohnlev return (DDI_SUCCESS); 15805084Sjohnlev 15815084Sjohnlev unwatch_and_fail: 15825084Sjohnlev i_xvdi_rem_watch_oestate(dip); 15837756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 15845084Sjohnlev 15855084Sjohnlev return (DDI_FAILURE); 15865084Sjohnlev } 15875084Sjohnlev 15885084Sjohnlev static void 15895084Sjohnlev i_xvdi_rem_watches(dev_info_t *dip) 15905084Sjohnlev { 15915084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 15925084Sjohnlev 15935084Sjohnlev ASSERT(pdp != NULL); 15945084Sjohnlev 15957756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 15965084Sjohnlev 15975084Sjohnlev i_xvdi_rem_watch_oestate(dip); 15985084Sjohnlev 15995084Sjohnlev if (pdp->xd_xsdev.frontend == 1) 16005084Sjohnlev i_xvdi_rem_watch_bepath(dip); 16015084Sjohnlev else 16025084Sjohnlev i_xvdi_rem_watch_hpstate(dip); 16035084Sjohnlev 16047756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 1605*8863SEdward.Pilatowicz@Sun.COM 1606*8863SEdward.Pilatowicz@Sun.COM xvdi_remove_xb_watch_handlers(dip); 16075084Sjohnlev } 16085084Sjohnlev 16095084Sjohnlev static int 16105084Sjohnlev i_xvdi_add_watch_bepath(dev_info_t *dip) 16115084Sjohnlev { 16125084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 16135084Sjohnlev 16145084Sjohnlev ASSERT(pdp != NULL); 16155084Sjohnlev ASSERT(pdp->xd_xsdev.frontend == 1); 16165084Sjohnlev 16175084Sjohnlev /* 16185084Sjohnlev * Frontend devices need to watch for the backend path changing. 16195084Sjohnlev */ 16205084Sjohnlev if (pdp->xd_bepath_watch.node == NULL) { 16215084Sjohnlev size_t len; 16225084Sjohnlev char *path; 16235084Sjohnlev 16245084Sjohnlev ASSERT(pdp->xd_xsdev.nodename != NULL); 16255084Sjohnlev 16265084Sjohnlev len = strlen(pdp->xd_xsdev.nodename) + strlen("/backend") + 1; 16275084Sjohnlev path = kmem_alloc(len, KM_SLEEP); 16285084Sjohnlev (void) snprintf(path, len, "%s/backend", 16295084Sjohnlev pdp->xd_xsdev.nodename); 16305084Sjohnlev 16315084Sjohnlev pdp->xd_bepath_watch.node = path; 16325084Sjohnlev pdp->xd_bepath_watch.callback = i_xvdi_bepath_cb; 16335084Sjohnlev pdp->xd_bepath_watch.dev = (struct xenbus_device *)dip; 16345084Sjohnlev if (register_xenbus_watch(&pdp->xd_bepath_watch) != 0) { 16355084Sjohnlev kmem_free(path, len); 16365084Sjohnlev pdp->xd_bepath_watch.node = NULL; 16375084Sjohnlev return (DDI_FAILURE); 16385084Sjohnlev } 16395084Sjohnlev } 16405084Sjohnlev 16415084Sjohnlev return (DDI_SUCCESS); 16425084Sjohnlev } 16435084Sjohnlev 16445084Sjohnlev static void 16455084Sjohnlev i_xvdi_rem_watch_bepath(dev_info_t *dip) 16465084Sjohnlev { 16475084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 16485084Sjohnlev 16495084Sjohnlev ASSERT(pdp != NULL); 16505084Sjohnlev ASSERT(pdp->xd_xsdev.frontend == 1); 16517756SMark.Johnson@Sun.COM ASSERT(mutex_owned(&pdp->xd_ndi_lk)); 16525084Sjohnlev 16535084Sjohnlev if (pdp->xd_bepath_watch.node != NULL) { 16547756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 16555084Sjohnlev unregister_xenbus_watch(&pdp->xd_bepath_watch); 16567756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 16575084Sjohnlev 16585084Sjohnlev kmem_free((void *)(pdp->xd_bepath_watch.node), 16595084Sjohnlev strlen(pdp->xd_bepath_watch.node) + 1); 16605084Sjohnlev pdp->xd_bepath_watch.node = NULL; 16615084Sjohnlev } 16625084Sjohnlev } 16635084Sjohnlev 16645084Sjohnlev int 16655084Sjohnlev xvdi_switch_state(dev_info_t *dip, xenbus_transaction_t xbt, 16665084Sjohnlev XenbusState newState) 16675084Sjohnlev { 16685084Sjohnlev int rv; 16695084Sjohnlev struct xendev_ppd *pdp; 16705084Sjohnlev 16715084Sjohnlev pdp = ddi_get_parent_data(dip); 16725084Sjohnlev ASSERT(pdp != NULL); 16735084Sjohnlev 16745084Sjohnlev XVDI_DPRINTF(XVDI_DBG_STATE, 16757679SMax.Zhen@Sun.COM "xvdi_switch_state: %s@%s's xenbus state moves to %d\n", 16767679SMax.Zhen@Sun.COM ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip), 16777679SMax.Zhen@Sun.COM ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip), 16787679SMax.Zhen@Sun.COM newState); 16795084Sjohnlev 16805084Sjohnlev rv = xenbus_switch_state(&pdp->xd_xsdev, xbt, newState); 16815084Sjohnlev if (rv > 0) 16825084Sjohnlev cmn_err(CE_WARN, "xvdi_switch_state: change state failed"); 16835084Sjohnlev 16845084Sjohnlev return (rv); 16855084Sjohnlev } 16865084Sjohnlev 16875084Sjohnlev /* 16885084Sjohnlev * Notify hotplug script running in userland 16895084Sjohnlev */ 16905084Sjohnlev int 16915084Sjohnlev xvdi_post_event(dev_info_t *dip, xendev_hotplug_cmd_t hpc) 16925084Sjohnlev { 16935084Sjohnlev struct xendev_ppd *pdp; 16945084Sjohnlev nvlist_t *attr_list = NULL; 16955084Sjohnlev i_xd_cfg_t *xdcp; 16965084Sjohnlev sysevent_id_t eid; 16975084Sjohnlev int err; 16985084Sjohnlev char devname[256]; /* XXPV dme: ? */ 16995084Sjohnlev 17005084Sjohnlev pdp = ddi_get_parent_data(dip); 17015084Sjohnlev ASSERT(pdp != NULL); 17025084Sjohnlev 17035084Sjohnlev xdcp = i_xvdi_devclass2cfg(pdp->xd_devclass); 17045084Sjohnlev ASSERT(xdcp != NULL); 17055084Sjohnlev 17065084Sjohnlev (void) snprintf(devname, sizeof (devname) - 1, "%s%d", 17075084Sjohnlev ddi_driver_name(dip), ddi_get_instance(dip)); 17085084Sjohnlev 17095084Sjohnlev err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME, KM_NOSLEEP); 17105084Sjohnlev if (err != DDI_SUCCESS) 17115084Sjohnlev goto failure; 17125084Sjohnlev 17135084Sjohnlev err = nvlist_add_int32(attr_list, "domain", pdp->xd_domain); 17145084Sjohnlev if (err != DDI_SUCCESS) 17155084Sjohnlev goto failure; 17165084Sjohnlev err = nvlist_add_int32(attr_list, "vdev", pdp->xd_vdevnum); 17175084Sjohnlev if (err != DDI_SUCCESS) 17185084Sjohnlev goto failure; 17195084Sjohnlev err = nvlist_add_string(attr_list, "devclass", xdcp->xsdev); 17205084Sjohnlev if (err != DDI_SUCCESS) 17215084Sjohnlev goto failure; 17225084Sjohnlev err = nvlist_add_string(attr_list, "device", devname); 17235084Sjohnlev if (err != DDI_SUCCESS) 17245084Sjohnlev goto failure; 17255084Sjohnlev err = nvlist_add_string(attr_list, "fob", 17265084Sjohnlev ((pdp->xd_xsdev.frontend == 1) ? "frontend" : "backend")); 17275084Sjohnlev if (err != DDI_SUCCESS) 17285084Sjohnlev goto failure; 17295084Sjohnlev 17305084Sjohnlev switch (hpc) { 17315084Sjohnlev case XEN_HP_ADD: 17325084Sjohnlev err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev", 17335084Sjohnlev "add", attr_list, &eid, DDI_NOSLEEP); 17345084Sjohnlev break; 17355084Sjohnlev case XEN_HP_REMOVE: 17365084Sjohnlev err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev", 17375084Sjohnlev "remove", attr_list, &eid, DDI_NOSLEEP); 17385084Sjohnlev break; 17395084Sjohnlev default: 17405084Sjohnlev err = DDI_FAILURE; 17415084Sjohnlev goto failure; 17425084Sjohnlev } 17435084Sjohnlev 17445084Sjohnlev failure: 17455084Sjohnlev if (attr_list != NULL) 17465084Sjohnlev nvlist_free(attr_list); 17475084Sjohnlev 17485084Sjohnlev return (err); 17495084Sjohnlev } 17505084Sjohnlev 17515084Sjohnlev /* ARGSUSED */ 17525084Sjohnlev static void 17535084Sjohnlev i_xvdi_probe_path_cb(struct xenbus_watch *w, const char **vec, 17545084Sjohnlev unsigned int len) 17555084Sjohnlev { 17565084Sjohnlev char *path; 17575084Sjohnlev 17585084Sjohnlev if (xendev_dip == NULL) 17595084Sjohnlev xendev_dip = ddi_find_devinfo("xpvd", -1, 0); 17605084Sjohnlev 17615084Sjohnlev path = i_ddi_strdup((char *)vec[XS_WATCH_PATH], KM_SLEEP); 17625084Sjohnlev 17635084Sjohnlev (void) ddi_taskq_dispatch(DEVI(xendev_dip)->devi_taskq, 17645084Sjohnlev i_xvdi_probe_path_handler, (void *)path, DDI_SLEEP); 17655084Sjohnlev } 17665084Sjohnlev 17675084Sjohnlev static void 17685084Sjohnlev i_xvdi_watch_device(char *path) 17695084Sjohnlev { 17705084Sjohnlev struct xenbus_watch *w; 17715084Sjohnlev 17725084Sjohnlev ASSERT(path != NULL); 17735084Sjohnlev 17745084Sjohnlev w = kmem_zalloc(sizeof (*w), KM_SLEEP); 17755084Sjohnlev w->node = path; 17765084Sjohnlev w->callback = &i_xvdi_probe_path_cb; 17775084Sjohnlev w->dev = NULL; 17785084Sjohnlev 17795084Sjohnlev if (register_xenbus_watch(w) != 0) { 17805084Sjohnlev cmn_err(CE_WARN, "i_xvdi_watch_device: " 17815084Sjohnlev "cannot set watch on %s", path); 17825084Sjohnlev kmem_free(w, sizeof (*w)); 17835084Sjohnlev return; 17845084Sjohnlev } 17855084Sjohnlev } 17865084Sjohnlev 17875084Sjohnlev void 17885084Sjohnlev xvdi_watch_devices(int newstate) 17895084Sjohnlev { 17905084Sjohnlev int devclass; 17915084Sjohnlev 17925084Sjohnlev /* 17935084Sjohnlev * Watch for devices being created in the store. 17945084Sjohnlev */ 17955084Sjohnlev if (newstate == XENSTORE_DOWN) 17965084Sjohnlev return; 17975084Sjohnlev for (devclass = 0; devclass < NXDC; devclass++) { 17985084Sjohnlev if (xdci[devclass].xs_path_fe != NULL) 17995084Sjohnlev i_xvdi_watch_device(xdci[devclass].xs_path_fe); 18005084Sjohnlev if (xdci[devclass].xs_path_be != NULL) 18015084Sjohnlev i_xvdi_watch_device(xdci[devclass].xs_path_be); 18025084Sjohnlev } 18035084Sjohnlev } 18045084Sjohnlev 18055084Sjohnlev /* 18065084Sjohnlev * Iterate over the store looking for backend devices to create. 18075084Sjohnlev */ 18085084Sjohnlev static void 18095084Sjohnlev i_xvdi_enum_be(dev_info_t *parent, i_xd_cfg_t *xdcp) 18105084Sjohnlev { 18115084Sjohnlev char **domains; 18125084Sjohnlev unsigned int ndomains; 18135084Sjohnlev int ldomains, i; 18145084Sjohnlev 18155084Sjohnlev if ((domains = xenbus_directory(XBT_NULL, xdcp->xs_path_be, "", 18165084Sjohnlev &ndomains)) == NULL) 18175084Sjohnlev return; 18185084Sjohnlev 18195084Sjohnlev for (i = 0, ldomains = 0; i < ndomains; i++) { 18205084Sjohnlev ldomains += strlen(domains[i]) + 1 + sizeof (char *); 18215084Sjohnlev 18225084Sjohnlev i_xvdi_enum_worker(parent, xdcp, domains[i]); 18235084Sjohnlev } 18245084Sjohnlev kmem_free(domains, ldomains); 18255084Sjohnlev } 18265084Sjohnlev 18275084Sjohnlev /* 18285084Sjohnlev * Iterate over the store looking for frontend devices to create. 18295084Sjohnlev */ 18305084Sjohnlev static void 18315084Sjohnlev i_xvdi_enum_fe(dev_info_t *parent, i_xd_cfg_t *xdcp) 18325084Sjohnlev { 18335084Sjohnlev i_xvdi_enum_worker(parent, xdcp, NULL); 18345084Sjohnlev } 18355084Sjohnlev 18365084Sjohnlev static void 18375084Sjohnlev i_xvdi_enum_worker(dev_info_t *parent, i_xd_cfg_t *xdcp, 18385084Sjohnlev char *domain) 18395084Sjohnlev { 18405084Sjohnlev char *path, *domain_path, *ep; 18415084Sjohnlev char **devices; 18425084Sjohnlev unsigned int ndevices; 18435084Sjohnlev int ldevices, j, circ; 18445084Sjohnlev domid_t dom; 18456460Sedp long tmplong; 18465084Sjohnlev 18475084Sjohnlev if (domain == NULL) { 18485084Sjohnlev dom = DOMID_SELF; 18495084Sjohnlev path = xdcp->xs_path_fe; 18505084Sjohnlev domain_path = ""; 18515084Sjohnlev } else { 18526460Sedp (void) ddi_strtol(domain, &ep, 0, &tmplong); 18536460Sedp dom = tmplong; 18545084Sjohnlev path = xdcp->xs_path_be; 18555084Sjohnlev domain_path = domain; 18565084Sjohnlev } 18575084Sjohnlev 18585084Sjohnlev if ((devices = xenbus_directory(XBT_NULL, path, domain_path, 18595084Sjohnlev &ndevices)) == NULL) 18605084Sjohnlev return; 18615084Sjohnlev 18625084Sjohnlev for (j = 0, ldevices = 0; j < ndevices; j++) { 18635084Sjohnlev int vdev; 18645084Sjohnlev 18655084Sjohnlev ldevices += strlen(devices[j]) + 1 + sizeof (char *); 18666460Sedp (void) ddi_strtol(devices[j], &ep, 0, &tmplong); 18676460Sedp vdev = tmplong; 18685084Sjohnlev 18695084Sjohnlev ndi_devi_enter(parent, &circ); 18705084Sjohnlev 18716460Sedp if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL) 18725084Sjohnlev (void) xvdi_create_dev(parent, xdcp->devclass, 18735084Sjohnlev dom, vdev); 18745084Sjohnlev 18755084Sjohnlev ndi_devi_exit(parent, circ); 18765084Sjohnlev } 18775084Sjohnlev kmem_free(devices, ldevices); 18785084Sjohnlev } 18795084Sjohnlev 18805084Sjohnlev /* 18815084Sjohnlev * Leaf drivers should call this in their detach() routine during suspend. 18825084Sjohnlev */ 18835084Sjohnlev void 18845084Sjohnlev xvdi_suspend(dev_info_t *dip) 18855084Sjohnlev { 18865084Sjohnlev i_xvdi_rem_watches(dip); 18875084Sjohnlev } 18885084Sjohnlev 18895084Sjohnlev /* 18905084Sjohnlev * Leaf drivers should call this in their attach() routine during resume. 18915084Sjohnlev */ 18925084Sjohnlev int 18935084Sjohnlev xvdi_resume(dev_info_t *dip) 18945084Sjohnlev { 18955084Sjohnlev return (i_xvdi_add_watches(dip)); 18965084Sjohnlev } 18975084Sjohnlev 18985084Sjohnlev /* 18995084Sjohnlev * Add event handler for the leaf driver 19005084Sjohnlev * to handle event triggered by the change in xenstore 19015084Sjohnlev */ 19025084Sjohnlev int 19035084Sjohnlev xvdi_add_event_handler(dev_info_t *dip, char *name, 19047756SMark.Johnson@Sun.COM void (*evthandler)(dev_info_t *, ddi_eventcookie_t, void *, void *), 19057756SMark.Johnson@Sun.COM void *arg) 19065084Sjohnlev { 19075084Sjohnlev ddi_eventcookie_t ecv; 19085084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 19095084Sjohnlev ddi_callback_id_t *cbid; 19107756SMark.Johnson@Sun.COM boolean_t call_handler; 19117756SMark.Johnson@Sun.COM i_oestate_evt_t *evt = NULL; 19127756SMark.Johnson@Sun.COM XenbusState oestate; 19135084Sjohnlev 19145084Sjohnlev ASSERT(pdp != NULL); 19155084Sjohnlev 19167756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 19175084Sjohnlev 19185084Sjohnlev if (strcmp(name, XS_OE_STATE) == 0) { 19195084Sjohnlev ASSERT(pdp->xd_xsdev.otherend != NULL); 19205084Sjohnlev 19215084Sjohnlev cbid = &pdp->xd_oe_ehid; 19225084Sjohnlev } else if (strcmp(name, XS_HP_STATE) == 0) { 19235084Sjohnlev if (pdp->xd_xsdev.frontend == 1) { 19247756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 19255084Sjohnlev return (DDI_FAILURE); 19265084Sjohnlev } 19275084Sjohnlev 19285084Sjohnlev ASSERT(pdp->xd_hp_watch.node != NULL); 19295084Sjohnlev 19305084Sjohnlev cbid = &pdp->xd_hp_ehid; 19315084Sjohnlev } else { 19325084Sjohnlev /* Unsupported watch. */ 19337756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 19345084Sjohnlev return (DDI_FAILURE); 19355084Sjohnlev } 19365084Sjohnlev 19375084Sjohnlev /* 19385084Sjohnlev * No event handler provided, take default action to handle 19395084Sjohnlev * event. 19405084Sjohnlev */ 19415084Sjohnlev if (evthandler == NULL) { 19427756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 19435084Sjohnlev return (DDI_SUCCESS); 19445084Sjohnlev } 19455084Sjohnlev 19465084Sjohnlev ASSERT(*cbid == NULL); 19475084Sjohnlev 19485084Sjohnlev if (ddi_get_eventcookie(dip, name, &ecv) != DDI_SUCCESS) { 19495084Sjohnlev cmn_err(CE_WARN, "failed to find %s cookie for %s@%s", 19505084Sjohnlev name, ddi_get_name(dip), ddi_get_name_addr(dip)); 19517756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 19525084Sjohnlev return (DDI_FAILURE); 19535084Sjohnlev } 19547756SMark.Johnson@Sun.COM if (ddi_add_event_handler(dip, ecv, evthandler, arg, cbid) 19555084Sjohnlev != DDI_SUCCESS) { 19565084Sjohnlev cmn_err(CE_WARN, "failed to add %s event handler for %s@%s", 19575084Sjohnlev name, ddi_get_name(dip), ddi_get_name_addr(dip)); 19585084Sjohnlev *cbid = NULL; 19597756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 19605084Sjohnlev return (DDI_FAILURE); 19615084Sjohnlev } 19625084Sjohnlev 19637756SMark.Johnson@Sun.COM /* 19647756SMark.Johnson@Sun.COM * if we're adding an oe state callback, and the ring has already 19657756SMark.Johnson@Sun.COM * transitioned out of Unknown, call the handler after we release 19667756SMark.Johnson@Sun.COM * the mutex. 19677756SMark.Johnson@Sun.COM */ 19687756SMark.Johnson@Sun.COM call_handler = B_FALSE; 19697756SMark.Johnson@Sun.COM if ((strcmp(name, XS_OE_STATE) == 0) && 19707756SMark.Johnson@Sun.COM (pdp->xd_xsdev.otherend_state != XenbusStateUnknown)) { 19717756SMark.Johnson@Sun.COM oestate = pdp->xd_xsdev.otherend_state; 19727756SMark.Johnson@Sun.COM call_handler = B_TRUE; 19737756SMark.Johnson@Sun.COM } 19747756SMark.Johnson@Sun.COM 19757756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 19767756SMark.Johnson@Sun.COM 19777756SMark.Johnson@Sun.COM if (call_handler) { 19787756SMark.Johnson@Sun.COM evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP); 19797756SMark.Johnson@Sun.COM evt->dip = dip; 19807756SMark.Johnson@Sun.COM evt->state = oestate; 19817756SMark.Johnson@Sun.COM (void) ddi_taskq_dispatch(pdp->xd_oe_taskq, 19827756SMark.Johnson@Sun.COM i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP); 19837756SMark.Johnson@Sun.COM } 19845084Sjohnlev 19855084Sjohnlev return (DDI_SUCCESS); 19865084Sjohnlev } 19875084Sjohnlev 19885084Sjohnlev /* 19895084Sjohnlev * Remove event handler for the leaf driver and unwatch xenstore 19905084Sjohnlev * so, driver will not be notified when xenstore entry changed later 19915084Sjohnlev */ 19925084Sjohnlev void 19935084Sjohnlev xvdi_remove_event_handler(dev_info_t *dip, char *name) 19945084Sjohnlev { 19955084Sjohnlev struct xendev_ppd *pdp; 19965084Sjohnlev boolean_t rem_oe = B_FALSE, rem_hp = B_FALSE; 19975084Sjohnlev ddi_callback_id_t oeid = NULL, hpid = NULL; 19985084Sjohnlev 19995084Sjohnlev pdp = ddi_get_parent_data(dip); 20005084Sjohnlev ASSERT(pdp != NULL); 20015084Sjohnlev 20025084Sjohnlev if (name == NULL) { 20035084Sjohnlev rem_oe = B_TRUE; 20045084Sjohnlev rem_hp = B_TRUE; 20055084Sjohnlev } else if (strcmp(name, XS_OE_STATE) == 0) { 20065084Sjohnlev rem_oe = B_TRUE; 20075084Sjohnlev } else if (strcmp(name, XS_HP_STATE) == 0) { 20085084Sjohnlev rem_hp = B_TRUE; 20095084Sjohnlev } else { 20105084Sjohnlev cmn_err(CE_WARN, "event %s not supported, cannot remove", name); 20115084Sjohnlev return; 20125084Sjohnlev } 20135084Sjohnlev 20147756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 20155084Sjohnlev 20165084Sjohnlev if (rem_oe && (pdp->xd_oe_ehid != NULL)) { 20175084Sjohnlev oeid = pdp->xd_oe_ehid; 20185084Sjohnlev pdp->xd_oe_ehid = NULL; 20195084Sjohnlev } 20205084Sjohnlev 20215084Sjohnlev if (rem_hp && (pdp->xd_hp_ehid != NULL)) { 20225084Sjohnlev hpid = pdp->xd_hp_ehid; 20235084Sjohnlev pdp->xd_hp_ehid = NULL; 20245084Sjohnlev } 20255084Sjohnlev 20267756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 20275084Sjohnlev 20285084Sjohnlev if (oeid != NULL) 20295084Sjohnlev (void) ddi_remove_event_handler(oeid); 20305084Sjohnlev if (hpid != NULL) 20315084Sjohnlev (void) ddi_remove_event_handler(hpid); 20325084Sjohnlev } 20335084Sjohnlev 20345084Sjohnlev 20355084Sjohnlev /* 20365084Sjohnlev * common ring interfaces 20375084Sjohnlev */ 20385084Sjohnlev 20395084Sjohnlev #define FRONT_RING(_ringp) (&(_ringp)->xr_sring.fr) 20405084Sjohnlev #define BACK_RING(_ringp) (&(_ringp)->xr_sring.br) 20415084Sjohnlev #define GET_RING_SIZE(_ringp) RING_SIZE(FRONT_RING(ringp)) 20425084Sjohnlev #define GET_RING_ENTRY_FE(_ringp, _idx) \ 20435084Sjohnlev (FRONT_RING(_ringp)->sring->ring + \ 20445084Sjohnlev (_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1))) 20455084Sjohnlev #define GET_RING_ENTRY_BE(_ringp, _idx) \ 20465084Sjohnlev (BACK_RING(_ringp)->sring->ring + \ 20475084Sjohnlev (_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1))) 20485084Sjohnlev 20495084Sjohnlev unsigned int 20505084Sjohnlev xvdi_ring_avail_slots(xendev_ring_t *ringp) 20515084Sjohnlev { 20525084Sjohnlev comif_ring_fe_t *frp; 20535084Sjohnlev comif_ring_be_t *brp; 20545084Sjohnlev 20555084Sjohnlev if (ringp->xr_frontend) { 20565084Sjohnlev frp = FRONT_RING(ringp); 20575084Sjohnlev return (GET_RING_SIZE(ringp) - 20585084Sjohnlev (frp->req_prod_pvt - frp->rsp_cons)); 20595084Sjohnlev } else { 20605084Sjohnlev brp = BACK_RING(ringp); 20615084Sjohnlev return (GET_RING_SIZE(ringp) - 20625084Sjohnlev (brp->rsp_prod_pvt - brp->req_cons)); 20635084Sjohnlev } 20645084Sjohnlev } 20655084Sjohnlev 20665084Sjohnlev int 20675084Sjohnlev xvdi_ring_has_unconsumed_requests(xendev_ring_t *ringp) 20685084Sjohnlev { 20695084Sjohnlev comif_ring_be_t *brp; 20705084Sjohnlev 20715084Sjohnlev ASSERT(!ringp->xr_frontend); 20725084Sjohnlev brp = BACK_RING(ringp); 20735084Sjohnlev return ((brp->req_cons != 20745084Sjohnlev ddi_get32(ringp->xr_acc_hdl, &brp->sring->req_prod)) && 20755084Sjohnlev ((brp->req_cons - brp->rsp_prod_pvt) != RING_SIZE(brp))); 20765084Sjohnlev } 20775084Sjohnlev 20785084Sjohnlev int 20795084Sjohnlev xvdi_ring_has_incomp_request(xendev_ring_t *ringp) 20805084Sjohnlev { 20815084Sjohnlev comif_ring_fe_t *frp; 20825084Sjohnlev 20835084Sjohnlev ASSERT(ringp->xr_frontend); 20845084Sjohnlev frp = FRONT_RING(ringp); 20855084Sjohnlev return (frp->req_prod_pvt != 20865084Sjohnlev ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod)); 20875084Sjohnlev } 20885084Sjohnlev 20895084Sjohnlev int 20905084Sjohnlev xvdi_ring_has_unconsumed_responses(xendev_ring_t *ringp) 20915084Sjohnlev { 20925084Sjohnlev comif_ring_fe_t *frp; 20935084Sjohnlev 20945084Sjohnlev ASSERT(ringp->xr_frontend); 20955084Sjohnlev frp = FRONT_RING(ringp); 20965084Sjohnlev return (frp->rsp_cons != 20975084Sjohnlev ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod)); 20985084Sjohnlev } 20995084Sjohnlev 21005084Sjohnlev /* NOTE: req_event will be increased as needed */ 21015084Sjohnlev void * 21025084Sjohnlev xvdi_ring_get_request(xendev_ring_t *ringp) 21035084Sjohnlev { 21045084Sjohnlev comif_ring_fe_t *frp; 21055084Sjohnlev comif_ring_be_t *brp; 21065084Sjohnlev 21075084Sjohnlev if (ringp->xr_frontend) { 21085084Sjohnlev /* for frontend ring */ 21095084Sjohnlev frp = FRONT_RING(ringp); 21105084Sjohnlev if (!RING_FULL(frp)) 21115084Sjohnlev return (GET_RING_ENTRY_FE(ringp, frp->req_prod_pvt++)); 21125084Sjohnlev else 21135084Sjohnlev return (NULL); 21145084Sjohnlev } else { 21155084Sjohnlev /* for backend ring */ 21165084Sjohnlev brp = BACK_RING(ringp); 21175084Sjohnlev /* RING_FINAL_CHECK_FOR_REQUESTS() */ 21185084Sjohnlev if (xvdi_ring_has_unconsumed_requests(ringp)) 21195084Sjohnlev return (GET_RING_ENTRY_BE(ringp, brp->req_cons++)); 21205084Sjohnlev else { 21215084Sjohnlev ddi_put32(ringp->xr_acc_hdl, &brp->sring->req_event, 21225084Sjohnlev brp->req_cons + 1); 21235084Sjohnlev membar_enter(); 21245084Sjohnlev if (xvdi_ring_has_unconsumed_requests(ringp)) 21255084Sjohnlev return (GET_RING_ENTRY_BE(ringp, 21265084Sjohnlev brp->req_cons++)); 21275084Sjohnlev else 21285084Sjohnlev return (NULL); 21295084Sjohnlev } 21305084Sjohnlev } 21315084Sjohnlev } 21325084Sjohnlev 21335084Sjohnlev int 21345084Sjohnlev xvdi_ring_push_request(xendev_ring_t *ringp) 21355084Sjohnlev { 21365084Sjohnlev RING_IDX old, new, reqevt; 21375084Sjohnlev comif_ring_fe_t *frp; 21385084Sjohnlev 21395084Sjohnlev /* only frontend should be able to push request */ 21405084Sjohnlev ASSERT(ringp->xr_frontend); 21415084Sjohnlev 21425084Sjohnlev /* RING_PUSH_REQUEST_AND_CHECK_NOTIFY() */ 21435084Sjohnlev frp = FRONT_RING(ringp); 21445084Sjohnlev old = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_prod); 21455084Sjohnlev new = frp->req_prod_pvt; 21465084Sjohnlev ddi_put32(ringp->xr_acc_hdl, &frp->sring->req_prod, new); 21475084Sjohnlev membar_enter(); 21485084Sjohnlev reqevt = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_event); 21495084Sjohnlev return ((RING_IDX)(new - reqevt) < (RING_IDX)(new - old)); 21505084Sjohnlev } 21515084Sjohnlev 21525084Sjohnlev /* NOTE: rsp_event will be increased as needed */ 21535084Sjohnlev void * 21545084Sjohnlev xvdi_ring_get_response(xendev_ring_t *ringp) 21555084Sjohnlev { 21565084Sjohnlev comif_ring_fe_t *frp; 21575084Sjohnlev comif_ring_be_t *brp; 21585084Sjohnlev 21595084Sjohnlev if (!ringp->xr_frontend) { 21605084Sjohnlev /* for backend ring */ 21615084Sjohnlev brp = BACK_RING(ringp); 21625084Sjohnlev return (GET_RING_ENTRY_BE(ringp, brp->rsp_prod_pvt++)); 21635084Sjohnlev } else { 21645084Sjohnlev /* for frontend ring */ 21655084Sjohnlev frp = FRONT_RING(ringp); 21665084Sjohnlev /* RING_FINAL_CHECK_FOR_RESPONSES() */ 21675084Sjohnlev if (xvdi_ring_has_unconsumed_responses(ringp)) 21685084Sjohnlev return (GET_RING_ENTRY_FE(ringp, frp->rsp_cons++)); 21695084Sjohnlev else { 21705084Sjohnlev ddi_put32(ringp->xr_acc_hdl, &frp->sring->rsp_event, 21715084Sjohnlev frp->rsp_cons + 1); 21725084Sjohnlev membar_enter(); 21735084Sjohnlev if (xvdi_ring_has_unconsumed_responses(ringp)) 21745084Sjohnlev return (GET_RING_ENTRY_FE(ringp, 21755084Sjohnlev frp->rsp_cons++)); 21765084Sjohnlev else 21775084Sjohnlev return (NULL); 21785084Sjohnlev } 21795084Sjohnlev } 21805084Sjohnlev } 21815084Sjohnlev 21825084Sjohnlev int 21835084Sjohnlev xvdi_ring_push_response(xendev_ring_t *ringp) 21845084Sjohnlev { 21855084Sjohnlev RING_IDX old, new, rspevt; 21865084Sjohnlev comif_ring_be_t *brp; 21875084Sjohnlev 21885084Sjohnlev /* only backend should be able to push response */ 21895084Sjohnlev ASSERT(!ringp->xr_frontend); 21905084Sjohnlev 21915084Sjohnlev /* RING_PUSH_RESPONSE_AND_CHECK_NOTIFY() */ 21925084Sjohnlev brp = BACK_RING(ringp); 21935084Sjohnlev old = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_prod); 21945084Sjohnlev new = brp->rsp_prod_pvt; 21955084Sjohnlev ddi_put32(ringp->xr_acc_hdl, &brp->sring->rsp_prod, new); 21965084Sjohnlev membar_enter(); 21975084Sjohnlev rspevt = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_event); 21985084Sjohnlev return ((RING_IDX)(new - rspevt) < (RING_IDX)(new - old)); 21995084Sjohnlev } 22005084Sjohnlev 22015084Sjohnlev static void 22025084Sjohnlev xvdi_ring_init_sring(xendev_ring_t *ringp) 22035084Sjohnlev { 22045084Sjohnlev ddi_acc_handle_t acchdl; 22055084Sjohnlev comif_sring_t *xsrp; 22065084Sjohnlev int i; 22075084Sjohnlev 22085084Sjohnlev xsrp = (comif_sring_t *)ringp->xr_vaddr; 22095084Sjohnlev acchdl = ringp->xr_acc_hdl; 22105084Sjohnlev 22115084Sjohnlev /* shared ring initialization */ 22125084Sjohnlev ddi_put32(acchdl, &xsrp->req_prod, 0); 22135084Sjohnlev ddi_put32(acchdl, &xsrp->rsp_prod, 0); 22145084Sjohnlev ddi_put32(acchdl, &xsrp->req_event, 1); 22155084Sjohnlev ddi_put32(acchdl, &xsrp->rsp_event, 1); 22165084Sjohnlev for (i = 0; i < sizeof (xsrp->pad); i++) 22175084Sjohnlev ddi_put8(acchdl, xsrp->pad + i, 0); 22185084Sjohnlev } 22195084Sjohnlev 22205084Sjohnlev static void 22215084Sjohnlev xvdi_ring_init_front_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize) 22225084Sjohnlev { 22235084Sjohnlev comif_ring_fe_t *xfrp; 22245084Sjohnlev 22255084Sjohnlev xfrp = &ringp->xr_sring.fr; 22265084Sjohnlev xfrp->req_prod_pvt = 0; 22275084Sjohnlev xfrp->rsp_cons = 0; 22285084Sjohnlev xfrp->nr_ents = nentry; 22295084Sjohnlev xfrp->sring = (comif_sring_t *)ringp->xr_vaddr; 22305084Sjohnlev 22315084Sjohnlev ringp->xr_frontend = 1; 22325084Sjohnlev ringp->xr_entry_size = entrysize; 22335084Sjohnlev } 22345084Sjohnlev 22355741Smrj #ifndef XPV_HVM_DRIVER 22365084Sjohnlev static void 22375084Sjohnlev xvdi_ring_init_back_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize) 22385084Sjohnlev { 22395084Sjohnlev comif_ring_be_t *xbrp; 22405084Sjohnlev 22415084Sjohnlev xbrp = &ringp->xr_sring.br; 22425084Sjohnlev xbrp->rsp_prod_pvt = 0; 22435084Sjohnlev xbrp->req_cons = 0; 22445084Sjohnlev xbrp->nr_ents = nentry; 22455084Sjohnlev xbrp->sring = (comif_sring_t *)ringp->xr_vaddr; 22465084Sjohnlev 22475084Sjohnlev ringp->xr_frontend = 0; 22485084Sjohnlev ringp->xr_entry_size = entrysize; 22495084Sjohnlev } 22505741Smrj #endif /* XPV_HVM_DRIVER */ 22515084Sjohnlev 22525084Sjohnlev static void 22535084Sjohnlev xendev_offline_device(void *arg) 22545084Sjohnlev { 22555084Sjohnlev dev_info_t *dip = (dev_info_t *)arg; 22565084Sjohnlev char devname[MAXNAMELEN] = {0}; 22575084Sjohnlev 22585084Sjohnlev /* 22595084Sjohnlev * This is currently the only chance to delete a devinfo node, which 22605084Sjohnlev * is _not_ always successful. 22615084Sjohnlev */ 22625084Sjohnlev (void) ddi_deviname(dip, devname); 22635084Sjohnlev (void) devfs_clean(ddi_get_parent(dip), devname + 1, DV_CLEAN_FORCE); 22645084Sjohnlev (void) ndi_devi_offline(dip, NDI_DEVI_REMOVE); 22655084Sjohnlev } 22665084Sjohnlev 22675084Sjohnlev static void 22685084Sjohnlev i_xvdi_oestate_cb(struct xenbus_device *dev, XenbusState oestate) 22695084Sjohnlev { 22705084Sjohnlev dev_info_t *dip = (dev_info_t *)dev->data; 22715084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 22727679SMax.Zhen@Sun.COM i_oestate_evt_t *evt = NULL; 22737756SMark.Johnson@Sun.COM boolean_t call_handler; 22747679SMax.Zhen@Sun.COM 22757679SMax.Zhen@Sun.COM XVDI_DPRINTF(XVDI_DBG_STATE, 22767679SMax.Zhen@Sun.COM "i_xvdi_oestate_cb: %s@%s sees oestate change to %d\n", 22777679SMax.Zhen@Sun.COM ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip), 22787679SMax.Zhen@Sun.COM ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip), 22797679SMax.Zhen@Sun.COM oestate); 22805084Sjohnlev 22817756SMark.Johnson@Sun.COM /* only call the handler if our state has changed */ 22827756SMark.Johnson@Sun.COM call_handler = B_FALSE; 22837756SMark.Johnson@Sun.COM mutex_enter(&pdp->xd_ndi_lk); 22847756SMark.Johnson@Sun.COM if (dev->otherend_state != oestate) { 22857756SMark.Johnson@Sun.COM dev->otherend_state = oestate; 22867756SMark.Johnson@Sun.COM call_handler = B_TRUE; 22877756SMark.Johnson@Sun.COM } 22887756SMark.Johnson@Sun.COM mutex_exit(&pdp->xd_ndi_lk); 22895084Sjohnlev 22907756SMark.Johnson@Sun.COM if (call_handler) { 22917756SMark.Johnson@Sun.COM /* 22927756SMark.Johnson@Sun.COM * Try to deliver the oestate change event to the dip 22937756SMark.Johnson@Sun.COM */ 22947756SMark.Johnson@Sun.COM evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP); 22957756SMark.Johnson@Sun.COM evt->dip = dip; 22967756SMark.Johnson@Sun.COM evt->state = oestate; 22977756SMark.Johnson@Sun.COM (void) ddi_taskq_dispatch(pdp->xd_oe_taskq, 22987756SMark.Johnson@Sun.COM i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP); 22997756SMark.Johnson@Sun.COM } 23005084Sjohnlev } 23015084Sjohnlev 23025084Sjohnlev /*ARGSUSED*/ 23035084Sjohnlev static void 23045084Sjohnlev i_xvdi_hpstate_cb(struct xenbus_watch *w, const char **vec, 23055084Sjohnlev unsigned int len) 23065084Sjohnlev { 23075084Sjohnlev dev_info_t *dip = (dev_info_t *)w->dev; 23085084Sjohnlev struct xendev_ppd *pdp = ddi_get_parent_data(dip); 23095084Sjohnlev 23107679SMax.Zhen@Sun.COM #ifdef DEBUG 23117679SMax.Zhen@Sun.COM char *hp_status = NULL; 23127679SMax.Zhen@Sun.COM unsigned int hpl = 0; 23137679SMax.Zhen@Sun.COM 23147679SMax.Zhen@Sun.COM (void) xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "", 23157679SMax.Zhen@Sun.COM (void *)&hp_status, &hpl); 23167679SMax.Zhen@Sun.COM XVDI_DPRINTF(XVDI_DBG_STATE, 23177679SMax.Zhen@Sun.COM "i_xvdi_hpstate_cb: %s@%s sees hpstate change to %s\n", 23187679SMax.Zhen@Sun.COM ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip), 23197679SMax.Zhen@Sun.COM ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip), 23207679SMax.Zhen@Sun.COM hp_status == NULL ? "null" : hp_status); 23217679SMax.Zhen@Sun.COM if (hp_status != NULL) 23227679SMax.Zhen@Sun.COM kmem_free(hp_status, hpl); 23237679SMax.Zhen@Sun.COM #endif /* DEBUG */ 23247679SMax.Zhen@Sun.COM 23255084Sjohnlev (void) ddi_taskq_dispatch(pdp->xd_hp_taskq, 23265084Sjohnlev i_xvdi_hpstate_handler, (void *)dip, DDI_SLEEP); 23275084Sjohnlev } 23285084Sjohnlev 23295084Sjohnlev static void 23305084Sjohnlev i_xvdi_probe_path_handler(void *arg) 23315084Sjohnlev { 23325084Sjohnlev dev_info_t *parent; 23335084Sjohnlev char *path = arg, *p = NULL; 23345084Sjohnlev int i, vdev, circ; 23355084Sjohnlev i_xd_cfg_t *xdcp; 23365084Sjohnlev boolean_t frontend; 23375084Sjohnlev domid_t dom; 23385084Sjohnlev 23395084Sjohnlev for (i = 0, xdcp = &xdci[0]; i < NXDC; i++, xdcp++) { 23405084Sjohnlev 23415084Sjohnlev if ((xdcp->xs_path_fe != NULL) && 23425084Sjohnlev (strncmp(path, xdcp->xs_path_fe, strlen(xdcp->xs_path_fe)) 23435084Sjohnlev == 0)) { 23445084Sjohnlev 23455084Sjohnlev frontend = B_TRUE; 23465084Sjohnlev p = path + strlen(xdcp->xs_path_fe); 23475084Sjohnlev break; 23485084Sjohnlev } 23495084Sjohnlev 23505084Sjohnlev if ((xdcp->xs_path_be != NULL) && 23515084Sjohnlev (strncmp(path, xdcp->xs_path_be, strlen(xdcp->xs_path_be)) 23525084Sjohnlev == 0)) { 23535084Sjohnlev 23545084Sjohnlev frontend = B_FALSE; 23555084Sjohnlev p = path + strlen(xdcp->xs_path_be); 23565084Sjohnlev break; 23575084Sjohnlev } 23585084Sjohnlev 23595084Sjohnlev } 23605084Sjohnlev 23615084Sjohnlev if (p == NULL) { 23625084Sjohnlev cmn_err(CE_WARN, "i_xvdi_probe_path_handler: " 23635084Sjohnlev "unexpected path prefix in %s", path); 23645084Sjohnlev goto done; 23655084Sjohnlev } 23665084Sjohnlev 23675084Sjohnlev if (frontend) { 23685084Sjohnlev dom = DOMID_SELF; 23695084Sjohnlev if (sscanf(p, "/%d/", &vdev) != 1) { 23705084Sjohnlev XVDI_DPRINTF(XVDI_DBG_PROBE, 23715084Sjohnlev "i_xvdi_probe_path_handler: " 23725084Sjohnlev "cannot parse frontend path %s", 23735084Sjohnlev path); 23745084Sjohnlev goto done; 23755084Sjohnlev } 23765084Sjohnlev } else { 23777632SNick.Todd@Sun.COM if (sscanf(p, "/%hu/%d/", &dom, &vdev) != 2) { 23785084Sjohnlev XVDI_DPRINTF(XVDI_DBG_PROBE, 23795084Sjohnlev "i_xvdi_probe_path_handler: " 23805084Sjohnlev "cannot parse backend path %s", 23815084Sjohnlev path); 23825084Sjohnlev goto done; 23835084Sjohnlev } 23845084Sjohnlev } 23855084Sjohnlev 23866175Sjohnlev /* 23876175Sjohnlev * This is an oxymoron, so indicates a bogus configuration we 23886175Sjohnlev * must check for. 23896175Sjohnlev */ 23906175Sjohnlev if (vdev == VDEV_NOXS) { 23916175Sjohnlev cmn_err(CE_WARN, "i_xvdi_probe_path_handler: " 23926175Sjohnlev "invalid path %s", path); 23936175Sjohnlev goto done; 23946175Sjohnlev } 23956175Sjohnlev 23965084Sjohnlev parent = xendev_dip; 23975084Sjohnlev ASSERT(parent != NULL); 23985084Sjohnlev 23995084Sjohnlev ndi_devi_enter(parent, &circ); 24005084Sjohnlev 24015084Sjohnlev if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL) { 24025084Sjohnlev XVDI_DPRINTF(XVDI_DBG_PROBE, 24035084Sjohnlev "i_xvdi_probe_path_handler: create for %s", path); 24045084Sjohnlev (void) xvdi_create_dev(parent, xdcp->devclass, dom, vdev); 24055084Sjohnlev } else { 24065084Sjohnlev XVDI_DPRINTF(XVDI_DBG_PROBE, 24075084Sjohnlev "i_xvdi_probe_path_handler: %s already exists", path); 24085084Sjohnlev } 24095084Sjohnlev 24105084Sjohnlev ndi_devi_exit(parent, circ); 24115084Sjohnlev 24125084Sjohnlev done: 24135084Sjohnlev kmem_free(path, strlen(path) + 1); 24145084Sjohnlev } 2415