diff options
Diffstat (limited to 'sys/xen/xenbus')
-rw-r--r-- | sys/xen/xenbus/init.txt | 14 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbus.c (renamed from sys/xen/xenbus/xenbus_client.c) | 141 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbus_comms.c | 226 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbus_comms.h | 48 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbus_dev.c | 229 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbus_if.m | 14 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbus_probe.c | 602 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbus_probe_backend.c | 308 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbus_xs.c | 935 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbusb.c | 878 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbusb.h | 272 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbusb_back.c | 295 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbusb_front.c | 195 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbusb_if.m | 78 | ||||
-rw-r--r-- | sys/xen/xenbus/xenbusvar.h | 325 |
15 files changed, 1990 insertions, 2570 deletions
diff --git a/sys/xen/xenbus/init.txt b/sys/xen/xenbus/init.txt deleted file mode 100644 index 4249549..0000000 --- a/sys/xen/xenbus/init.txt +++ /dev/null @@ -1,14 +0,0 @@ - - -- frontend driver initializes static xenbus_driver with _ids, _probe, _remove, -_resume, _otherend_changed - - - initialization calls xenbus_register_frontend(xenbus_driver) - - - xenbus_register_frontend sets read_otherend details to read_backend_details - then calls xenbus_register_driver_common(xenbus_driver, xenbus_frontend) - - - xenbus_register_driver_common sets underlying driver name to xenbus_driver name - underlying driver bus to xenbus_frontend's bus, driver's probe to xenbus_dev_probe - driver's remove to xenbus_dev_remove then calls driver_register - diff --git a/sys/xen/xenbus/xenbus_client.c b/sys/xen/xenbus/xenbus.c index 740d664..c3e5fee 100644 --- a/sys/xen/xenbus/xenbus_client.c +++ b/sys/xen/xenbus/xenbus.c @@ -1,8 +1,4 @@ /****************************************************************************** - * Client-facing interface for the Xenbus driver. In other words, the - * interface between the Xenbus and the device-specific code, be it the - * frontend or the backend of that driver. - * * Copyright (C) 2005 XenSource Ltd * * This file may be distributed separately from the Linux kernel, or @@ -27,6 +23,14 @@ * IN THE SOFTWARE. */ +/** + * \file xenbus.c + * + * \brief Client-facing interface for the Xenbus driver. + * + * In other words, the interface between the Xenbus and the device-specific + * code, be it the frontend or the backend of that driver. + */ #if 0 #define DPRINTK(fmt, args...) \ @@ -39,9 +43,12 @@ __FBSDID("$FreeBSD$"); #include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/kernel.h> #include <sys/types.h> #include <sys/malloc.h> #include <sys/libkern.h> +#include <sys/sbuf.h> #include <machine/xen/xen-os.h> #include <xen/hypervisor.h> @@ -50,6 +57,34 @@ __FBSDID("$FreeBSD$"); #include <xen/xenbus/xenbusvar.h> #include <machine/stdarg.h> +MALLOC_DEFINE(M_XENBUS, "xenbus", "XenBus Support"); + +/*------------------------- Private Functions --------------------------------*/ +/** + * \brief Construct the error path corresponding to the given XenBus + * device. + * + * \param dev The XenBus device for which we are constructing an error path. + * + * \return On success, the contructed error path. Otherwise NULL. + * + * It is the caller's responsibility to free any returned error path + * node using the M_XENBUS malloc type. + */ +static char * +error_path(device_t dev) +{ + char *path_buffer = malloc(strlen("error/") + + strlen(xenbus_get_node(dev)) + 1,M_XENBUS, M_WAITOK); + + strcpy(path_buffer, "error/"); + strcpy(path_buffer + strlen("error/"), xenbus_get_node(dev)); + + return (path_buffer); +} + +/*--------------------------- Public Functions -------------------------------*/ +/*-------- API comments for these methods can be found in xenbusvar.h --------*/ const char * xenbus_strstate(XenbusState state) { @@ -67,15 +102,15 @@ xenbus_strstate(XenbusState state) } int -xenbus_watch_path(device_t dev, char *path, struct xenbus_watch *watch, - void (*callback)(struct xenbus_watch *, const char **, unsigned int)) +xenbus_watch_path(device_t dev, char *path, struct xs_watch *watch, + xs_watch_cb_t *callback) { int error; watch->node = path; watch->callback = callback; - error = register_xenbus_watch(watch); + error = xs_register_watch(watch); if (error) { watch->node = NULL; @@ -88,12 +123,12 @@ xenbus_watch_path(device_t dev, char *path, struct xenbus_watch *watch, int xenbus_watch_path2(device_t dev, const char *path, - const char *path2, struct xenbus_watch *watch, - void (*callback)(struct xenbus_watch *, const char **, unsigned int)) + const char *path2, struct xs_watch *watch, + xs_watch_cb_t *callback) { int error; char *state = malloc(strlen(path) + 1 + strlen(path2) + 1, - M_DEVBUF, M_WAITOK); + M_XENBUS, M_WAITOK); strcpy(state, path); strcat(state, "/"); @@ -101,46 +136,27 @@ xenbus_watch_path2(device_t dev, const char *path, error = xenbus_watch_path(dev, state, watch, callback); if (error) { - free(state, M_DEVBUF); + free(state,M_XENBUS); } return (error); } -/** - * Return the path to the error node for the given device, or NULL on failure. - * If the value returned is non-NULL, then it is the caller's to kfree. - */ -static char * -error_path(device_t dev) -{ - char *path_buffer = malloc(strlen("error/") - + strlen(xenbus_get_node(dev)) + 1, M_DEVBUF, M_WAITOK); - - strcpy(path_buffer, "error/"); - strcpy(path_buffer + strlen("error/"), xenbus_get_node(dev)); - - return (path_buffer); -} - - -static void -_dev_error(device_t dev, int err, const char *fmt, va_list ap) +void +xenbus_dev_verror(device_t dev, int err, const char *fmt, va_list ap) { int ret; unsigned int len; char *printf_buffer = NULL, *path_buffer = NULL; #define PRINTF_BUFFER_SIZE 4096 - printf_buffer = malloc(PRINTF_BUFFER_SIZE, M_DEVBUF, M_WAITOK); + printf_buffer = malloc(PRINTF_BUFFER_SIZE,M_XENBUS, M_WAITOK); len = sprintf(printf_buffer, "%i ", err); ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap); KASSERT(len + ret <= PRINTF_BUFFER_SIZE-1, ("xenbus error message too big")); -#if 0 - dev_err(&dev->dev, "%s\n", printf_buffer); -#endif + device_printf(dev, "Error %s\n", printf_buffer); path_buffer = error_path(dev); if (path_buffer == NULL) { @@ -149,7 +165,7 @@ _dev_error(device_t dev, int err, const char *fmt, va_list ap) goto fail; } - if (xenbus_write(XBT_NIL, path_buffer, "error", printf_buffer) != 0) { + if (xs_write(XST_NIL, path_buffer, "error", printf_buffer) != 0) { printf("xenbus: failed to write error node for %s (%s)\n", xenbus_get_node(dev), printf_buffer); goto fail; @@ -157,9 +173,9 @@ _dev_error(device_t dev, int err, const char *fmt, va_list ap) fail: if (printf_buffer) - free(printf_buffer, M_DEVBUF); + free(printf_buffer,M_XENBUS); if (path_buffer) - free(path_buffer, M_DEVBUF); + free(path_buffer,M_XENBUS); } void @@ -168,41 +184,45 @@ xenbus_dev_error(device_t dev, int err, const char *fmt, ...) va_list ap; va_start(ap, fmt); - _dev_error(dev, err, fmt, ap); + xenbus_dev_verror(dev, err, fmt, ap); va_end(ap); } void +xenbus_dev_vfatal(device_t dev, int err, const char *fmt, va_list ap) +{ + xenbus_dev_verror(dev, err, fmt, ap); + device_printf(dev, "Fatal error. Transitioning to Closing State\n"); + xenbus_set_state(dev, XenbusStateClosing); +} + +void xenbus_dev_fatal(device_t dev, int err, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - _dev_error(dev, err, fmt, ap); + xenbus_dev_vfatal(dev, err, fmt, ap); va_end(ap); - - xenbus_set_state(dev, XenbusStateClosing); } int -xenbus_grant_ring(device_t dev, unsigned long ring_mfn, int *refp) +xenbus_grant_ring(device_t dev, unsigned long ring_mfn, grant_ref_t *refp) { int error; - grant_ref_t ref; error = gnttab_grant_foreign_access( - xenbus_get_otherend_id(dev), ring_mfn, 0, &ref); + xenbus_get_otherend_id(dev), ring_mfn, 0, refp); if (error) { xenbus_dev_fatal(dev, error, "granting access to ring page"); return (error); } - *refp = ref; return (0); } int -xenbus_alloc_evtchn(device_t dev, int *port) +xenbus_alloc_evtchn(device_t dev, evtchn_port_t *port) { struct evtchn_alloc_unbound alloc_unbound; int err; @@ -222,7 +242,7 @@ xenbus_alloc_evtchn(device_t dev, int *port) } int -xenbus_free_evtchn(device_t dev, int port) +xenbus_free_evtchn(device_t dev, evtchn_port_t port) { struct evtchn_close close; int err; @@ -240,12 +260,29 @@ xenbus_free_evtchn(device_t dev, int port) XenbusState xenbus_read_driver_state(const char *path) { - XenbusState result; + XenbusState result; + int error; + + error = xs_gather(XST_NIL, path, "state", "%d", &result, NULL); + if (error) + result = XenbusStateClosed; + + return (result); +} + +int +xenbus_dev_is_online(device_t dev) +{ + const char *path; int error; + int value; - error = xenbus_gather(XBT_NIL, path, "state", "%d", &result, NULL); - if (error) - result = XenbusStateClosed; + path = xenbus_get_node(dev); + error = xs_gather(XST_NIL, path, "online", "%d", &value, NULL); + if (error != 0) { + /* Default to not online. */ + value = 0; + } - return (result); + return (value); } diff --git a/sys/xen/xenbus/xenbus_comms.c b/sys/xen/xenbus/xenbus_comms.c deleted file mode 100644 index 2f03955..0000000 --- a/sys/xen/xenbus/xenbus_comms.c +++ /dev/null @@ -1,226 +0,0 @@ -/****************************************************************************** - * xenbus_comms.c - * - * Low level code to talks to Xen Store: ringbuffer and event channel. - * - * Copyright (C) 2005 Rusty Russell, IBM Corporation - * - * This file may be distributed separately from the Linux kernel, or - * incorporated into other software packages, subject to the following license: - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this source file (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/bus.h> -#include <sys/kernel.h> -#include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/sx.h> -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/syslog.h> - -#include <machine/xen/xen-os.h> -#include <xen/hypervisor.h> - -#include <xen/xen_intr.h> -#include <xen/evtchn.h> -#include <xen/interface/io/xs_wire.h> -#include <xen/xenbus/xenbus_comms.h> - -static unsigned int xenstore_irq; - -static inline struct xenstore_domain_interface * -xenstore_domain_interface(void) -{ - - return (struct xenstore_domain_interface *)xen_store; -} - -static void -xb_intr(void * arg __attribute__((unused))) -{ - - wakeup(xen_store); -} - -static int -xb_check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod) -{ - - return ((prod - cons) <= XENSTORE_RING_SIZE); -} - -static void * -xb_get_output_chunk(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod, - char *buf, uint32_t *len) -{ - - *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod); - if ((XENSTORE_RING_SIZE - (prod - cons)) < *len) - *len = XENSTORE_RING_SIZE - (prod - cons); - return (buf + MASK_XENSTORE_IDX(prod)); -} - -static const void * -xb_get_input_chunk(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod, - const char *buf, uint32_t *len) -{ - - *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons); - if ((prod - cons) < *len) - *len = prod - cons; - return (buf + MASK_XENSTORE_IDX(cons)); -} - -int -xb_write(const void *tdata, unsigned len, struct lock_object *lock) -{ - struct xenstore_domain_interface *intf = xenstore_domain_interface(); - XENSTORE_RING_IDX cons, prod; - const char *data = (const char *)tdata; - int error; - - while (len != 0) { - void *dst; - unsigned int avail; - - while ((intf->req_prod - intf->req_cons) - == XENSTORE_RING_SIZE) { - error = _sleep(intf, - lock, - PCATCH, "xbwrite", hz/10); - if (error && error != EWOULDBLOCK) - return (error); - } - - /* Read indexes, then verify. */ - cons = intf->req_cons; - prod = intf->req_prod; - mb(); - if (!xb_check_indexes(cons, prod)) { - intf->req_cons = intf->req_prod = 0; - return (EIO); - } - - dst = xb_get_output_chunk(cons, prod, intf->req, &avail); - if (avail == 0) - continue; - if (avail > len) - avail = len; - mb(); - - memcpy(dst, data, avail); - data += avail; - len -= avail; - - /* Other side must not see new header until data is there. */ - wmb(); - intf->req_prod += avail; - - /* This implies mb() before other side sees interrupt. */ - notify_remote_via_evtchn(xen_store_evtchn); - } - - return (0); -} - -int -xb_read(void *tdata, unsigned len, struct lock_object *lock) -{ - struct xenstore_domain_interface *intf = xenstore_domain_interface(); - XENSTORE_RING_IDX cons, prod; - char *data = (char *)tdata; - int error; - - while (len != 0) { - unsigned int avail; - const char *src; - - while (intf->rsp_cons == intf->rsp_prod) { - error = _sleep(intf, lock, - PCATCH, "xbread", hz/10); - if (error && error != EWOULDBLOCK) - return (error); - } - - /* Read indexes, then verify. */ - cons = intf->rsp_cons; - prod = intf->rsp_prod; - if (!xb_check_indexes(cons, prod)) { - intf->rsp_cons = intf->rsp_prod = 0; - return (EIO); - } - - src = xb_get_input_chunk(cons, prod, intf->rsp, &avail); - if (avail == 0) - continue; - if (avail > len) - avail = len; - - /* We must read header before we read data. */ - rmb(); - - memcpy(data, src, avail); - data += avail; - len -= avail; - - /* Other side must not see free space until we've copied out */ - mb(); - intf->rsp_cons += avail; - - /* Implies mb(): they will see new header. */ - notify_remote_via_evtchn(xen_store_evtchn); - } - - return (0); -} - -/* Set up interrupt handler off store event channel. */ -int -xb_init_comms(void) -{ - struct xenstore_domain_interface *intf = xenstore_domain_interface(); - int error; - - if (intf->rsp_prod != intf->rsp_cons) { - log(LOG_WARNING, "XENBUS response ring is not quiescent " - "(%08x:%08x): fixing up\n", - intf->rsp_cons, intf->rsp_prod); - intf->rsp_cons = intf->rsp_prod; - } - - if (xenstore_irq) - unbind_from_irqhandler(xenstore_irq); - - error = bind_caller_port_to_irqhandler( - xen_store_evtchn, "xenbus", - xb_intr, NULL, INTR_TYPE_NET, &xenstore_irq); - if (error) { - log(LOG_WARNING, "XENBUS request irq failed %i\n", error); - return (error); - } - - return (0); -} diff --git a/sys/xen/xenbus/xenbus_comms.h b/sys/xen/xenbus/xenbus_comms.h deleted file mode 100644 index fa47331..0000000 --- a/sys/xen/xenbus/xenbus_comms.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Private include for xenbus communications. - * - * Copyright (C) 2005 Rusty Russell, IBM Corporation - * - * This file may be distributed separately from the Linux kernel, or - * incorporated into other software packages, subject to the following license: - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this source file (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - * - * $FreeBSD$ - */ - -#ifndef _XENBUS_COMMS_H -#define _XENBUS_COMMS_H - -struct sx; -extern int xen_store_evtchn; -extern char *xen_store; - -int xs_init(void); -int xb_init_comms(void); - -/* Low level routines. */ -int xb_write(const void *data, unsigned len, struct lock_object *); -int xb_read(void *data, unsigned len, struct lock_object *); -extern int xenbus_running; - -char *kasprintf(const char *fmt, ...); - - -#endif /* _XENBUS_COMMS_H */ diff --git a/sys/xen/xenbus/xenbus_dev.c b/sys/xen/xenbus/xenbus_dev.c deleted file mode 100644 index ac3f103..0000000 --- a/sys/xen/xenbus/xenbus_dev.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * xenbus_dev.c - * - * Driver giving user-space access to the kernel's xenbus connection - * to xenstore. - * - * Copyright (c) 2005, Christian Limpach - * Copyright (c) 2005, Rusty Russell, IBM Corporation - * - * This file may be distributed separately from the Linux kernel, or - * incorporated into other software packages, subject to the following license: - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this source file (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/cdefs.h> -#include <sys/errno.h> -#include <sys/uio.h> -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/proc.h> -#include <sys/kernel.h> -#include <sys/malloc.h> -#include <sys/conf.h> - -#include <machine/xen/xen-os.h> -#include <xen/hypervisor.h> -#include <xen/xenbus/xenbusvar.h> -#include <xen/xenbus/xenbus_comms.h> - -struct xenbus_dev_transaction { - LIST_ENTRY(xenbus_dev_transaction) list; - struct xenbus_transaction handle; -}; - -struct xenbus_dev_data { - /* In-progress transaction. */ - LIST_HEAD(xdd_list_head, xenbus_dev_transaction) transactions; - - /* Partial request. */ - unsigned int len; - union { - struct xsd_sockmsg msg; - char buffer[PAGE_SIZE]; - } u; - - /* Response queue. */ -#define MASK_READ_IDX(idx) ((idx)&(PAGE_SIZE-1)) - char read_buffer[PAGE_SIZE]; - unsigned int read_cons, read_prod; -}; - -static int -xenbus_dev_read(struct cdev *dev, struct uio *uio, int ioflag) -{ - int error; - struct xenbus_dev_data *u = dev->si_drv1; - - while (u->read_prod == u->read_cons) { - error = tsleep(u, PCATCH, "xbdread", hz/10); - if (error && error != EWOULDBLOCK) - return (error); - } - - while (uio->uio_resid > 0) { - if (u->read_cons == u->read_prod) - break; - error = uiomove(&u->read_buffer[MASK_READ_IDX(u->read_cons)], - 1, uio); - if (error) - return (error); - u->read_cons++; - } - return (0); -} - -static void -queue_reply(struct xenbus_dev_data *u, char *data, unsigned int len) -{ - int i; - - for (i = 0; i < len; i++, u->read_prod++) - u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i]; - - KASSERT((u->read_prod - u->read_cons) <= sizeof(u->read_buffer), - ("xenstore reply too big")); - - wakeup(u); -} - -static int -xenbus_dev_write(struct cdev *dev, struct uio *uio, int ioflag) -{ - int error; - struct xenbus_dev_data *u = dev->si_drv1; - struct xenbus_dev_transaction *trans; - void *reply; - int len = uio->uio_resid; - - if ((len + u->len) > sizeof(u->u.buffer)) - return (EINVAL); - - error = uiomove(u->u.buffer + u->len, len, uio); - if (error) - return (error); - - u->len += len; - if (u->len < (sizeof(u->u.msg) + u->u.msg.len)) - return (0); - - switch (u->u.msg.type) { - case XS_TRANSACTION_START: - case XS_TRANSACTION_END: - case XS_DIRECTORY: - case XS_READ: - case XS_GET_PERMS: - case XS_RELEASE: - case XS_GET_DOMAIN_PATH: - case XS_WRITE: - case XS_MKDIR: - case XS_RM: - case XS_SET_PERMS: - error = xenbus_dev_request_and_reply(&u->u.msg, &reply); - if (!error) { - if (u->u.msg.type == XS_TRANSACTION_START) { - trans = malloc(sizeof(*trans), M_DEVBUF, - M_WAITOK); - trans->handle.id = strtoul(reply, NULL, 0); - LIST_INSERT_HEAD(&u->transactions, trans, list); - } else if (u->u.msg.type == XS_TRANSACTION_END) { - LIST_FOREACH(trans, &u->transactions, list) - if (trans->handle.id == u->u.msg.tx_id) - break; -#if 0 /* XXX does this mean the list is empty? */ - BUG_ON(&trans->list == &u->transactions); -#endif - LIST_REMOVE(trans, list); - free(trans, M_DEVBUF); - } - queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg)); - queue_reply(u, (char *)reply, u->u.msg.len); - free(reply, M_DEVBUF); - } - break; - - default: - error = EINVAL; - break; - } - - if (error == 0) - u->len = 0; - - return (error); -} - -static int -xenbus_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) -{ - struct xenbus_dev_data *u; - - if (xen_store_evtchn == 0) - return (ENOENT); -#if 0 /* XXX figure out if equiv needed */ - nonseekable_open(inode, filp); -#endif - u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK|M_ZERO); - LIST_INIT(&u->transactions); - dev->si_drv1 = u; - - return (0); -} - -static int -xenbus_dev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) -{ - struct xenbus_dev_data *u = dev->si_drv1; - struct xenbus_dev_transaction *trans, *tmp; - - LIST_FOREACH_SAFE(trans, &u->transactions, list, tmp) { - xenbus_transaction_end(trans->handle, 1); - LIST_REMOVE(trans, list); - free(trans, M_DEVBUF); - } - - free(u, M_DEVBUF); - return (0); -} - -static struct cdevsw xenbus_dev_cdevsw = { - .d_version = D_VERSION, - .d_read = xenbus_dev_read, - .d_write = xenbus_dev_write, - .d_open = xenbus_dev_open, - .d_close = xenbus_dev_close, - .d_name = "xenbus_dev", -}; - -static int -xenbus_dev_sysinit(void) -{ - make_dev(&xenbus_dev_cdevsw, 0, UID_ROOT, GID_WHEEL, 0400, - "xen/xenbus"); - - return (0); -} -SYSINIT(xenbus_dev_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, - xenbus_dev_sysinit, NULL); diff --git a/sys/xen/xenbus/xenbus_if.m b/sys/xen/xenbus/xenbus_if.m index 018a2bb..d671418 100644 --- a/sys/xen/xenbus/xenbus_if.m +++ b/sys/xen/xenbus/xenbus_if.m @@ -31,7 +31,15 @@ INTERFACE xenbus; -METHOD int backend_changed { - device_t dev; - enum xenbus_state newstate; +/** + * \brief Callback triggered when the state of the otherend + * of a split device changes. + * + * \param _dev NewBus device_t for this XenBus device whose otherend's + * state has changed.. + * \param _newstate The new state of the otherend device. + */ +METHOD int otherend_changed { + device_t _dev; + enum xenbus_state _newstate; }; diff --git a/sys/xen/xenbus/xenbus_probe.c b/sys/xen/xenbus/xenbus_probe.c deleted file mode 100644 index b1e9a21..0000000 --- a/sys/xen/xenbus/xenbus_probe.c +++ /dev/null @@ -1,602 +0,0 @@ -/****************************************************************************** - * Talks to Xen Store to figure out what devices we have. - * - * Copyright (C) 2008 Doug Rabson - * Copyright (C) 2005 Rusty Russell, IBM Corporation - * Copyright (C) 2005 Mike Wray, Hewlett-Packard - * Copyright (C) 2005 XenSource Ltd - * - * This file may be distributed separately from the Linux kernel, or - * incorporated into other software packages, subject to the following license: - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this source file (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#if 0 -#define DPRINTK(fmt, args...) \ - printf("xenbus_probe (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args) -#else -#define DPRINTK(fmt, args...) ((void)0) -#endif - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/bus.h> -#include <sys/kernel.h> -#include <sys/lock.h> -#include <sys/malloc.h> -#include <sys/module.h> -#include <sys/sysctl.h> -#include <sys/syslog.h> -#include <sys/systm.h> -#include <sys/sx.h> -#include <sys/taskqueue.h> - -#include <machine/xen/xen-os.h> -#include <machine/stdarg.h> - -#include <xen/gnttab.h> -#include <xen/xenbus/xenbusvar.h> -#include <xen/xenbus/xenbus_comms.h> - -struct xenbus_softc { - struct xenbus_watch xs_devicewatch; - struct task xs_probechildren; - struct intr_config_hook xs_attachcb; - device_t xs_dev; -}; - -struct xenbus_device_ivars { - struct xenbus_watch xd_otherend_watch; /* must be first */ - struct sx xd_lock; - device_t xd_dev; - char *xd_node; /* node name in xenstore */ - char *xd_type; /* xen device type */ - enum xenbus_state xd_state; - int xd_otherend_id; - char *xd_otherend_path; -}; - -/* Simplified asprintf. */ -char * -kasprintf(const char *fmt, ...) -{ - va_list ap; - unsigned int len; - char *p, dummy[1]; - - va_start(ap, fmt); - /* FIXME: vsnprintf has a bug, NULL should work */ - len = vsnprintf(dummy, 0, fmt, ap); - va_end(ap); - - p = malloc(len + 1, M_DEVBUF, M_WAITOK); - va_start(ap, fmt); - vsprintf(p, fmt, ap); - va_end(ap); - return p; -} - -static void -xenbus_identify(driver_t *driver, device_t parent) -{ - - BUS_ADD_CHILD(parent, 0, "xenbus", 0); -} - -static int -xenbus_probe(device_t dev) -{ - int err = 0; - - DPRINTK(""); - - /* Initialize the interface to xenstore. */ - err = xs_init(); - if (err) { - log(LOG_WARNING, - "XENBUS: Error initializing xenstore comms: %i\n", err); - return (ENXIO); - } - err = gnttab_init(); - if (err) { - log(LOG_WARNING, - "XENBUS: Error initializing grant table: %i\n", err); - return (ENXIO); - } - device_set_desc(dev, "Xen Devices"); - - return (0); -} - -static enum xenbus_state -xenbus_otherend_state(struct xenbus_device_ivars *ivars) -{ - - return (xenbus_read_driver_state(ivars->xd_otherend_path)); -} - -static void -xenbus_backend_changed(struct xenbus_watch *watch, const char **vec, - unsigned int len) -{ - struct xenbus_device_ivars *ivars; - device_t dev; - enum xenbus_state newstate; - - ivars = (struct xenbus_device_ivars *) watch; - dev = ivars->xd_dev; - - if (!ivars->xd_otherend_path - || strncmp(ivars->xd_otherend_path, vec[XS_WATCH_PATH], - strlen(ivars->xd_otherend_path))) - return; - - newstate = xenbus_otherend_state(ivars); - XENBUS_BACKEND_CHANGED(dev, newstate); -} - -static int -xenbus_device_exists(device_t dev, const char *node) -{ - device_t *kids; - struct xenbus_device_ivars *ivars; - int i, count, result; - - if (device_get_children(dev, &kids, &count)) - return (FALSE); - - result = FALSE; - for (i = 0; i < count; i++) { - ivars = device_get_ivars(kids[i]); - if (!strcmp(ivars->xd_node, node)) { - result = TRUE; - break; - } - } - free(kids, M_TEMP); - - return (result); -} - -static int -xenbus_add_device(device_t dev, const char *bus, - const char *type, const char *id) -{ - device_t child; - struct xenbus_device_ivars *ivars; - enum xenbus_state state; - char *statepath; - int error; - - ivars = malloc(sizeof(struct xenbus_device_ivars), - M_DEVBUF, M_ZERO|M_WAITOK); - ivars->xd_node = kasprintf("%s/%s/%s", bus, type, id); - - if (xenbus_device_exists(dev, ivars->xd_node)) { - /* - * We are already tracking this node - */ - free(ivars->xd_node, M_DEVBUF); - free(ivars, M_DEVBUF); - return (0); - } - - state = xenbus_read_driver_state(ivars->xd_node); - - if (state != XenbusStateInitialising) { - /* - * Device is not new, so ignore it. This can - * happen if a device is going away after - * switching to Closed. - */ - free(ivars->xd_node, M_DEVBUF); - free(ivars, M_DEVBUF); - return (0); - } - - /* - * Find the backend details - */ - error = xenbus_gather(XBT_NIL, ivars->xd_node, - "backend-id", "%i", &ivars->xd_otherend_id, - "backend", NULL, &ivars->xd_otherend_path, - NULL); - if (error) - return (error); - - sx_init(&ivars->xd_lock, "xdlock"); - ivars->xd_type = strdup(type, M_DEVBUF); - ivars->xd_state = XenbusStateInitialising; - - statepath = malloc(strlen(ivars->xd_otherend_path) - + strlen("/state") + 1, M_DEVBUF, M_WAITOK); - sprintf(statepath, "%s/state", ivars->xd_otherend_path); - - ivars->xd_otherend_watch.node = statepath; - ivars->xd_otherend_watch.callback = xenbus_backend_changed; - - child = device_add_child(dev, NULL, -1); - ivars->xd_dev = child; - device_set_ivars(child, ivars); - - return (0); -} - -static int -xenbus_enumerate_type(device_t dev, const char *bus, const char *type) -{ - char **dir; - unsigned int i, count; - int error; - - error = xenbus_directory(XBT_NIL, bus, type, &count, &dir); - if (error) - return (error); - for (i = 0; i < count; i++) - xenbus_add_device(dev, bus, type, dir[i]); - - free(dir, M_DEVBUF); - - return (0); -} - -static int -xenbus_enumerate_bus(device_t dev, const char *bus) -{ - char **dir; - unsigned int i, count; - int error; - - error = xenbus_directory(XBT_NIL, bus, "", &count, &dir); - if (error) - return (error); - for (i = 0; i < count; i++) { - xenbus_enumerate_type(dev, bus, dir[i]); - } - free(dir, M_DEVBUF); - - return (0); -} - -static int -xenbus_probe_children(device_t dev) -{ - device_t *kids; - struct xenbus_device_ivars *ivars; - int i, count; - - /* - * Probe any new devices and register watches for any that - * attach successfully. Since part of the protocol which - * establishes a connection with the other end is interrupt - * driven, we sleep until the device reaches a stable state - * (closed or connected). - */ - if (device_get_children(dev, &kids, &count) == 0) { - for (i = 0; i < count; i++) { - if (device_get_state(kids[i]) != DS_NOTPRESENT) - continue; - - if (device_probe_and_attach(kids[i])) - continue; - ivars = device_get_ivars(kids[i]); - register_xenbus_watch( - &ivars->xd_otherend_watch); - sx_xlock(&ivars->xd_lock); - while (ivars->xd_state != XenbusStateClosed - && ivars->xd_state != XenbusStateConnected) - sx_sleep(&ivars->xd_state, &ivars->xd_lock, - 0, "xdattach", 0); - sx_xunlock(&ivars->xd_lock); - } - free(kids, M_TEMP); - } - - return (0); -} - -static void -xenbus_probe_children_cb(void *arg, int pending) -{ - device_t dev = (device_t) arg; - - xenbus_probe_children(dev); -} - -static void -xenbus_devices_changed(struct xenbus_watch *watch, - const char **vec, unsigned int len) -{ - struct xenbus_softc *sc = (struct xenbus_softc *) watch; - device_t dev = sc->xs_dev; - char *node, *bus, *type, *id, *p; - - node = strdup(vec[XS_WATCH_PATH], M_DEVBUF); - p = strchr(node, '/'); - if (!p) - goto out; - bus = node; - *p = 0; - type = p + 1; - - p = strchr(type, '/'); - if (!p) - goto out; - *p = 0; - id = p + 1; - - p = strchr(id, '/'); - if (p) - *p = 0; - - xenbus_add_device(dev, bus, type, id); - taskqueue_enqueue(taskqueue_thread, &sc->xs_probechildren); -out: - free(node, M_DEVBUF); -} - -static void -xenbus_attach_deferred(void *arg) -{ - device_t dev = (device_t) arg; - struct xenbus_softc *sc = device_get_softc(dev); - int error; - - error = xenbus_enumerate_bus(dev, "device"); - if (error) - return; - xenbus_probe_children(dev); - - sc->xs_dev = dev; - sc->xs_devicewatch.node = "device"; - sc->xs_devicewatch.callback = xenbus_devices_changed; - - TASK_INIT(&sc->xs_probechildren, 0, xenbus_probe_children_cb, dev); - - register_xenbus_watch(&sc->xs_devicewatch); - - config_intrhook_disestablish(&sc->xs_attachcb); -} - -static int -xenbus_attach(device_t dev) -{ - struct xenbus_softc *sc = device_get_softc(dev); - - sc->xs_attachcb.ich_func = xenbus_attach_deferred; - sc->xs_attachcb.ich_arg = dev; - config_intrhook_establish(&sc->xs_attachcb); - - return (0); -} - -static int -xenbus_suspend(device_t dev) -{ - int error; - - DPRINTK(""); - - error = bus_generic_suspend(dev); - if (error) - return (error); - - xs_suspend(); - - return (0); -} - -static int -xenbus_resume(device_t dev) -{ - device_t *kids; - struct xenbus_device_ivars *ivars; - int i, count, error; - char *statepath; - - xb_init_comms(); - xs_resume(); - - /* - * We must re-examine each device and find the new path for - * its backend. - */ - if (device_get_children(dev, &kids, &count) == 0) { - for (i = 0; i < count; i++) { - if (device_get_state(kids[i]) == DS_NOTPRESENT) - continue; - - ivars = device_get_ivars(kids[i]); - - unregister_xenbus_watch( - &ivars->xd_otherend_watch); - ivars->xd_state = XenbusStateInitialising; - - /* - * Find the new backend details and - * re-register our watch. - */ - free(ivars->xd_otherend_path, M_DEVBUF); - error = xenbus_gather(XBT_NIL, ivars->xd_node, - "backend-id", "%i", &ivars->xd_otherend_id, - "backend", NULL, &ivars->xd_otherend_path, - NULL); - if (error) - return (error); - - DEVICE_RESUME(kids[i]); - - statepath = malloc(strlen(ivars->xd_otherend_path) - + strlen("/state") + 1, M_DEVBUF, M_WAITOK); - sprintf(statepath, "%s/state", ivars->xd_otherend_path); - - free(ivars->xd_otherend_watch.node, M_DEVBUF); - ivars->xd_otherend_watch.node = statepath; - register_xenbus_watch( - &ivars->xd_otherend_watch); - -#if 0 - /* - * Can't do this yet since we are running in - * the xenwatch thread and if we sleep here, - * we will stop delivering watch notifications - * and the device will never come back online. - */ - sx_xlock(&ivars->xd_lock); - while (ivars->xd_state != XenbusStateClosed - && ivars->xd_state != XenbusStateConnected) - sx_sleep(&ivars->xd_state, &ivars->xd_lock, - 0, "xdresume", 0); - sx_xunlock(&ivars->xd_lock); -#endif - } - free(kids, M_TEMP); - } - - return (0); -} - -static int -xenbus_print_child(device_t dev, device_t child) -{ - struct xenbus_device_ivars *ivars = device_get_ivars(child); - int retval = 0; - - retval += bus_print_child_header(dev, child); - retval += printf(" at %s", ivars->xd_node); - retval += bus_print_child_footer(dev, child); - - return (retval); -} - -static int -xenbus_read_ivar(device_t dev, device_t child, int index, - uintptr_t * result) -{ - struct xenbus_device_ivars *ivars = device_get_ivars(child); - - switch (index) { - case XENBUS_IVAR_NODE: - *result = (uintptr_t) ivars->xd_node; - return (0); - - case XENBUS_IVAR_TYPE: - *result = (uintptr_t) ivars->xd_type; - return (0); - - case XENBUS_IVAR_STATE: - *result = (uintptr_t) ivars->xd_state; - return (0); - - case XENBUS_IVAR_OTHEREND_ID: - *result = (uintptr_t) ivars->xd_otherend_id; - return (0); - - case XENBUS_IVAR_OTHEREND_PATH: - *result = (uintptr_t) ivars->xd_otherend_path; - return (0); - } - - return (ENOENT); -} - -static int -xenbus_write_ivar(device_t dev, device_t child, int index, uintptr_t value) -{ - struct xenbus_device_ivars *ivars = device_get_ivars(child); - enum xenbus_state newstate; - int currstate; - int error; - - switch (index) { - case XENBUS_IVAR_STATE: - newstate = (enum xenbus_state) value; - sx_xlock(&ivars->xd_lock); - if (ivars->xd_state == newstate) - goto out; - - error = xenbus_scanf(XBT_NIL, ivars->xd_node, "state", - NULL, "%d", &currstate); - if (error) - goto out; - - error = xenbus_printf(XBT_NIL, ivars->xd_node, "state", - "%d", newstate); - if (error) { - if (newstate != XenbusStateClosing) /* Avoid looping */ - xenbus_dev_fatal(dev, error, "writing new state"); - goto out; - } - ivars->xd_state = newstate; - wakeup(&ivars->xd_state); - out: - sx_xunlock(&ivars->xd_lock); - return (0); - - case XENBUS_IVAR_NODE: - case XENBUS_IVAR_TYPE: - case XENBUS_IVAR_OTHEREND_ID: - case XENBUS_IVAR_OTHEREND_PATH: - /* - * These variables are read-only. - */ - return (EINVAL); - } - - return (ENOENT); -} - -SYSCTL_NODE(_dev, OID_AUTO, xen, CTLFLAG_RD, NULL, "Xen"); -SYSCTL_INT(_dev_xen, OID_AUTO, xsd_port, CTLFLAG_RD, &xen_store_evtchn, 0, ""); -SYSCTL_ULONG(_dev_xen, OID_AUTO, xsd_kva, CTLFLAG_RD, (u_long *) &xen_store, 0, ""); - -static device_method_t xenbus_methods[] = { - /* Device interface */ - DEVMETHOD(device_identify, xenbus_identify), - DEVMETHOD(device_probe, xenbus_probe), - DEVMETHOD(device_attach, xenbus_attach), - DEVMETHOD(device_detach, bus_generic_detach), - DEVMETHOD(device_shutdown, bus_generic_shutdown), - DEVMETHOD(device_suspend, xenbus_suspend), - DEVMETHOD(device_resume, xenbus_resume), - - /* Bus interface */ - DEVMETHOD(bus_print_child, xenbus_print_child), - DEVMETHOD(bus_read_ivar, xenbus_read_ivar), - DEVMETHOD(bus_write_ivar, xenbus_write_ivar), - - { 0, 0 } -}; - -static char driver_name[] = "xenbus"; -static driver_t xenbus_driver = { - driver_name, - xenbus_methods, - sizeof(struct xenbus_softc), -}; -devclass_t xenbus_devclass; - -#ifdef XENHVM -DRIVER_MODULE(xenbus, xenpci, xenbus_driver, xenbus_devclass, 0, 0); -#else -DRIVER_MODULE(xenbus, nexus, xenbus_driver, xenbus_devclass, 0, 0); -#endif diff --git a/sys/xen/xenbus/xenbus_probe_backend.c b/sys/xen/xenbus/xenbus_probe_backend.c deleted file mode 100644 index 20cc49f..0000000 --- a/sys/xen/xenbus/xenbus_probe_backend.c +++ /dev/null @@ -1,308 +0,0 @@ -/****************************************************************************** - * Talks to Xen Store to figure out what devices we have (backend half). - * - * Copyright (C) 2005 Rusty Russell, IBM Corporation - * Copyright (C) 2005 Mike Wray, Hewlett-Packard - * Copyright (C) 2005, 2006 XenSource Ltd - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation; or, when distributed - * separately from the Linux kernel or incorporated into other - * software packages, subject to the following license: - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this source file (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#if 0 -#define DPRINTK(fmt, args...) \ - printf("xenbus_probe (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args) -#else -#define DPRINTK(fmt, args...) ((void)0) -#endif - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/types.h> -#include <sys/cdefs.h> -#include <sys/time.h> -#include <sys/sema.h> -#include <sys/eventhandler.h> -#include <sys/errno.h> -#include <sys/kernel.h> -#include <sys/malloc.h> -#include <sys/module.h> -#include <sys/conf.h> -#include <sys/systm.h> -#include <sys/syslog.h> -#include <sys/proc.h> -#include <sys/bus.h> -#include <sys/sx.h> - -#include <machine/xen/xen-os.h> -#include <xen/hypervisor.h> -#include <machine/xen/xenbus.h> -#include <machine/stdarg.h> - -#include <xen/evtchn.h> -#include <xen/xenbus/xenbus_comms.h> - -#define BUG_ON PANIC_IF -#define semaphore sema -#define rw_semaphore sema -#define DEFINE_SPINLOCK(lock) struct mtx lock -#define DECLARE_MUTEX(lock) struct sema lock -#define u32 uint32_t -#define list_del(head, ent) TAILQ_REMOVE(head, ent, list) -#define simple_strtoul strtoul -#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) -#define list_empty TAILQ_EMPTY - -extern struct xendev_list_head xenbus_device_backend_list; -#if 0 -static int xenbus_uevent_backend(struct device *dev, char **envp, - int num_envp, char *buffer, int buffer_size); -#endif -static int xenbus_probe_backend(const char *type, const char *domid); - -static int read_frontend_details(struct xenbus_device *xendev) -{ - return read_otherend_details(xendev, "frontend-id", "frontend"); -} - -/* backend/<type>/<fe-uuid>/<id> => <type>-<fe-domid>-<id> */ -static int backend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename) -{ - int domid, err; - const char *devid, *type, *frontend; - unsigned int typelen; - - type = strchr(nodename, '/'); - if (!type) - return -EINVAL; - type++; - typelen = strcspn(type, "/"); - if (!typelen || type[typelen] != '/') - return -EINVAL; - - devid = strrchr(nodename, '/') + 1; - - err = xenbus_gather(XBT_NIL, nodename, "frontend-id", "%i", &domid, - "frontend", NULL, &frontend, - NULL); - if (err) - return err; - if (strlen(frontend) == 0) - err = -ERANGE; - if (!err && !xenbus_exists(XBT_NIL, frontend, "")) - err = -ENOENT; - kfree(frontend); - - if (err) - return err; - - if (snprintf(bus_id, BUS_ID_SIZE, - "%.*s-%i-%s", typelen, type, domid, devid) >= BUS_ID_SIZE) - return -ENOSPC; - return 0; -} - -static struct xen_bus_type xenbus_backend = { - .root = "backend", - .levels = 3, /* backend/type/<frontend>/<id> */ - .get_bus_id = backend_bus_id, - .probe = xenbus_probe_backend, - .bus = &xenbus_device_backend_list, - -#if 0 - .error = -ENODEV, - .bus = { - .name = "xen-backend", - .match = xenbus_match, - .probe = xenbus_dev_probe, - .remove = xenbus_dev_remove, -// .shutdown = xenbus_dev_shutdown, - .uevent = xenbus_uevent_backend, - }, - .dev = { - .bus_id = "xen-backend", - }, -#endif -}; - -#if 0 -static int xenbus_uevent_backend(struct device *dev, char **envp, - int num_envp, char *buffer, int buffer_size) -{ - struct xenbus_device *xdev; - struct xenbus_driver *drv; - int i = 0; - int length = 0; - - DPRINTK(""); - - if (dev == NULL) - return -ENODEV; - - xdev = to_xenbus_device(dev); - if (xdev == NULL) - return -ENODEV; -2 - /* stuff we want to pass to /sbin/hotplug */ - add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "XENBUS_TYPE=%s", xdev->devicetype); - - add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "XENBUS_PATH=%s", xdev->nodename); - - add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "XENBUS_BASE_PATH=%s", xenbus_backend.root); - - /* terminate, set to next free slot, shrink available space */ - envp[i] = NULL; - envp = &envp[i]; - num_envp -= i; - buffer = &buffer[length]; - buffer_size -= length; - - if (dev->driver) { - drv = to_xenbus_driver(dev->driver); - if (drv && drv->uevent) - return drv->uevent(xdev, envp, num_envp, buffer, - buffer_size); - } - - return 0; -} -#endif - -int xenbus_register_backend(struct xenbus_driver *drv) -{ - drv->read_otherend_details = read_frontend_details; - - return xenbus_register_driver_common(drv, &xenbus_backend); -} - -/* backend/<typename>/<frontend-uuid>/<name> */ -static int xenbus_probe_backend_unit(const char *dir, - const char *type, - const char *name) -{ - char *nodename; - int err; - - nodename = kasprintf("%s/%s", dir, name); - if (!nodename) - return -ENOMEM; - - DPRINTK("%s\n", nodename); - - err = xenbus_probe_node(&xenbus_backend, type, nodename); - kfree(nodename); - return err; -} - -/* backend/<typename>/<frontend-domid> */ -static int xenbus_probe_backend(const char *type, const char *domid) -{ - char *nodename; - int err = 0; - char **dir; - unsigned int i, dir_n = 0; - - DPRINTK(""); - - nodename = kasprintf("%s/%s/%s", xenbus_backend.root, type, domid); - if (!nodename) - return -ENOMEM; - - dir = xenbus_directory(XBT_NIL, nodename, "", &dir_n); - if (IS_ERR(dir)) { - kfree(nodename); - return PTR_ERR(dir); - } - - for (i = 0; i < dir_n; i++) { - err = xenbus_probe_backend_unit(nodename, type, dir[i]); - if (err) - break; - } - kfree(dir); - kfree(nodename); - return err; -} - -static void backend_changed(struct xenbus_watch *watch, - const char **vec, unsigned int len) -{ - DPRINTK(""); - - dev_changed(vec[XS_WATCH_PATH], &xenbus_backend); -} - -static struct xenbus_watch be_watch = { - .node = "backend", - .callback = backend_changed, -}; -#if 0 -void xenbus_backend_suspend(int (*fn)(struct device *, void *)) -{ - DPRINTK(""); - if (!xenbus_backend.error) - bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, fn); -} - -void xenbus_backend_resume(int (*fn)(struct device *, void *)) -{ - DPRINTK(""); - if (!xenbus_backend.error) - bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, fn); -} -#endif -void xenbus_backend_probe_and_watch(void) -{ - xenbus_probe_devices(&xenbus_backend); - register_xenbus_watch(&be_watch); -} - -#if 0 -void xenbus_backend_bus_register(void) -{ - xenbus_backend.error = bus_register(&xenbus_backend.bus); - if (xenbus_backend.error) - log(LOG_WARNING, - "XENBUS: Error registering backend bus: %i\n", - xenbus_backend.error); -} - -void xenbus_backend_device_register(void) -{ - if (xenbus_backend.error) - return; - - xenbus_backend.error = device_register(&xenbus_backend.dev); - if (xenbus_backend.error) { - bus_unregister(&xenbus_backend.bus); - log(LOG_WARNING, - "XENBUS: Error registering backend device: %i\n", - xenbus_backend.error); - } -} -#endif diff --git a/sys/xen/xenbus/xenbus_xs.c b/sys/xen/xenbus/xenbus_xs.c deleted file mode 100644 index 9312255..0000000 --- a/sys/xen/xenbus/xenbus_xs.c +++ /dev/null @@ -1,935 +0,0 @@ -/****************************************************************************** - * xenbus_xs.c - * - * This is the kernel equivalent of the "xs" library. We don't need everything - * and we use xenbus_comms for communication. - * - * Copyright (C) 2005 Rusty Russell, IBM Corporation - * - * This file may be distributed separately from the Linux kernel, or - * incorporated into other software packages, subject to the following license: - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this source file (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/uio.h> -#include <sys/kernel.h> -#include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/sx.h> -#include <sys/syslog.h> -#include <sys/malloc.h> -#include <sys/systm.h> -#include <sys/proc.h> -#include <sys/kthread.h> -#include <sys/unistd.h> - -#include <machine/xen/xen-os.h> -#include <xen/hypervisor.h> -#include <machine/stdarg.h> - -#include <xen/xenbus/xenbusvar.h> -#include <xen/xenbus/xenbus_comms.h> -#include <xen/interface/hvm/params.h> - -#include <vm/vm.h> -#include <vm/pmap.h> - -static int xs_process_msg(enum xsd_sockmsg_type *type); - -int xenwatch_running = 0; -int xenbus_running = 0; -int xen_store_evtchn; - -struct xs_stored_msg { - TAILQ_ENTRY(xs_stored_msg) list; - - struct xsd_sockmsg hdr; - - union { - /* Queued replies. */ - struct { - char *body; - } reply; - - /* Queued watch events. */ - struct { - struct xenbus_watch *handle; - char **vec; - unsigned int vec_size; - } watch; - } u; -}; - -struct xs_handle { - /* A list of replies. Currently only one will ever be outstanding. */ - TAILQ_HEAD(xs_handle_list, xs_stored_msg) reply_list; - struct mtx reply_lock; - int reply_waitq; - - /* One request at a time. */ - struct sx request_mutex; - - /* Protect transactions against save/restore. */ - struct sx suspend_mutex; -}; - -static struct xs_handle xs_state; - -/* List of registered watches, and a lock to protect it. */ -static LIST_HEAD(watch_list_head, xenbus_watch) watches; -static struct mtx watches_lock; -/* List of pending watch callback events, and a lock to protect it. */ -static TAILQ_HEAD(event_list_head, xs_stored_msg) watch_events; -static struct mtx watch_events_lock; - -/* - * Details of the xenwatch callback kernel thread. The thread waits on the - * watch_events_waitq for work to do (queued on watch_events list). When it - * wakes up it acquires the xenwatch_mutex before reading the list and - * carrying out work. - */ -static pid_t xenwatch_pid; -struct sx xenwatch_mutex; -static int watch_events_waitq; - -#define xsd_error_count (sizeof(xsd_errors) / sizeof(xsd_errors[0])) - -static int -xs_get_error(const char *errorstring) -{ - unsigned int i; - - for (i = 0; i < xsd_error_count; i++) { - if (!strcmp(errorstring, xsd_errors[i].errstring)) - return (xsd_errors[i].errnum); - } - log(LOG_WARNING, "XENBUS xen store gave: unknown error %s", - errorstring); - return (EINVAL); -} - -extern void kdb_backtrace(void); - -static int -xs_read_reply(enum xsd_sockmsg_type *type, unsigned int *len, void **result) -{ - struct xs_stored_msg *msg; - char *body; - int error; - - mtx_lock(&xs_state.reply_lock); - - while (TAILQ_EMPTY(&xs_state.reply_list)) { - while (TAILQ_EMPTY(&xs_state.reply_list)) { - error = mtx_sleep(&xs_state.reply_waitq, - &xs_state.reply_lock, - PCATCH, "xswait", hz/10); - if (error && error != EWOULDBLOCK) { - mtx_unlock(&xs_state.reply_lock); - return (error); - } - } - } - - msg = TAILQ_FIRST(&xs_state.reply_list); - TAILQ_REMOVE(&xs_state.reply_list, msg, list); - - mtx_unlock(&xs_state.reply_lock); - - *type = msg->hdr.type; - if (len) - *len = msg->hdr.len; - body = msg->u.reply.body; - - free(msg, M_DEVBUF); - *result = body; - return (0); -} - -#if 0 -/* Emergency write. UNUSED*/ -void xenbus_debug_write(const char *str, unsigned int count) -{ - struct xsd_sockmsg msg = { 0 }; - - msg.type = XS_DEBUG; - msg.len = sizeof("print") + count + 1; - - sx_xlock(&xs_state.request_mutex); - xb_write(&msg, sizeof(msg)); - xb_write("print", sizeof("print")); - xb_write(str, count); - xb_write("", 1); - sx_xunlock(&xs_state.request_mutex); -} - -#endif - -int -xenbus_dev_request_and_reply(struct xsd_sockmsg *msg, void **result) -{ - struct xsd_sockmsg req_msg = *msg; - int error; - - if (req_msg.type == XS_TRANSACTION_START) - sx_slock(&xs_state.suspend_mutex); - - sx_xlock(&xs_state.request_mutex); - - error = xb_write(msg, sizeof(*msg) + msg->len, - &xs_state.request_mutex.lock_object); - if (error) { - msg->type = XS_ERROR; - } else { - error = xs_read_reply(&msg->type, &msg->len, result); - } - - sx_xunlock(&xs_state.request_mutex); - - if ((msg->type == XS_TRANSACTION_END) || - ((req_msg.type == XS_TRANSACTION_START) && - (msg->type == XS_ERROR))) - sx_sunlock(&xs_state.suspend_mutex); - - return (error); -} - -/* - * Send message to xs. The reply is returned in *result and should be - * fred with free(*result, M_DEVBUF). Return zero on success or an - * error code on failure. - */ -static int -xs_talkv(struct xenbus_transaction t, enum xsd_sockmsg_type type, - const struct iovec *iovec, unsigned int num_vecs, - unsigned int *len, void **result) -{ - struct xsd_sockmsg msg; - void *ret = NULL; - unsigned int i; - int error; - - msg.tx_id = t.id; - msg.req_id = 0; - msg.type = type; - msg.len = 0; - for (i = 0; i < num_vecs; i++) - msg.len += iovec[i].iov_len; - - sx_xlock(&xs_state.request_mutex); - - error = xb_write(&msg, sizeof(msg), - &xs_state.request_mutex.lock_object); - if (error) { - sx_xunlock(&xs_state.request_mutex); - printf("xs_talkv failed %d\n", error); - return (error); - } - - for (i = 0; i < num_vecs; i++) { - error = xb_write(iovec[i].iov_base, iovec[i].iov_len, - &xs_state.request_mutex.lock_object); - if (error) { - sx_xunlock(&xs_state.request_mutex); - printf("xs_talkv failed %d\n", error); - return (error); - } - } - - error = xs_read_reply(&msg.type, len, &ret); - - sx_xunlock(&xs_state.request_mutex); - - if (error) - return (error); - - if (msg.type == XS_ERROR) { - error = xs_get_error(ret); - free(ret, M_DEVBUF); - return (error); - } - -#if 0 - if ((xenwatch_running == 0) && (xenwatch_inline == 0)) { - xenwatch_inline = 1; - while (!TAILQ_EMPTY(&watch_events) - && xenwatch_running == 0) { - - struct xs_stored_msg *wmsg = TAILQ_FIRST(&watch_events); - TAILQ_REMOVE(&watch_events, wmsg, list); - - wmsg->u.watch.handle->callback( - wmsg->u.watch.handle, - (const char **)wmsg->u.watch.vec, - wmsg->u.watch.vec_size); - free(wmsg->u.watch.vec, M_DEVBUF); - free(wmsg, M_DEVBUF); - } - xenwatch_inline = 0; - } -#endif - KASSERT(msg.type == type, ("bad xenstore message type")); - - if (result) - *result = ret; - else - free(ret, M_DEVBUF); - - return (0); -} - -/* Simplified version of xs_talkv: single message. */ -static int -xs_single(struct xenbus_transaction t, enum xsd_sockmsg_type type, - const char *string, unsigned int *len, void **result) -{ - struct iovec iovec; - - iovec.iov_base = (void *)(uintptr_t) string; - iovec.iov_len = strlen(string) + 1; - - return (xs_talkv(t, type, &iovec, 1, len, result)); -} - -static unsigned int -count_strings(const char *strings, unsigned int len) -{ - unsigned int num; - const char *p; - - for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1) - num++; - - return num; -} - -/* Return the path to dir with /name appended. Buffer must be kfree()'ed. */ -static char * -join(const char *dir, const char *name) -{ - char *buffer; - - buffer = malloc(strlen(dir) + strlen("/") + strlen(name) + 1, - M_DEVBUF, M_WAITOK); - - strcpy(buffer, dir); - if (strcmp(name, "")) { - strcat(buffer, "/"); - strcat(buffer, name); - } - - return (buffer); -} - -static char ** -split(char *strings, unsigned int len, unsigned int *num) -{ - char *p, **ret; - - /* Count the strings. */ - *num = count_strings(strings, len) + 1; - - /* Transfer to one big alloc for easy freeing. */ - ret = malloc(*num * sizeof(char *) + len, M_DEVBUF, M_WAITOK); - memcpy(&ret[*num], strings, len); - free(strings, M_DEVBUF); - - strings = (char *)&ret[*num]; - for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1) - ret[(*num)++] = p; - - ret[*num] = strings + len; - - return ret; -} - -/* - * Return the contents of a directory in *result which should be freed - * with free(*result, M_DEVBUF). - */ -int -xenbus_directory(struct xenbus_transaction t, const char *dir, - const char *node, unsigned int *num, char ***result) -{ - char *strings, *path; - unsigned int len = 0; - int error; - - path = join(dir, node); - error = xs_single(t, XS_DIRECTORY, path, &len, (void **) &strings); - free(path, M_DEVBUF); - if (error) - return (error); - - *result = split(strings, len, num); - return (0); -} - -/* - * Check if a path exists. Return 1 if it does. - */ -int -xenbus_exists(struct xenbus_transaction t, const char *dir, const char *node) -{ - char **d; - int error, dir_n; - - error = xenbus_directory(t, dir, node, &dir_n, &d); - if (error) - return (0); - free(d, M_DEVBUF); - return (1); -} - -/* - * Get the value of a single file. Returns the contents in *result - * which should be freed with free(*result, M_DEVBUF) after use. - * The length of the value in bytes is returned in *len. - */ -int -xenbus_read(struct xenbus_transaction t, const char *dir, const char *node, - unsigned int *len, void **result) -{ - char *path; - void *ret; - int error; - - path = join(dir, node); - error = xs_single(t, XS_READ, path, len, &ret); - free(path, M_DEVBUF); - if (error) - return (error); - *result = ret; - return (0); -} - -/* - * Write the value of a single file. Returns error on failure. - */ -int -xenbus_write(struct xenbus_transaction t, const char *dir, const char *node, - const char *string) -{ - char *path; - struct iovec iovec[2]; - int error; - - path = join(dir, node); - - iovec[0].iov_base = (void *)(uintptr_t) path; - iovec[0].iov_len = strlen(path) + 1; - iovec[1].iov_base = (void *)(uintptr_t) string; - iovec[1].iov_len = strlen(string); - - error = xs_talkv(t, XS_WRITE, iovec, 2, NULL, NULL); - free(path, M_DEVBUF); - - return (error); -} - -/* - * Create a new directory. - */ -int -xenbus_mkdir(struct xenbus_transaction t, const char *dir, const char *node) -{ - char *path; - int ret; - - path = join(dir, node); - ret = xs_single(t, XS_MKDIR, path, NULL, NULL); - free(path, M_DEVBUF); - - return (ret); -} - -/* - * Destroy a file or directory (directories must be empty). - */ -int -xenbus_rm(struct xenbus_transaction t, const char *dir, const char *node) -{ - char *path; - int ret; - - path = join(dir, node); - ret = xs_single(t, XS_RM, path, NULL, NULL); - free(path, M_DEVBUF); - - return (ret); -} - -/* - * Start a transaction: changes by others will not be seen during this - * transaction, and changes will not be visible to others until end. - */ -int -xenbus_transaction_start(struct xenbus_transaction *t) -{ - char *id_str; - int error; - - sx_slock(&xs_state.suspend_mutex); - error = xs_single(XBT_NIL, XS_TRANSACTION_START, "", NULL, - (void **) &id_str); - if (error) { - sx_sunlock(&xs_state.suspend_mutex); - return (error); - } - - t->id = strtoul(id_str, NULL, 0); - free(id_str, M_DEVBUF); - - return (0); -} - -/* - * End a transaction. If abandon is true, transaction is discarded - * instead of committed. - */ -int xenbus_transaction_end(struct xenbus_transaction t, int abort) -{ - char abortstr[2]; - int error; - - if (abort) - strcpy(abortstr, "F"); - else - strcpy(abortstr, "T"); - - error = xs_single(t, XS_TRANSACTION_END, abortstr, NULL, NULL); - - sx_sunlock(&xs_state.suspend_mutex); - - return (error); -} - -/* Single read and scanf: returns zero or errno. */ -int -xenbus_scanf(struct xenbus_transaction t, - const char *dir, const char *node, int *scancountp, const char *fmt, ...) -{ - va_list ap; - int error, ns; - char *val; - - error = xenbus_read(t, dir, node, NULL, (void **) &val); - if (error) - return (error); - - va_start(ap, fmt); - ns = vsscanf(val, fmt, ap); - va_end(ap); - free(val, M_DEVBUF); - /* Distinctive errno. */ - if (ns == 0) - return (ERANGE); - if (scancountp) - *scancountp = ns; - return (0); -} - -/* Single printf and write: returns zero or errno. */ -int -xenbus_printf(struct xenbus_transaction t, - const char *dir, const char *node, const char *fmt, ...) -{ - va_list ap; - int error, ret; -#define PRINTF_BUFFER_SIZE 4096 - char *printf_buffer; - - printf_buffer = malloc(PRINTF_BUFFER_SIZE, M_DEVBUF, M_WAITOK); - - va_start(ap, fmt); - ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap); - va_end(ap); - - KASSERT(ret <= PRINTF_BUFFER_SIZE-1, ("xenbus_printf: message too large")); - error = xenbus_write(t, dir, node, printf_buffer); - - free(printf_buffer, M_DEVBUF); - - return (error); -} - -/* Takes tuples of names, scanf-style args, and void **, NULL terminated. */ -int -xenbus_gather(struct xenbus_transaction t, const char *dir, ...) -{ - va_list ap; - const char *name; - int error, i; - - for (i = 0; i < 10000; i++) - HYPERVISOR_yield(); - - va_start(ap, dir); - error = 0; - while (error == 0 && (name = va_arg(ap, char *)) != NULL) { - const char *fmt = va_arg(ap, char *); - void *result = va_arg(ap, void *); - char *p; - - error = xenbus_read(t, dir, name, NULL, (void **) &p); - if (error) - break; - - if (fmt) { - if (sscanf(p, fmt, result) == 0) - error = EINVAL; - free(p, M_DEVBUF); - } else - *(char **)result = p; - } - va_end(ap); - - return (error); -} - -static int -xs_watch(const char *path, const char *token) -{ - struct iovec iov[2]; - - iov[0].iov_base = (void *)(uintptr_t) path; - iov[0].iov_len = strlen(path) + 1; - iov[1].iov_base = (void *)(uintptr_t) token; - iov[1].iov_len = strlen(token) + 1; - - return (xs_talkv(XBT_NIL, XS_WATCH, iov, 2, NULL, NULL)); -} - -static int -xs_unwatch(const char *path, const char *token) -{ - struct iovec iov[2]; - - iov[0].iov_base = (void *)(uintptr_t) path; - iov[0].iov_len = strlen(path) + 1; - iov[1].iov_base = (void *)(uintptr_t) token; - iov[1].iov_len = strlen(token) + 1; - - return (xs_talkv(XBT_NIL, XS_UNWATCH, iov, 2, NULL, NULL)); -} - -static struct xenbus_watch * -find_watch(const char *token) -{ - struct xenbus_watch *i, *cmp; - - cmp = (void *)strtoul(token, NULL, 16); - - LIST_FOREACH(i, &watches, list) - if (i == cmp) - return (i); - - return (NULL); -} - -/* Register callback to watch this node. */ -int -register_xenbus_watch(struct xenbus_watch *watch) -{ - /* Pointer in ascii is the token. */ - char token[sizeof(watch) * 2 + 1]; - int error; - - sprintf(token, "%lX", (long)watch); - - sx_slock(&xs_state.suspend_mutex); - - mtx_lock(&watches_lock); - KASSERT(find_watch(token) == NULL, ("watch already registered")); - LIST_INSERT_HEAD(&watches, watch, list); - mtx_unlock(&watches_lock); - - error = xs_watch(watch->node, token); - - /* Ignore errors due to multiple registration. */ - if (error == EEXIST) { - mtx_lock(&watches_lock); - LIST_REMOVE(watch, list); - mtx_unlock(&watches_lock); - } - - sx_sunlock(&xs_state.suspend_mutex); - - return (error); -} - -void -unregister_xenbus_watch(struct xenbus_watch *watch) -{ - struct xs_stored_msg *msg, *tmp; - char token[sizeof(watch) * 2 + 1]; - int error; - - sprintf(token, "%lX", (long)watch); - - sx_slock(&xs_state.suspend_mutex); - - mtx_lock(&watches_lock); - KASSERT(find_watch(token), ("watch not registered")); - LIST_REMOVE(watch, list); - mtx_unlock(&watches_lock); - - error = xs_unwatch(watch->node, token); - if (error) - log(LOG_WARNING, "XENBUS Failed to release watch %s: %i\n", - watch->node, error); - - sx_sunlock(&xs_state.suspend_mutex); - - /* Cancel pending watch events. */ - mtx_lock(&watch_events_lock); - TAILQ_FOREACH_SAFE(msg, &watch_events, list, tmp) { - if (msg->u.watch.handle != watch) - continue; - TAILQ_REMOVE(&watch_events, msg, list); - free(msg->u.watch.vec, M_DEVBUF); - free(msg, M_DEVBUF); - } - mtx_unlock(&watch_events_lock); - - /* Flush any currently-executing callback, unless we are it. :-) */ - if (curproc->p_pid != xenwatch_pid) { - sx_xlock(&xenwatch_mutex); - sx_xunlock(&xenwatch_mutex); - } -} - -void -xs_suspend(void) -{ - - sx_xlock(&xs_state.suspend_mutex); - sx_xlock(&xs_state.request_mutex); -} - -void -xs_resume(void) -{ - struct xenbus_watch *watch; - char token[sizeof(watch) * 2 + 1]; - - sx_xunlock(&xs_state.request_mutex); - - /* No need for watches_lock: the suspend_mutex is sufficient. */ - LIST_FOREACH(watch, &watches, list) { - sprintf(token, "%lX", (long)watch); - xs_watch(watch->node, token); - } - - sx_xunlock(&xs_state.suspend_mutex); -} - -static void -xenwatch_thread(void *unused) -{ - struct xs_stored_msg *msg; - - for (;;) { - - mtx_lock(&watch_events_lock); - while (TAILQ_EMPTY(&watch_events)) - mtx_sleep(&watch_events_waitq, - &watch_events_lock, - PWAIT | PCATCH, "waitev", hz/10); - - mtx_unlock(&watch_events_lock); - sx_xlock(&xenwatch_mutex); - - mtx_lock(&watch_events_lock); - msg = TAILQ_FIRST(&watch_events); - if (msg) - TAILQ_REMOVE(&watch_events, msg, list); - mtx_unlock(&watch_events_lock); - - if (msg != NULL) { - /* - * XXX There are messages coming in with a NULL callback. - * XXX This deserves further investigation; the workaround - * XXX here simply prevents the kernel from panic'ing - * XXX on startup. - */ - if (msg->u.watch.handle->callback != NULL) - msg->u.watch.handle->callback( - msg->u.watch.handle, - (const char **)msg->u.watch.vec, - msg->u.watch.vec_size); - free(msg->u.watch.vec, M_DEVBUF); - free(msg, M_DEVBUF); - } - - sx_xunlock(&xenwatch_mutex); - } -} - -static int -xs_process_msg(enum xsd_sockmsg_type *type) -{ - struct xs_stored_msg *msg; - char *body; - int error; - - msg = malloc(sizeof(*msg), M_DEVBUF, M_WAITOK); - mtx_lock(&xs_state.reply_lock); - error = xb_read(&msg->hdr, sizeof(msg->hdr), - &xs_state.reply_lock.lock_object); - mtx_unlock(&xs_state.reply_lock); - if (error) { - free(msg, M_DEVBUF); - return (error); - } - - body = malloc(msg->hdr.len + 1, M_DEVBUF, M_WAITOK); - mtx_lock(&xs_state.reply_lock); - error = xb_read(body, msg->hdr.len, - &xs_state.reply_lock.lock_object); - mtx_unlock(&xs_state.reply_lock); - if (error) { - free(body, M_DEVBUF); - free(msg, M_DEVBUF); - return (error); - } - body[msg->hdr.len] = '\0'; - - *type = msg->hdr.type; - if (msg->hdr.type == XS_WATCH_EVENT) { - msg->u.watch.vec = split(body, msg->hdr.len, - &msg->u.watch.vec_size); - - mtx_lock(&watches_lock); - msg->u.watch.handle = find_watch( - msg->u.watch.vec[XS_WATCH_TOKEN]); - if (msg->u.watch.handle != NULL) { - mtx_lock(&watch_events_lock); - TAILQ_INSERT_TAIL(&watch_events, msg, list); - wakeup(&watch_events_waitq); - mtx_unlock(&watch_events_lock); - } else { - free(msg->u.watch.vec, M_DEVBUF); - free(msg, M_DEVBUF); - } - mtx_unlock(&watches_lock); - } else { - msg->u.reply.body = body; - mtx_lock(&xs_state.reply_lock); - TAILQ_INSERT_TAIL(&xs_state.reply_list, msg, list); - wakeup(&xs_state.reply_waitq); - mtx_unlock(&xs_state.reply_lock); - } - - return 0; -} - -static void -xenbus_thread(void *unused) -{ - int error; - enum xsd_sockmsg_type type; - xenbus_running = 1; - - for (;;) { - error = xs_process_msg(&type); - if (error) - printf("XENBUS error %d while reading message\n", - error); - } -} - -#ifdef XENHVM -static unsigned long xen_store_mfn; -char *xen_store; - -static inline unsigned long -hvm_get_parameter(int index) -{ - struct xen_hvm_param xhv; - int error; - - xhv.domid = DOMID_SELF; - xhv.index = index; - error = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv); - if (error) { - printf("hvm_get_parameter: failed to get %d, error %d\n", - index, error); - return (0); - } - return (xhv.value); -} - -#endif - -int -xs_init(void) -{ - int error; - struct proc *p; - -#ifdef XENHVM - xen_store_evtchn = hvm_get_parameter(HVM_PARAM_STORE_EVTCHN); - xen_store_mfn = hvm_get_parameter(HVM_PARAM_STORE_PFN); - xen_store = pmap_mapdev(xen_store_mfn * PAGE_SIZE, PAGE_SIZE); -#else - xen_store_evtchn = xen_start_info->store_evtchn; -#endif - - TAILQ_INIT(&xs_state.reply_list); - TAILQ_INIT(&watch_events); - sx_init(&xenwatch_mutex, "xenwatch"); - - - mtx_init(&xs_state.reply_lock, "state reply", NULL, MTX_DEF); - sx_init(&xs_state.request_mutex, "xenstore request"); - sx_init(&xs_state.suspend_mutex, "xenstore suspend"); - - -#if 0 - mtx_init(&xs_state.suspend_mutex, "xenstore suspend", NULL, MTX_DEF); - sema_init(&xs_state.request_mutex, 1, "xenstore request"); - sema_init(&xenwatch_mutex, 1, "xenwatch"); -#endif - mtx_init(&watches_lock, "watches", NULL, MTX_DEF); - mtx_init(&watch_events_lock, "watch events", NULL, MTX_DEF); - - /* Initialize the shared memory rings to talk to xenstored */ - error = xb_init_comms(); - if (error) - return (error); - - xenwatch_running = 1; - error = kproc_create(xenwatch_thread, NULL, &p, - RFHIGHPID, 0, "xenwatch"); - if (error) - return (error); - xenwatch_pid = p->p_pid; - - error = kproc_create(xenbus_thread, NULL, NULL, - RFHIGHPID, 0, "xenbus"); - - return (error); -} diff --git a/sys/xen/xenbus/xenbusb.c b/sys/xen/xenbus/xenbusb.c new file mode 100644 index 0000000..49facb6 --- /dev/null +++ b/sys/xen/xenbus/xenbusb.c @@ -0,0 +1,878 @@ +/****************************************************************************** + * Copyright (C) 2010 Spectra Logic Corporation + * Copyright (C) 2008 Doug Rabson + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * Copyright (C) 2005 Mike Wray, Hewlett-Packard + * Copyright (C) 2005 XenSource Ltd + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * \file xenbusb.c + * + * \brief Shared support functions for managing the NewBus busses that contain + * Xen front and back end device instances. + * + * The NewBus implementation of XenBus attaches a xenbusb_front and xenbusb_back + * child bus to the xenstore device. This strategy allows the small differences + * in the handling of XenBus operations for front and back devices to be handled + * as overrides in xenbusb_front/back.c. Front and back specific device + * classes are also provided so device drivers can register for the devices they + * can handle without the need to filter within their probe routines. The + * net result is a device hierarchy that might look like this: + * + * xenstore0/ + * xenbusb_front0/ + * xn0 + * xbd0 + * xbd1 + * xenbusb_back0/ + * xbbd0 + * xnb0 + * xnb1 + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/sbuf.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/systm.h> +#include <sys/sx.h> +#include <sys/taskqueue.h> + +#include <machine/xen/xen-os.h> +#include <machine/stdarg.h> + +#include <xen/gnttab.h> +#include <xen/xenstore/xenstorevar.h> +#include <xen/xenbus/xenbusb.h> +#include <xen/xenbus/xenbusvar.h> + +/*------------------------- Private Functions --------------------------------*/ +/** + * \brief Deallocate XenBus device instance variables. + * + * \param ivars The instance variable block to free. + */ +static void +xenbusb_free_child_ivars(struct xenbus_device_ivars *ivars) +{ + if (ivars->xd_otherend_watch.node != NULL) { + xs_unregister_watch(&ivars->xd_otherend_watch); + free(ivars->xd_otherend_watch.node, M_XENBUS); + ivars->xd_otherend_watch.node = NULL; + } + + if (ivars->xd_node != NULL) { + free(ivars->xd_node, M_XENBUS); + ivars->xd_node = NULL; + } + + if (ivars->xd_type != NULL) { + free(ivars->xd_type, M_XENBUS); + ivars->xd_type = NULL; + } + + if (ivars->xd_otherend_path != NULL) { + free(ivars->xd_otherend_path, M_XENBUS); + ivars->xd_otherend_path = NULL; + } + + free(ivars, M_XENBUS); +} + +/** + * XenBus watch callback registered against the "state" XenStore + * node of the other-end of a split device connection. + * + * This callback is invoked whenever the state of a device instance's + * peer changes. + * + * \param watch The xs_watch object used to register this callback + * function. + * \param vec An array of pointers to NUL terminated strings containing + * watch event data. The vector should be indexed via the + * xs_watch_type enum in xs_wire.h. + * \param vec_size The number of elements in vec. + * + * \return The device_t of the found device if any, or NULL. + * + * \note device_t is a pointer type, so it can be compared against + * NULL for validity. + */ +static void +xenbusb_otherend_changed(struct xs_watch *watch, const char **vec, + unsigned int vec_size __unused) +{ + struct xenbus_device_ivars *ivars; + device_t dev; + enum xenbus_state newstate; + + ivars = (struct xenbus_device_ivars *) watch; + dev = ivars->xd_dev; + + if (!ivars->xd_otherend_path + || strncmp(ivars->xd_otherend_path, vec[XS_WATCH_PATH], + strlen(ivars->xd_otherend_path))) + return; + + newstate = xenbus_read_driver_state(ivars->xd_otherend_path); + XENBUS_OTHEREND_CHANGED(dev, newstate); +} + +/** + * Search our internal record of configured devices (not the XenStore) + * to determine if the XenBus device indicated by \a node is known to + * the system. + * + * \param dev The XenBus bus instance to search for device children. + * \param node The XenStore node path for the device to find. + * + * \return The device_t of the found device if any, or NULL. + * + * \note device_t is a pointer type, so it can be compared against + * NULL for validity. + */ +static device_t +xenbusb_device_exists(device_t dev, const char *node) +{ + device_t *kids; + device_t result; + struct xenbus_device_ivars *ivars; + int i, count; + + if (device_get_children(dev, &kids, &count)) + return (FALSE); + + result = NULL; + for (i = 0; i < count; i++) { + ivars = device_get_ivars(kids[i]); + if (!strcmp(ivars->xd_node, node)) { + result = kids[i]; + break; + } + } + free(kids, M_TEMP); + + return (result); +} + +static void +xenbusb_delete_child(device_t dev, device_t child) +{ + struct xenbus_device_ivars *ivars; + + ivars = device_get_ivars(child); + + /* + * We no longer care about the otherend of the + * connection. Cancel the watch now so that we + * don't try to handle an event for a partially + * detached child. + */ + if (ivars->xd_otherend_watch.node != NULL) + xs_unregister_watch(&ivars->xd_otherend_watch); + + device_delete_child(dev, child); + xenbusb_free_child_ivars(ivars); +} + +/** + * \param dev The NewBus device representing this XenBus bus. + * \param child The NewBus device representing a child of dev%'s XenBus bus. + */ +static void +xenbusb_verify_device(device_t dev, device_t child) +{ + if (xs_exists(XST_NIL, xenbus_get_node(child), "") == 0) { + + /* + * Device tree has been removed from Xenbus. + * Tear down the device. + */ + xenbusb_delete_child(dev, child); + } +} + +/** + * \brief Enumerate the devices on a XenBus bus and register them with + * the NewBus device tree. + * + * xenbusb_enumerate_bus() will create entries (in state DS_NOTPRESENT) + * for nodes that appear in the XenStore, but will not invoke probe/attach + * operations on drivers. Probe/Attach processing must be separately + * performed via an invocation of xenbusb_probe_children(). This is usually + * done via the xbs_probe_children task. + * + * \param xbs XenBus Bus device softc of the owner of the bus to enumerate. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +static int +xenbusb_enumerate_bus(struct xenbusb_softc *xbs) +{ + const char **types; + u_int type_idx; + u_int type_count; + int error; + + error = xs_directory(XST_NIL, xbs->xbs_node, "", &type_count, &types); + if (error) + return (error); + + for (type_idx = 0; type_idx < type_count; type_idx++) + XENBUSB_ENUMERATE_TYPE(xbs->xbs_dev, types[type_idx]); + + free(types, M_XENSTORE); + + return (0); +} + +/** + * Handler for all generic XenBus device systcl nodes. + */ +static int +xenbusb_device_sysctl_handler(SYSCTL_HANDLER_ARGS) +{ + device_t dev; + const char *value; + + dev = (device_t)arg1; + switch (arg2) { + case XENBUS_IVAR_NODE: + value = xenbus_get_node(dev); + break; + case XENBUS_IVAR_TYPE: + value = xenbus_get_type(dev); + break; + case XENBUS_IVAR_STATE: + value = xenbus_strstate(xenbus_get_state(dev)); + break; + case XENBUS_IVAR_OTHEREND_ID: + return (sysctl_handle_int(oidp, NULL, + xenbus_get_otherend_id(dev), + req)); + /* NOTREACHED */ + case XENBUS_IVAR_OTHEREND_PATH: + value = xenbus_get_otherend_path(dev); + break; + default: + return (EINVAL); + } + return (SYSCTL_OUT(req, value, strlen(value))); +} + +/** + * Create read-only systcl nodes for xenbusb device ivar data. + * + * \param dev The XenBus device instance to register with sysctl. + */ +static void +xenbusb_device_sysctl_init(device_t dev) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + + SYSCTL_ADD_PROC(ctx, + SYSCTL_CHILDREN(tree), + OID_AUTO, + "xenstore_path", + CTLFLAG_RD, + dev, + XENBUS_IVAR_NODE, + xenbusb_device_sysctl_handler, + "A", + "XenStore path to device"); + + SYSCTL_ADD_PROC(ctx, + SYSCTL_CHILDREN(tree), + OID_AUTO, + "xenbus_dev_type", + CTLFLAG_RD, + dev, + XENBUS_IVAR_TYPE, + xenbusb_device_sysctl_handler, + "A", + "XenBus device type"); + + SYSCTL_ADD_PROC(ctx, + SYSCTL_CHILDREN(tree), + OID_AUTO, + "xenbus_connection_state", + CTLFLAG_RD, + dev, + XENBUS_IVAR_STATE, + xenbusb_device_sysctl_handler, + "A", + "XenBus state of peer connection"); + + SYSCTL_ADD_PROC(ctx, + SYSCTL_CHILDREN(tree), + OID_AUTO, + "xenbus_peer_domid", + CTLFLAG_RD, + dev, + XENBUS_IVAR_OTHEREND_ID, + xenbusb_device_sysctl_handler, + "I", + "Xen domain ID of peer"); + + SYSCTL_ADD_PROC(ctx, + SYSCTL_CHILDREN(tree), + OID_AUTO, + "xenstore_peer_path", + CTLFLAG_RD, + dev, + XENBUS_IVAR_OTHEREND_PATH, + xenbusb_device_sysctl_handler, + "A", + "XenStore path to peer device"); +} + +/** + * \brief Verify the existance of attached device instances and perform + * probe/attach processing for newly arrived devices. + * + * \param dev The NewBus device representing this XenBus bus. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +static int +xenbusb_probe_children(device_t dev) +{ + device_t *kids; + struct xenbus_device_ivars *ivars; + int i, count; + + if (device_get_children(dev, &kids, &count) == 0) { + for (i = 0; i < count; i++) { + if (device_get_state(kids[i]) != DS_NOTPRESENT) { + /* + * We already know about this one. + * Make sure it's still here. + */ + xenbusb_verify_device(dev, kids[i]); + continue; + } + + if (device_probe_and_attach(kids[i])) { + /* + * Transition device to the closed state + * so the world knows that attachment will + * not occur. + */ + xenbus_set_state(kids[i], XenbusStateClosed); + + /* + * Remove our record of this device. + * So long as it remains in the closed + * state in the XenStore, we will not find + * it again. The state will only change + * if the control domain actively reconfigures + * this device. + */ + xenbusb_delete_child(dev, kids[i]); + + continue; + } + /* + * Augment default newbus provided dynamic sysctl + * variables with the standard ivar contents of + * XenBus devices. + */ + xenbusb_device_sysctl_init(kids[i]); + + /* + * Now that we have a driver managing this device + * that can receive otherend state change events, + * hook up a watch for them. + */ + ivars = device_get_ivars(kids[i]); + xs_register_watch(&ivars->xd_otherend_watch); + } + free(kids, M_TEMP); + } + + return (0); +} + +/** + * \brief Task callback function to perform XenBus probe operations + * from a known safe context. + * + * \param arg The NewBus device_t representing the bus instance to + * on which to perform probe processing. + * \param pending The number of times this task was queued before it could + * be run. + */ +static void +xenbusb_probe_children_cb(void *arg, int pending __unused) +{ + device_t dev = (device_t)arg; + + /* + * Hold Giant until the Giant free newbus changes are committed. + */ + mtx_lock(&Giant); + xenbusb_probe_children(dev); + mtx_unlock(&Giant); +} + +/** + * \brief XenStore watch callback for the root node of the XenStore + * subtree representing a XenBus. + * + * This callback performs, or delegates to the xbs_probe_children task, + * all processing necessary to handle dynmaic device arrival and departure + * events from a XenBus. + * + * \param watch The XenStore watch object associated with this callback. + * \param vec The XenStore watch event data. + * \param len The number of fields in the event data stream. + */ +static void +xenbusb_devices_changed(struct xs_watch *watch, const char **vec, + unsigned int len) +{ + struct xenbusb_softc *xbs; + device_t dev; + char *node; + char *bus; + char *type; + char *id; + char *p; + u_int component; + + xbs = (struct xenbusb_softc *)watch; + dev = xbs->xbs_dev; + + if (len <= XS_WATCH_PATH) { + device_printf(dev, "xenbusb_devices_changed: " + "Short Event Data.\n"); + return; + } + + node = strdup(vec[XS_WATCH_PATH], M_XENBUS); + p = strchr(node, '/'); + if (p == NULL) + goto out; + bus = node; + *p = 0; + type = p + 1; + + p = strchr(type, '/'); + if (p == NULL) + goto out; + *p++ = 0; + + /* + * Extract the device ID. A device ID has one or more path + * components separated by the '/' character. + * + * e.g. "<frontend vm id>/<frontend dev id>" for backend devices. + */ + id = p; + for (component = 0; component < xbs->xbs_id_components; component++) { + p = strchr(p, '/'); + if (p == NULL) + break; + p++; + } + if (p != NULL) + *p = 0; + + if (*id != 0 && component >= xbs->xbs_id_components - 1) { + xenbusb_add_device(xbs->xbs_dev, type, id); + taskqueue_enqueue(taskqueue_thread, &xbs->xbs_probe_children); + } +out: + free(node, M_XENBUS); +} + +/** + * \brief Interrupt configuration hook callback associated with xbs_attch_ch. + * + * Since interrupts are always functional at the time of XenBus configuration, + * there is nothing to be done when the callback occurs. This hook is only + * registered to hold up boot processing while XenBus devices come online. + * + * \param arg Unused configuration hook callback argument. + */ +static void +xenbusb_nop_confighook_cb(void *arg __unused) +{ +} + +/** + * \brief Decrement the number of XenBus child devices in the + * connecting state by one and release the xbs_attch_ch + * interrupt configuration hook if the connecting count + * drops to zero. + * + * \param xbs XenBus Bus device softc of the owner of the bus to enumerate. + */ +static void +xenbusb_release_confighook(struct xenbusb_softc *xbs) +{ + mtx_lock(&xbs->xbs_lock); + KASSERT(xbs->xbs_connecting_children > 0, + ("Connecting device count error\n")); + xbs->xbs_connecting_children--; + if (xbs->xbs_connecting_children == 0 + && (xbs->xbs_flags & XBS_ATTACH_CH_ACTIVE) != 0) { + xbs->xbs_flags &= ~XBS_ATTACH_CH_ACTIVE; + mtx_unlock(&xbs->xbs_lock); + config_intrhook_disestablish(&xbs->xbs_attach_ch); + } else { + mtx_unlock(&xbs->xbs_lock); + } +} + +/*--------------------------- Public Functions -------------------------------*/ +/*--------- API comments for these methods can be found in xenbusb.h ---------*/ +void +xenbusb_identify(driver_t *driver __unused, device_t parent) +{ + /* + * A single instance of each bus type for which we have a driver + * is always present in a system operating under Xen. + */ + BUS_ADD_CHILD(parent, 0, driver->name, 0); +} + +int +xenbusb_add_device(device_t dev, const char *type, const char *id) +{ + struct xenbusb_softc *xbs; + struct sbuf *devpath_sbuf; + char *devpath; + struct xenbus_device_ivars *ivars; + int error; + + xbs = device_get_softc(dev); + devpath_sbuf = sbuf_new_auto(); + sbuf_printf(devpath_sbuf, "%s/%s/%s", xbs->xbs_node, type, id); + sbuf_finish(devpath_sbuf); + devpath = sbuf_data(devpath_sbuf); + + ivars = malloc(sizeof(*ivars), M_XENBUS, M_ZERO|M_WAITOK); + error = ENXIO; + + if (xs_exists(XST_NIL, devpath, "") != 0) { + device_t child; + enum xenbus_state state; + char *statepath; + + child = xenbusb_device_exists(dev, devpath); + if (child != NULL) { + /* + * We are already tracking this node + */ + error = 0; + goto out; + } + + state = xenbus_read_driver_state(devpath); + if (state != XenbusStateInitialising) { + /* + * Device is not new, so ignore it. This can + * happen if a device is going away after + * switching to Closed. + */ + printf("xenbusb_add_device: Device %s ignored. " + "State %d\n", devpath, state); + error = 0; + goto out; + } + + sx_init(&ivars->xd_lock, "xdlock"); + ivars->xd_flags = XDF_CONNECTING; + ivars->xd_node = strdup(devpath, M_XENBUS); + ivars->xd_type = strdup(type, M_XENBUS); + ivars->xd_state = XenbusStateInitialising; + + error = XENBUSB_GET_OTHEREND_NODE(dev, ivars); + if (error) { + printf("xenbus_update_device: %s no otherend id\n", + devpath); + goto out; + } + + statepath = malloc(strlen(ivars->xd_otherend_path) + + strlen("/state") + 1, M_XENBUS, M_WAITOK); + sprintf(statepath, "%s/state", ivars->xd_otherend_path); + + ivars->xd_otherend_watch.node = statepath; + ivars->xd_otherend_watch.callback = xenbusb_otherend_changed; + + mtx_lock(&xbs->xbs_lock); + xbs->xbs_connecting_children++; + mtx_unlock(&xbs->xbs_lock); + + child = device_add_child(dev, NULL, -1); + ivars->xd_dev = child; + device_set_ivars(child, ivars); + } + +out: + sbuf_delete(devpath_sbuf); + if (error != 0) + xenbusb_free_child_ivars(ivars); + + return (error); +} + +int +xenbusb_attach(device_t dev, char *bus_node, u_int id_components) +{ + struct xenbusb_softc *xbs; + + xbs = device_get_softc(dev); + mtx_init(&xbs->xbs_lock, "xenbusb softc lock", NULL, MTX_DEF); + xbs->xbs_node = bus_node; + xbs->xbs_id_components = id_components; + xbs->xbs_dev = dev; + + /* + * Since XenBus busses are attached to the XenStore, and + * the XenStore does not probe children until after interrupt + * services are available, this config hook is used solely + * to ensure that the remainder of the boot process (e.g. + * mount root) is deferred until child devices are adequately + * probed. We unblock the boot process as soon as the + * connecting child count in our softc goes to 0. + */ + xbs->xbs_attach_ch.ich_func = xenbusb_nop_confighook_cb; + xbs->xbs_attach_ch.ich_arg = dev; + config_intrhook_establish(&xbs->xbs_attach_ch); + xbs->xbs_flags |= XBS_ATTACH_CH_ACTIVE; + xbs->xbs_connecting_children = 1; + + /* + * The subtree for this bus type may not yet exist + * causing initial enumeration to fail. We still + * want to return success from our attach though + * so that we are ready to handle devices for this + * bus when they are dynamically attached to us + * by a Xen management action. + */ + (void)xenbusb_enumerate_bus(xbs); + xenbusb_probe_children(dev); + + xbs->xbs_device_watch.node = bus_node; + xbs->xbs_device_watch.callback = xenbusb_devices_changed; + + TASK_INIT(&xbs->xbs_probe_children, 0, xenbusb_probe_children_cb, dev); + + xs_register_watch(&xbs->xbs_device_watch); + + xenbusb_release_confighook(xbs); + + return (0); +} + +int +xenbusb_resume(device_t dev) +{ + device_t *kids; + struct xenbus_device_ivars *ivars; + int i, count, error; + char *statepath; + + /* + * We must re-examine each device and find the new path for + * its backend. + */ + if (device_get_children(dev, &kids, &count) == 0) { + for (i = 0; i < count; i++) { + if (device_get_state(kids[i]) == DS_NOTPRESENT) + continue; + + ivars = device_get_ivars(kids[i]); + + xs_unregister_watch(&ivars->xd_otherend_watch); + ivars->xd_state = XenbusStateInitialising; + + /* + * Find the new backend details and + * re-register our watch. + */ + error = XENBUSB_GET_OTHEREND_NODE(dev, ivars); + if (error) + return (error); + + DEVICE_RESUME(kids[i]); + + statepath = malloc(strlen(ivars->xd_otherend_path) + + strlen("/state") + 1, M_XENBUS, M_WAITOK); + sprintf(statepath, "%s/state", ivars->xd_otherend_path); + + free(ivars->xd_otherend_watch.node, M_XENBUS); + ivars->xd_otherend_watch.node = statepath; + xs_register_watch(&ivars->xd_otherend_watch); + +#if 0 + /* + * Can't do this yet since we are running in + * the xenwatch thread and if we sleep here, + * we will stop delivering watch notifications + * and the device will never come back online. + */ + sx_xlock(&ivars->xd_lock); + while (ivars->xd_state != XenbusStateClosed + && ivars->xd_state != XenbusStateConnected) + sx_sleep(&ivars->xd_state, &ivars->xd_lock, + 0, "xdresume", 0); + sx_xunlock(&ivars->xd_lock); +#endif + } + free(kids, M_TEMP); + } + + return (0); +} + +int +xenbusb_print_child(device_t dev, device_t child) +{ + struct xenbus_device_ivars *ivars = device_get_ivars(child); + int retval = 0; + + retval += bus_print_child_header(dev, child); + retval += printf(" at %s", ivars->xd_node); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +int +xenbusb_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) +{ + struct xenbus_device_ivars *ivars = device_get_ivars(child); + + switch (index) { + case XENBUS_IVAR_NODE: + *result = (uintptr_t) ivars->xd_node; + return (0); + + case XENBUS_IVAR_TYPE: + *result = (uintptr_t) ivars->xd_type; + return (0); + + case XENBUS_IVAR_STATE: + *result = (uintptr_t) ivars->xd_state; + return (0); + + case XENBUS_IVAR_OTHEREND_ID: + *result = (uintptr_t) ivars->xd_otherend_id; + return (0); + + case XENBUS_IVAR_OTHEREND_PATH: + *result = (uintptr_t) ivars->xd_otherend_path; + return (0); + } + + return (ENOENT); +} + +int +xenbusb_write_ivar(device_t dev, device_t child, int index, uintptr_t value) +{ + struct xenbus_device_ivars *ivars = device_get_ivars(child); + enum xenbus_state newstate; + int currstate; + + switch (index) { + case XENBUS_IVAR_STATE: + { + int error; + + newstate = (enum xenbus_state) value; + sx_xlock(&ivars->xd_lock); + if (ivars->xd_state == newstate) { + error = 0; + goto out; + } + + error = xs_scanf(XST_NIL, ivars->xd_node, "state", + NULL, "%d", &currstate); + if (error) + goto out; + + do { + error = xs_printf(XST_NIL, ivars->xd_node, "state", + "%d", newstate); + } while (error == EAGAIN); + if (error) { + /* + * Avoid looping through xenbus_dev_fatal() + * which calls xenbus_write_ivar to set the + * state to closing. + */ + if (newstate != XenbusStateClosing) + xenbus_dev_fatal(dev, error, + "writing new state"); + goto out; + } + ivars->xd_state = newstate; + + if ((ivars->xd_flags & XDF_CONNECTING) != 0 + && (newstate == XenbusStateClosed + || newstate == XenbusStateConnected)) { + struct xenbusb_softc *xbs; + + ivars->xd_flags &= ~XDF_CONNECTING; + xbs = device_get_softc(dev); + xenbusb_release_confighook(xbs); + } + + wakeup(&ivars->xd_state); + out: + sx_xunlock(&ivars->xd_lock); + return (error); + } + + case XENBUS_IVAR_NODE: + case XENBUS_IVAR_TYPE: + case XENBUS_IVAR_OTHEREND_ID: + case XENBUS_IVAR_OTHEREND_PATH: + /* + * These variables are read-only. + */ + return (EINVAL); + } + + return (ENOENT); +} diff --git a/sys/xen/xenbus/xenbusb.h b/sys/xen/xenbus/xenbusb.h new file mode 100644 index 0000000..75abb98 --- /dev/null +++ b/sys/xen/xenbus/xenbusb.h @@ -0,0 +1,272 @@ +/*- + * Core definitions and data structures shareable across OS platforms. + * + * Copyright (c) 2010 Spectra Logic Corporation + * Copyright (C) 2008 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ +#ifndef _XEN_XENBUS_XENBUSB_H +#define _XEN_XENBUS_XENBUSB_H + +/** + * \file xenbusb.h + * + * Datastructures and function declarations for use in implementing + * bus attachements (e.g. frontend and backend device busses) for XenBus. + */ +#include "xenbusb_if.h" + +/** + * Enumeration of state flag values for the xbs_flags field of + * the xenbusb_softc structure. + */ +typedef enum { + /** */ + XBS_ATTACH_CH_ACTIVE = 0x01 +} xenbusb_softc_flag; + +/** + * \brief Container for all state needed to manage a Xenbus Bus + * attachment. + */ +struct xenbusb_softc { + /** + * XenStore watch used to monitor the subtree of the + * XenStore where devices for this bus attachment arrive + * and depart. + * + * \note This field must be the first in the softc structure + * so that a simple cast can be used to retrieve the + * softc from within a XenStore watch event callback. + */ + struct xs_watch xbs_device_watch; + + /** Mutex used to protect fields of the xenbusb_softc. */ + struct mtx xbs_lock; + + /** State flags. */ + xenbusb_softc_flag xbs_flags; + + /** + * A dedicated task for processing child arrival and + * departure events. + */ + struct task xbs_probe_children; + + /** + * Config Hook used to block boot processing until + * XenBus devices complete their connection processing + * with other VMs. + */ + struct intr_config_hook xbs_attach_ch; + + /** + * The number of children for this bus that are still + * in the connecting (to other VMs) state. This variable + * is used to determine when to release xbs_attach_ch. + */ + u_int xbs_connecting_children; + + /** The NewBus device_t for this bus attachment. */ + device_t xbs_dev; + + /** + * The VM relative path to the XenStore subtree this + * bus attachment manages. + */ + const char *xbs_node; + + /** + * The number of path components (strings separated by the '/' + * character) that make up the device ID on this bus. + */ + u_int xbs_id_components; +}; + +/** + * Enumeration of state flag values for the xbs_flags field of + * the xenbusb_softc structure. + */ +typedef enum { + + /** + * This device is contributing to the xbs_connecting_children + * count of its parent bus. + */ + XDF_CONNECTING = 0x01 +} xenbus_dev_flag; + +/** Instance variables for devices on a XenBus bus. */ +struct xenbus_device_ivars { + /** + * XenStore watch used to monitor the subtree of the + * XenStore where information about the otherend of + * the split Xen device this device instance represents. + * + * \note This field must be the first in the instance + * variable structure so that a simple cast can be + * used to retrieve ivar data from within a XenStore + * watch event callback. + */ + struct xs_watch xd_otherend_watch; + + /** Sleepable lock used to protect instance data. */ + struct sx xd_lock; + + /** State flags. */ + xenbus_dev_flag xd_flags; + + /** The NewBus device_t for this XenBus device instance. */ + device_t xd_dev; + + /** + * The VM relative path to the XenStore subtree representing + * this VMs half of this device. + */ + char *xd_node; + + /** XenBus device type ("vbd", "vif", etc.). */ + char *xd_type; + + /** + * Cached version of <xd_node>/state node in the XenStore. + */ + enum xenbus_state xd_state; + + /** The VM identifier of the other end of this split device. */ + int xd_otherend_id; + + /** + * The path to the subtree of the XenStore where information + * about the otherend of this split device instance. + */ + char *xd_otherend_path; +}; + +/** + * \brief Identify instances of this device type in the system. + * + * \param driver The driver performing this identify action. + * \param parent The NewBus parent device for any devices this method adds. + */ +void xenbusb_identify(driver_t *driver __unused, device_t parent); + +/** + * \brief Perform common XenBus bus attach processing. + * + * \param dev The NewBus device representing this XenBus bus. + * \param bus_node The XenStore path to the XenStore subtree for + * this XenBus bus. + * \param id_components The number of '/' separated path components that + * make up a unique device ID on this XenBus bus. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * Intiailizes the softc for this bus, installs an interrupt driven + * configuration hook to block boot processing until XenBus devices fully + * configure, performs an initial probe/attach of the bus, and registers + * a XenStore watch so we are notified when the bus topology changes. + */ +int xenbusb_attach(device_t dev, char *bus_node, u_int id_components); + +/** + * \brief Perform common XenBus bus resume handling. + * + * \param dev The NewBus device representing this XenBus bus. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +int xenbusb_resume(device_t dev); + +/** + * \brief Pretty-prints information about a child of a XenBus bus. + * + * \param dev The NewBus device representing this XenBus bus. + * \param child The NewBus device representing a child of dev%'s XenBus bus. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +int xenbusb_print_child(device_t dev, device_t child); + +/** + * \brief Common XenBus child instance variable read access method. + * + * \param dev The NewBus device representing this XenBus bus. + * \param child The NewBus device representing a child of dev%'s XenBus bus. + * \param index The index of the instance variable to access. + * \param result The value of the instance variable accessed. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +int xenbusb_read_ivar(device_t dev, device_t child, int index, + uintptr_t *result); + +/** + * \brief Common XenBus child instance variable write access method. + * + * \param dev The NewBus device representing this XenBus bus. + * \param child The NewBus device representing a child of dev%'s XenBus bus. + * \param index The index of the instance variable to access. + * \param value The new value to set in the instance variable accessed. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +int xenbusb_write_ivar(device_t dev, device_t child, int index, + uintptr_t value); + +/** + * \brief Attempt to add a XenBus device instance to this XenBus bus. + * + * \param dev The NewBus device representing this XenBus bus. + * \param type The device type being added (e.g. "vbd", "vif"). + * \param id The device ID for this device. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. Failure indicates that either the + * path to this device no longer exists or insufficient + * information exists in the XenStore to create a new + * device. + * + * If successful, this routine will add a device_t with instance + * variable storage to the NewBus device topology. Probe/Attach + * processing is not performed by this routine, but must be scheduled + * via the xbs_probe_children task. This separation of responsibilities + * is required to avoid hanging up the XenStore event delivery thread + * with our probe/attach work in the event a device is added via + * a callback from the XenStore. + */ +int xenbusb_add_device(device_t dev, const char *type, const char *id); + +#endif /* _XEN_XENBUS_XENBUSB_H */ diff --git a/sys/xen/xenbus/xenbusb_back.c b/sys/xen/xenbus/xenbusb_back.c new file mode 100644 index 0000000..32bbc04 --- /dev/null +++ b/sys/xen/xenbus/xenbusb_back.c @@ -0,0 +1,295 @@ +/****************************************************************************** + * Talks to Xen Store to figure out what devices we have. + * + * Copyright (C) 2009, 2010 Spectra Logic Corporation + * Copyright (C) 2008 Doug Rabson + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * Copyright (C) 2005 Mike Wray, Hewlett-Packard + * Copyright (C) 2005 XenSource Ltd + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * \file xenbusb_back.c + * + * XenBus management of the NewBus bus containing the backend instances of + * Xen split devices. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/sbuf.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/systm.h> +#include <sys/sx.h> +#include <sys/taskqueue.h> + +#include <machine/xen/xen-os.h> +#include <machine/stdarg.h> + +#include <xen/gnttab.h> +#include <xen/xenbus/xenbusvar.h> +#include <xen/xenbus/xenbusb.h> + + +/*------------------ Private Device Attachment Functions --------------------*/ +/** + * \brief Probe for the existance of the XenBus back bus. + * + * \param dev NewBus device_t for this XenBus back bus instance. + * + * \return Always returns 0 indicating success. + */ +static int +xenbusb_back_probe(device_t dev) +{ + device_set_desc(dev, "Xen Backend Devices"); + + return (0); +} + +/** + * \brief Attach the XenBus back bus. + * + * \param dev NewBus device_t for this XenBus back bus instance. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +static int +xenbusb_back_attach(device_t dev) +{ + struct xenbusb_softc *xbs; + int error; + + xbs = device_get_softc(dev); + error = xenbusb_attach(dev, "backend", /*id_components*/2); + + /* + * Backend devices operate to serve other domains, + * so there is no need to hold up boot processing + * while connections to foreign domains are made. + */ + mtx_lock(&xbs->xbs_lock); + if ((xbs->xbs_flags & XBS_ATTACH_CH_ACTIVE) != 0) { + xbs->xbs_flags &= ~XBS_ATTACH_CH_ACTIVE; + mtx_unlock(&xbs->xbs_lock); + config_intrhook_disestablish(&xbs->xbs_attach_ch); + } else { + mtx_unlock(&xbs->xbs_lock); + } + + return (error); +} + +/** + * \brief Enumerate all devices of the given type on this bus. + * + * \param dev NewBus device_t for this XenBus backend bus instance. + * \param type String indicating the device sub-tree (e.g. "vfb", "vif") + * to enumerate. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * Devices that are found are entered into the NewBus hierarchy via + * xenbusb_add_device(). xenbusb_add_device() ignores duplicate detects + * and ignores duplicate devices, so it can be called unconditionally + * for any device found in the XenStore. + * + * The backend XenStore hierarchy has the following format: + * + * backend/<device type>/<frontend vm id>/<device id> + * + */ +static int +xenbusb_back_enumerate_type(device_t dev, const char *type) +{ + struct xenbusb_softc *xbs; + const char **vms; + u_int vm_idx; + u_int vm_count; + int error; + + xbs = device_get_softc(dev); + error = xs_directory(XST_NIL, xbs->xbs_node, type, &vm_count, &vms); + if (error) + return (error); + for (vm_idx = 0; vm_idx < vm_count; vm_idx++) { + struct sbuf *vm_path; + const char *vm; + const char **devs; + u_int dev_idx; + u_int dev_count; + + vm = vms[vm_idx]; + + vm_path = xs_join(type, vm); + error = xs_directory(XST_NIL, xbs->xbs_node, sbuf_data(vm_path), + &dev_count, &devs); + sbuf_delete(vm_path); + if (error) + break; + + for (dev_idx = 0; dev_idx < dev_count; dev_idx++) { + const char *dev_num; + struct sbuf *id; + + dev_num = devs[dev_idx]; + id = xs_join(vm, dev_num); + xenbusb_add_device(dev, type, sbuf_data(id)); + sbuf_delete(id); + } + free(devs, M_XENSTORE); + } + + free(vms, M_XENSTORE); + + return (0); +} + +/** + * \brief Determine and store the XenStore path for the other end of + * a split device whose local end is represented by ivars. + * + * \param dev NewBus device_t for this XenBus backend bus instance. + * \param ivars Instance variables from the XenBus child device for + * which to perform this function. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * If successful, the xd_otherend_path field of the child's instance + * variables will be updated. + * + */ +static int +xenbusb_back_get_otherend_node(device_t dev, struct xenbus_device_ivars *ivars) +{ + char *otherend_path; + int error; + + if (ivars->xd_otherend_path != NULL) { + free(ivars->xd_otherend_path, M_XENBUS); + ivars->xd_otherend_path = NULL; + } + + error = xs_gather(XST_NIL, ivars->xd_node, + "frontend-id", "%i", &ivars->xd_otherend_id, + "frontend", NULL, &otherend_path, + NULL); + + if (error == 0) { + ivars->xd_otherend_path = strdup(otherend_path, M_XENBUS); + free(otherend_path, M_XENSTORE); + } + return (error); +} + +/** + * \brief Backend XenBus child instance variable write access method. + * + * \param dev The NewBus device representing this XenBus bus. + * \param child The NewBus device representing a child of dev%'s XenBus bus. + * \param index The index of the instance variable to access. + * \param value The new value to set in the instance variable accessed. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * Xenbus_back overrides this method so that it can trap state transitions + * of local backend devices and clean up their XenStore entries as necessary + * during device instance teardown. + */ +static int +xenbusb_back_write_ivar(device_t dev, device_t child, int index, + uintptr_t value) +{ + int error; + + error = xenbusb_write_ivar(dev, child, index, value); + + if (index == XENBUS_IVAR_STATE + && (enum xenbus_state)value == XenbusStateClosed + && xenbus_dev_is_online(child) == 0) { + + /* + * Cleanup the hotplug entry in the XenStore if + * present. The control domain expects any userland + * component associated with this device to destroy + * this node in order to signify it is safe to + * teardown the device. However, not all backends + * rely on userland components, and those that + * do should either use a communication channel + * other than the XenStore, or ensure the hotplug + * data is already cleaned up. + * + * This removal ensures that no matter what path + * is taken to mark a back-end closed, the control + * domain will understand that it is closed. + */ + xs_rm(XST_NIL, xenbus_get_node(child), "hotplug-status"); + } + + return (error); +} + +/*-------------------- Private Device Attachment Data -----------------------*/ +static device_method_t xenbusb_back_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, xenbusb_identify), + DEVMETHOD(device_probe, xenbusb_back_probe), + DEVMETHOD(device_attach, xenbusb_back_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus Interface */ + DEVMETHOD(bus_print_child, xenbusb_print_child), + DEVMETHOD(bus_read_ivar, xenbusb_read_ivar), + DEVMETHOD(bus_write_ivar, xenbusb_back_write_ivar), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + + /* XenBus Bus Interface */ + DEVMETHOD(xenbusb_enumerate_type, xenbusb_back_enumerate_type), + DEVMETHOD(xenbusb_get_otherend_node, xenbusb_back_get_otherend_node), + { 0, 0 } +}; + +DEFINE_CLASS_0(xenbusb_back, xenbusb_back_driver, xenbusb_back_methods, + sizeof(struct xenbusb_softc)); +devclass_t xenbusb_back_devclass; + +DRIVER_MODULE(xenbusb_back, xenstore, xenbusb_back_driver, + xenbusb_back_devclass, 0, 0); diff --git a/sys/xen/xenbus/xenbusb_front.c b/sys/xen/xenbus/xenbusb_front.c new file mode 100644 index 0000000..0bc06a4 --- /dev/null +++ b/sys/xen/xenbus/xenbusb_front.c @@ -0,0 +1,195 @@ +/****************************************************************************** + * Talks to Xen Store to figure out what devices we have. + * + * Copyright (C) 2009, 2010 Spectra Logic Corporation + * Copyright (C) 2008 Doug Rabson + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * Copyright (C) 2005 Mike Wray, Hewlett-Packard + * Copyright (C) 2005 XenSource Ltd + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * \file xenbusb_front.c + * + * XenBus management of the NewBus bus containing the frontend instances of + * Xen split devices. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/sbuf.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/systm.h> +#include <sys/sx.h> +#include <sys/taskqueue.h> + +#include <machine/xen/xen-os.h> +#include <machine/stdarg.h> + +#include <xen/gnttab.h> +#include <xen/xenbus/xenbusvar.h> +#include <xen/xenbus/xenbusb.h> + + +/*------------------ Private Device Attachment Functions --------------------*/ +/** + * \brief Probe for the existance of the XenBus front bus. + * + * \param dev NewBus device_t for this XenBus front bus instance. + * + * \return Always returns 0 indicating success. + */ +static int +xenbusb_front_probe(device_t dev) +{ + device_set_desc(dev, "Xen Frontend Devices"); + + return (0); +} + +/** + * \brief Attach the XenBus front bus. + * + * \param dev NewBus device_t for this XenBus front bus instance. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +static int +xenbusb_front_attach(device_t dev) +{ + return (xenbusb_attach(dev, "device", /*id_components*/1)); +} + +/** + * \brief Enumerate all devices of the given type on this bus. + * + * \param dev NewBus device_t for this XenBus front bus instance. + * \param type String indicating the device sub-tree (e.g. "vfb", "vif") + * to enumerate. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * Devices that are found are entered into the NewBus hierarchy via + * xenbusb_add_device(). xenbusb_add_device() ignores duplicate detects + * and ignores duplicate devices, so it can be called unconditionally + * for any device found in the XenStore. + */ +static int +xenbusb_front_enumerate_type(device_t dev, const char *type) +{ + struct xenbusb_softc *xbs; + const char **dir; + unsigned int i, count; + int error; + + xbs = device_get_softc(dev); + error = xs_directory(XST_NIL, xbs->xbs_node, type, &count, &dir); + if (error) + return (error); + for (i = 0; i < count; i++) + xenbusb_add_device(dev, type, dir[i]); + + free(dir, M_XENSTORE); + + return (0); +} + +/** + * \brief Determine and store the XenStore path for the other end of + * a split device whose local end is represented by ivars. + * + * If successful, the xd_otherend_path field of the child's instance + * variables will be updated. + * + * \param dev NewBus device_t for this XenBus front bus instance. + * \param ivars Instance variables from the XenBus child device for + * which to perform this function. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +static int +xenbusb_front_get_otherend_node(device_t dev, struct xenbus_device_ivars *ivars) +{ + char *otherend_path; + int error; + + if (ivars->xd_otherend_path != NULL) { + free(ivars->xd_otherend_path, M_XENBUS); + ivars->xd_otherend_path = NULL; + } + + error = xs_gather(XST_NIL, ivars->xd_node, + "backend-id", "%i", &ivars->xd_otherend_id, + "backend", NULL, &otherend_path, + NULL); + + if (error == 0) { + ivars->xd_otherend_path = strdup(otherend_path, M_XENBUS); + free(otherend_path, M_XENSTORE); + } + return (error); +} + +/*-------------------- Private Device Attachment Data -----------------------*/ +static device_method_t xenbusb_front_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, xenbusb_identify), + DEVMETHOD(device_probe, xenbusb_front_probe), + DEVMETHOD(device_attach, xenbusb_front_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus Interface */ + DEVMETHOD(bus_print_child, xenbusb_print_child), + DEVMETHOD(bus_read_ivar, xenbusb_read_ivar), + DEVMETHOD(bus_write_ivar, xenbusb_write_ivar), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + + /* XenBus Bus Interface */ + DEVMETHOD(xenbusb_enumerate_type, xenbusb_front_enumerate_type), + DEVMETHOD(xenbusb_get_otherend_node, xenbusb_front_get_otherend_node), + { 0, 0 } +}; + +DEFINE_CLASS_0(xenbusb_front, xenbusb_front_driver, xenbusb_front_methods, + sizeof(struct xenbusb_softc)); +devclass_t xenbusb_front_devclass; + +DRIVER_MODULE(xenbusb_front, xenstore, xenbusb_front_driver, + xenbusb_front_devclass, 0, 0); diff --git a/sys/xen/xenbus/xenbusb_if.m b/sys/xen/xenbus/xenbusb_if.m new file mode 100644 index 0000000..a32e3f6 --- /dev/null +++ b/sys/xen/xenbus/xenbusb_if.m @@ -0,0 +1,78 @@ +#- +# Copyright (c) 2010 Spectra Logic Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions, and the following disclaimer, +# without modification. +# 2. Redistributions in binary form must reproduce at minimum a disclaimer +# substantially similar to the "NO WARRANTY" disclaimer below +# ("Disclaimer") and any redistribution must be conditioned upon +# including a substantially similar Disclaimer requirement for further +# binary redistribution. +# +# NO WARRANTY +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGES. +# +# $FreeBSD$ +# + +#include <sys/bus.h> + +HEADER { +struct xenbus_device_ivars; +} + +INTERFACE xenbusb; + +/** + * \brief Enumerate all devices of the given type on this bus. + * + * \param _dev NewBus device_t for this XenBus (front/back) bus instance. + * \param _type String indicating the device sub-tree (e.g. "vfb", "vif") + * to enumerate. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * Devices that are found should be entered into the NewBus hierarchy via + * xenbusb_add_device(). xenbusb_add_device() ignores duplicate detects + * and ignores duplicate devices, so it can be called unconditionally + * for any device found in the XenStore. + */ +METHOD int enumerate_type { + device_t _dev; + const char *_type; +}; + +/** + * \brief Determine and store the XenStore path for the other end of + * a split device whose local end is represented by ivars. + * + * If successful, the xd_otherend_path field of the child's instance + * variables must be updated. + * + * \param _dev NewBus device_t for this XenBus (front/back) bus instance. + * \param _ivars Instance variables from the XenBus child device for + * which to perform this function. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + */ +METHOD int get_otherend_node { + device_t _dev; + struct xenbus_device_ivars *_ivars; +} diff --git a/sys/xen/xenbus/xenbusvar.h b/sys/xen/xenbus/xenbusvar.h index 6511664..55d7f29 100644 --- a/sys/xen/xenbus/xenbusvar.h +++ b/sys/xen/xenbus/xenbusvar.h @@ -1,8 +1,4 @@ /****************************************************************************** - * xenbus.h - * - * Talks to Xen Store to figure out what devices we have. - * * Copyright (C) 2005 Rusty Russell, IBM Corporation * Copyright (C) 2005 XenSource Ltd. * @@ -30,46 +26,64 @@ * $FreeBSD$ */ +/** + * \file xenbusvar.h + * + * \brief Datastructures and function declarations for usedby device + * drivers operating on the XenBus. + */ + #ifndef _XEN_XENBUS_XENBUSVAR_H #define _XEN_XENBUS_XENBUSVAR_H #include <sys/queue.h> #include <sys/bus.h> #include <sys/eventhandler.h> +#include <sys/malloc.h> +#include <sys/sbuf.h> + +#include <machine/stdarg.h> #include <machine/xen/xen-os.h> + +#include <xen/interface/grant_table.h> #include <xen/interface/io/xenbus.h> #include <xen/interface/io/xs_wire.h> +#include <xen/xenstore/xenstorevar.h> + #include "xenbus_if.h" +/* XenBus allocations including XenStore data returned to clients. */ +MALLOC_DECLARE(M_XENBUS); + enum { - /* + /** * Path of this device node. */ XENBUS_IVAR_NODE, - /* + /** * The device type (e.g. vif, vbd). */ XENBUS_IVAR_TYPE, - /* + /** * The state of this device (not the otherend's state). */ XENBUS_IVAR_STATE, - /* + /** * Domain ID of the other end device. */ XENBUS_IVAR_OTHEREND_ID, - /* + /** * Path of the other end device. */ XENBUS_IVAR_OTHEREND_PATH }; -/* +/** * Simplified accessors for xenbus devices */ #define XENBUS_ACCESSOR(var, ivar, type) \ @@ -81,179 +95,184 @@ XENBUS_ACCESSOR(state, STATE, enum xenbus_state) XENBUS_ACCESSOR(otherend_id, OTHEREND_ID, int) XENBUS_ACCESSOR(otherend_path, OTHEREND_PATH, const char *) -/* Register callback to watch this node. */ -struct xenbus_watch -{ - LIST_ENTRY(xenbus_watch) list; - - /* Path being watched. */ - char *node; - - /* Callback (executed in a process context with no locks held). */ - void (*callback)(struct xenbus_watch *, - const char **vec, unsigned int len); -}; - -typedef int (*xenstore_event_handler_t)(void *); - -struct xenbus_transaction -{ - uint32_t id; -}; - -#define XBT_NIL ((struct xenbus_transaction) { 0 }) - -int xenbus_directory(struct xenbus_transaction t, const char *dir, - const char *node, unsigned int *num, char ***result); -int xenbus_read(struct xenbus_transaction t, const char *dir, - const char *node, unsigned int *len, void **result); -int xenbus_write(struct xenbus_transaction t, const char *dir, - const char *node, const char *string); -int xenbus_mkdir(struct xenbus_transaction t, const char *dir, - const char *node); -int xenbus_exists(struct xenbus_transaction t, const char *dir, - const char *node); -int xenbus_rm(struct xenbus_transaction t, const char *dir, const char *node); -int xenbus_transaction_start(struct xenbus_transaction *t); -int xenbus_transaction_end(struct xenbus_transaction t, int abort); - -/* - * Single read and scanf: returns errno or zero. If scancountp is - * non-null, then number of items scanned is returned in *scanncountp. - */ -int xenbus_scanf(struct xenbus_transaction t, - const char *dir, const char *node, int *scancountp, const char *fmt, ...) - __attribute__((format(scanf, 5, 6))); - -/* Single printf and write: returns errno or 0. */ -int xenbus_printf(struct xenbus_transaction t, - const char *dir, const char *node, const char *fmt, ...) - __attribute__((format(printf, 4, 5))); - -/* - * Generic read function: NULL-terminated triples of name, - * sprintf-style type string, and pointer. Returns 0 or errno. +/** + * Return the state of a XenBus device. + * + * \param path The root XenStore path for the device. + * + * \return The current state of the device or XenbusStateClosed if no + * state can be read. */ -int xenbus_gather(struct xenbus_transaction t, const char *dir, ...); - -/* notifer routines for when the xenstore comes up */ -int register_xenstore_notifier(xenstore_event_handler_t func, void *arg, int priority); -#if 0 -void unregister_xenstore_notifier(); -#endif -int register_xenbus_watch(struct xenbus_watch *watch); -void unregister_xenbus_watch(struct xenbus_watch *watch); -void xs_suspend(void); -void xs_resume(void); - -/* Used by xenbus_dev to borrow kernel's store connection. */ -int xenbus_dev_request_and_reply(struct xsd_sockmsg *msg, void **result); - -#if 0 - -#define XENBUS_IS_ERR_READ(str) ({ \ - if (!IS_ERR(str) && strlen(str) == 0) { \ - free(str, M_DEVBUF); \ - str = ERR_PTR(-ERANGE); \ - } \ - IS_ERR(str); \ -}) - -#endif - -#define XENBUS_EXIST_ERR(err) ((err) == ENOENT || (err) == ERANGE) - +XenbusState xenbus_read_driver_state(const char *path); /** - * Register a watch on the given path, using the given xenbus_watch structure - * for storage, and the given callback function as the callback. Return 0 on - * success, or errno on error. On success, the given path will be saved as - * watch->node, and remains the caller's to free. On error, watch->node will - * be NULL, the device will switch to XenbusStateClosing, and the error will - * be saved in the store. + * Initialize and register a watch on the given path (client suplied storage). + * + * \param dev The XenBus device requesting the watch service. + * \param path The XenStore path of the object to be watched. The + * storage for this string must be stable for the lifetime + * of the watch. + * \param watch The watch object to use for this request. This object + * must be stable for the lifetime of the watch. + * \param callback The function to call when XenStore objects at or below + * path are modified. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * \note On error, the device 'dev' will be switched to the XenbusStateClosing + * state and the returned error is saved in the per-device error node + * for dev in the XenStore. */ int xenbus_watch_path(device_t dev, char *path, - struct xenbus_watch *watch, - void (*callback)(struct xenbus_watch *, - const char **, unsigned int)); - + struct xs_watch *watch, + xs_watch_cb_t *callback); /** - * Register a watch on the given path/path2, using the given xenbus_watch - * structure for storage, and the given callback function as the callback. - * Return 0 on success, or errno on error. On success, the watched path - * (path/path2) will be saved as watch->node, and becomes the caller's to - * kfree(). On error, watch->node will be NULL, so the caller has nothing to - * free, the device will switch to XenbusStateClosing, and the error will be - * saved in the store. + * Initialize and register a watch at path/path2 in the XenStore. + * + * \param dev The XenBus device requesting the watch service. + * \param path The base XenStore path of the object to be watched. + * \param path2 The tail XenStore path of the object to be watched. + * \param watch The watch object to use for this request. This object + * must be stable for the lifetime of the watch. + * \param callback The function to call when XenStore objects at or below + * path are modified. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * \note On error, \a dev will be switched to the XenbusStateClosing + * state and the returned error is saved in the per-device error node + * for \a dev in the XenStore. + * + * Similar to xenbus_watch_path, however the storage for the path to the + * watched object is allocated from the heap and filled with "path '/' path2". + * Should a call to this function succeed, it is the callers responsibility + * to free watch->node using the M_XENBUS malloc type. */ int xenbus_watch_path2(device_t dev, const char *path, - const char *path2, struct xenbus_watch *watch, - void (*callback)(struct xenbus_watch *, - const char **, unsigned int)); - + const char *path2, struct xs_watch *watch, + xs_watch_cb_t *callback); /** - * Advertise in the store a change of the given driver to the given new_state. - * which case this is performed inside its own transaction. Return 0 on - * success, or errno on error. On error, the device will switch to - * XenbusStateClosing, and the error will be saved in the store. + * Grant access to the given ring_mfn to the peer of the given device. + * + * \param dev The device granting access to the ring page. + * \param ring_mfn The guest machine page number of the page to grant + * peer access rights. + * \param refp[out] The grant reference for the page. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * A successful call to xenbus_grant_ring should be paired with a call + * to gnttab_end_foreign_access() when foregn access to this page is no + * longer requried. + * + * \note On error, \a dev will be switched to the XenbusStateClosing + * state and the returned error is saved in the per-device error node + * for \a dev in the XenStore. */ -int xenbus_switch_state(device_t dev, - XenbusState new_state); - +int xenbus_grant_ring(device_t dev, unsigned long ring_mfn, grant_ref_t *refp); /** - * Grant access to the given ring_mfn to the peer of the given device. - * Return 0 on success, or errno on error. On error, the device will - * switch to XenbusStateClosing, and the error will be saved in the - * store. The grant ring reference is returned in *refp. + * Allocate an event channel for the given XenBus device. + * + * \param dev The device for which to allocate the event channel. + * \param port[out] The port identifier for the allocated event channel. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * A successfully allocated event channel should be free'd using + * xenbus_free_evtchn(). + * + * \note On error, \a dev will be switched to the XenbusStateClosing + * state and the returned error is saved in the per-device error node + * for \a dev in the XenStore. */ -int xenbus_grant_ring(device_t dev, unsigned long ring_mfn, int *refp); - +int xenbus_alloc_evtchn(device_t dev, evtchn_port_t *port); /** - * Allocate an event channel for the given xenbus_device, assigning the newly - * created local port to *port. Return 0 on success, or errno on error. On - * error, the device will switch to XenbusStateClosing, and the error will be - * saved in the store. + * Free an existing event channel. + * + * \param dev The device which allocated this event channel. + * \param port The port identifier for the event channel to free. + * + * \return On success, 0. Otherwise an errno value indicating the + * type of failure. + * + * \note On error, \a dev will be switched to the XenbusStateClosing + * state and the returned error is saved in the per-device error node + * for \a dev in the XenStore. */ -int xenbus_alloc_evtchn(device_t dev, int *port); - +int xenbus_free_evtchn(device_t dev, evtchn_port_t port); /** - * Free an existing event channel. Returns 0 on success or errno on error. + * Record the given errno, along with the given, printf-style, formatted + * message in dev's device specific error node in the XenStore. + * + * \param dev The device which encountered the error. + * \param err The errno value corresponding to the error. + * \param fmt Printf format string followed by a variable number of + * printf arguments. */ -int xenbus_free_evtchn(device_t dev, int port); - +void xenbus_dev_error(device_t dev, int err, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); /** - * Return the state of the driver rooted at the given store path, or - * XenbusStateClosed if no state can be read. + * va_list version of xenbus_dev_error(). + * + * \param dev The device which encountered the error. + * \param err The errno value corresponding to the error. + * \param fmt Printf format string. + * \param ap Va_list of printf arguments. */ -XenbusState xenbus_read_driver_state(const char *path); +void xenbus_dev_verror(device_t dev, int err, const char *fmt, va_list ap) + __attribute__((format(printf, 3, 0))); - -/*** - * Report the given negative errno into the store, along with the given - * formatted message. +/** + * Equivalent to xenbus_dev_error(), followed by + * xenbus_set_state(dev, XenbusStateClosing). + * + * \param dev The device which encountered the error. + * \param err The errno value corresponding to the error. + * \param fmt Printf format string followed by a variable number of + * printf arguments. */ -void xenbus_dev_error(device_t dev, int err, const char *fmt, - ...); - +void xenbus_dev_fatal(device_t dev, int err, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); -/*** - * Equivalent to xenbus_dev_error(dev, err, fmt, args), followed by - * xenbus_switch_state(dev, NULL, XenbusStateClosing) to schedule an orderly - * closedown of this driver and its peer. +/** + * va_list version of xenbus_dev_fatal(). + * + * \param dev The device which encountered the error. + * \param err The errno value corresponding to the error. + * \param fmt Printf format string. + * \param ap Va_list of printf arguments. */ -void xenbus_dev_fatal(device_t dev, int err, const char *fmt, - ...); - -int xenbus_dev_init(void); +void xenbus_dev_vfatal(device_t dev, int err, const char *fmt, va_list) + __attribute__((format(printf, 3, 0))); +/** + * Convert a member of the xenbus_state enum into an ASCII string. + * + * /param state The XenBus state to lookup. + * + * /return A string representing state or, for unrecognized states, + * the string "Unknown". + */ const char *xenbus_strstate(enum xenbus_state state); + +/** + * Return the value of a XenBus device's "online" node within the XenStore. + * + * \param dev The XenBus device to query. + * + * \return The value of the "online" node for the device. If the node + * does not exist, 0 (offline) is returned. + */ int xenbus_dev_is_online(device_t dev); -int xenbus_frontend_closed(device_t dev); #endif /* _XEN_XENBUS_XENBUSVAR_H */ |