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