xen: add the Xenbus sysfs and virtual device hotplug driver
This communicates with the machine control software via a registry residing in a controlling virtual machine. This allows dynamic creation, destruction and modification of virtual device configurations (network devices, block devices and CPUS, to name some examples). [ Greg, would you mind giving this a review? Thanks -J ] Signed-off-by: Ian Pratt <ian.pratt@xensource.com> Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk> Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com> Signed-off-by: Chris Wright <chrisw@sous-sol.org> Cc: Greg KH <greg@kroah.com>
This commit is contained in:
parent
ad9a86121f
commit
4bac07c993
10 changed files with 2961 additions and 0 deletions
|
@ -1 +1,2 @@
|
|||
obj-y += grant-table.o
|
||||
obj-y += xenbus/
|
||||
|
|
7
drivers/xen/xenbus/Makefile
Normal file
7
drivers/xen/xenbus/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
obj-y += xenbus.o
|
||||
|
||||
xenbus-objs =
|
||||
xenbus-objs += xenbus_client.o
|
||||
xenbus-objs += xenbus_comms.o
|
||||
xenbus-objs += xenbus_xs.o
|
||||
xenbus-objs += xenbus_probe.o
|
569
drivers/xen/xenbus/xenbus_client.c
Normal file
569
drivers/xen/xenbus/xenbus_client.c
Normal file
|
@ -0,0 +1,569 @@
|
|||
/******************************************************************************
|
||||
* 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 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.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/xen/hypervisor.h>
|
||||
#include <xen/interface/xen.h>
|
||||
#include <xen/interface/event_channel.h>
|
||||
#include <xen/events.h>
|
||||
#include <xen/grant_table.h>
|
||||
#include <xen/xenbus.h>
|
||||
|
||||
const char *xenbus_strstate(enum xenbus_state state)
|
||||
{
|
||||
static const char *const name[] = {
|
||||
[ XenbusStateUnknown ] = "Unknown",
|
||||
[ XenbusStateInitialising ] = "Initialising",
|
||||
[ XenbusStateInitWait ] = "InitWait",
|
||||
[ XenbusStateInitialised ] = "Initialised",
|
||||
[ XenbusStateConnected ] = "Connected",
|
||||
[ XenbusStateClosing ] = "Closing",
|
||||
[ XenbusStateClosed ] = "Closed",
|
||||
};
|
||||
return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID";
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_strstate);
|
||||
|
||||
/**
|
||||
* xenbus_watch_path - register a watch
|
||||
* @dev: xenbus device
|
||||
* @path: path to watch
|
||||
* @watch: watch to register
|
||||
* @callback: callback to register
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
int xenbus_watch_path(struct xenbus_device *dev, const char *path,
|
||||
struct xenbus_watch *watch,
|
||||
void (*callback)(struct xenbus_watch *,
|
||||
const char **, unsigned int))
|
||||
{
|
||||
int err;
|
||||
|
||||
watch->node = path;
|
||||
watch->callback = callback;
|
||||
|
||||
err = register_xenbus_watch(watch);
|
||||
|
||||
if (err) {
|
||||
watch->node = NULL;
|
||||
watch->callback = NULL;
|
||||
xenbus_dev_fatal(dev, err, "adding watch on %s", path);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_watch_path);
|
||||
|
||||
|
||||
/**
|
||||
* xenbus_watch_pathfmt - register a watch on a sprintf-formatted path
|
||||
* @dev: xenbus device
|
||||
* @watch: watch to register
|
||||
* @callback: callback to register
|
||||
* @pathfmt: format of path to watch
|
||||
*
|
||||
* 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 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.
|
||||
*/
|
||||
int xenbus_watch_pathfmt(struct xenbus_device *dev,
|
||||
struct xenbus_watch *watch,
|
||||
void (*callback)(struct xenbus_watch *,
|
||||
const char **, unsigned int),
|
||||
const char *pathfmt, ...)
|
||||
{
|
||||
int err;
|
||||
va_list ap;
|
||||
char *path;
|
||||
|
||||
va_start(ap, pathfmt);
|
||||
path = kvasprintf(GFP_KERNEL, pathfmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!path) {
|
||||
xenbus_dev_fatal(dev, -ENOMEM, "allocating path for watch");
|
||||
return -ENOMEM;
|
||||
}
|
||||
err = xenbus_watch_path(dev, path, watch, callback);
|
||||
|
||||
if (err)
|
||||
kfree(path);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_watch_pathfmt);
|
||||
|
||||
|
||||
/**
|
||||
* xenbus_switch_state
|
||||
* @dev: xenbus device
|
||||
* @xbt: transaction handle
|
||||
* @state: new state
|
||||
*
|
||||
* Advertise in the store a change of the given driver to the given new_state.
|
||||
* 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.
|
||||
*/
|
||||
int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state state)
|
||||
{
|
||||
/* We check whether the state is currently set to the given value, and
|
||||
if not, then the state is set. We don't want to unconditionally
|
||||
write the given state, because we don't want to fire watches
|
||||
unnecessarily. Furthermore, if the node has gone, we don't write
|
||||
to it, as the device will be tearing down, and we don't want to
|
||||
resurrect that directory.
|
||||
|
||||
Note that, because of this cached value of our state, this function
|
||||
will not work inside a Xenstore transaction (something it was
|
||||
trying to in the past) because dev->state would not get reset if
|
||||
the transaction was aborted.
|
||||
|
||||
*/
|
||||
|
||||
int current_state;
|
||||
int err;
|
||||
|
||||
if (state == dev->state)
|
||||
return 0;
|
||||
|
||||
err = xenbus_scanf(XBT_NIL, dev->nodename, "state", "%d",
|
||||
¤t_state);
|
||||
if (err != 1)
|
||||
return 0;
|
||||
|
||||
err = xenbus_printf(XBT_NIL, dev->nodename, "state", "%d", state);
|
||||
if (err) {
|
||||
if (state != XenbusStateClosing) /* Avoid looping */
|
||||
xenbus_dev_fatal(dev, err, "writing new state");
|
||||
return err;
|
||||
}
|
||||
|
||||
dev->state = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_switch_state);
|
||||
|
||||
int xenbus_frontend_closed(struct xenbus_device *dev)
|
||||
{
|
||||
xenbus_switch_state(dev, XenbusStateClosed);
|
||||
complete(&dev->down);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_frontend_closed);
|
||||
|
||||
/**
|
||||
* 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(struct xenbus_device *dev)
|
||||
{
|
||||
return kasprintf(GFP_KERNEL, "error/%s", dev->nodename);
|
||||
}
|
||||
|
||||
|
||||
static void xenbus_va_dev_error(struct xenbus_device *dev, int err,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
int ret;
|
||||
unsigned int len;
|
||||
char *printf_buffer = NULL;
|
||||
char *path_buffer = NULL;
|
||||
|
||||
#define PRINTF_BUFFER_SIZE 4096
|
||||
printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (printf_buffer == NULL)
|
||||
goto fail;
|
||||
|
||||
len = sprintf(printf_buffer, "%i ", -err);
|
||||
ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
|
||||
|
||||
BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1);
|
||||
|
||||
dev_err(&dev->dev, "%s\n", printf_buffer);
|
||||
|
||||
path_buffer = error_path(dev);
|
||||
|
||||
if (path_buffer == NULL) {
|
||||
dev_err(&dev->dev, "failed to write error node for %s (%s)\n",
|
||||
dev->nodename, printf_buffer);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (xenbus_write(XBT_NIL, path_buffer, "error", printf_buffer) != 0) {
|
||||
dev_err(&dev->dev, "failed to write error node for %s (%s)\n",
|
||||
dev->nodename, printf_buffer);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
kfree(printf_buffer);
|
||||
kfree(path_buffer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* xenbus_dev_error
|
||||
* @dev: xenbus device
|
||||
* @err: error to report
|
||||
* @fmt: error message format
|
||||
*
|
||||
* Report the given negative errno into the store, along with the given
|
||||
* formatted message.
|
||||
*/
|
||||
void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xenbus_va_dev_error(dev, err, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_dev_error);
|
||||
|
||||
/**
|
||||
* xenbus_dev_fatal
|
||||
* @dev: xenbus device
|
||||
* @err: error to report
|
||||
* @fmt: error message format
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xenbus_va_dev_error(dev, err, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
xenbus_switch_state(dev, XenbusStateClosing);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_dev_fatal);
|
||||
|
||||
/**
|
||||
* xenbus_grant_ring
|
||||
* @dev: xenbus device
|
||||
* @ring_mfn: mfn of ring to grant
|
||||
|
||||
* 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.
|
||||
*/
|
||||
int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn)
|
||||
{
|
||||
int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0);
|
||||
if (err < 0)
|
||||
xenbus_dev_fatal(dev, err, "granting access to ring page");
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_grant_ring);
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
|
||||
{
|
||||
struct evtchn_alloc_unbound alloc_unbound;
|
||||
int err;
|
||||
|
||||
alloc_unbound.dom = DOMID_SELF;
|
||||
alloc_unbound.remote_dom = dev->otherend_id;
|
||||
|
||||
err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
|
||||
&alloc_unbound);
|
||||
if (err)
|
||||
xenbus_dev_fatal(dev, err, "allocating event channel");
|
||||
else
|
||||
*port = alloc_unbound.port;
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_alloc_evtchn);
|
||||
|
||||
|
||||
/**
|
||||
* Bind to an existing interdomain event channel in another domain. Returns 0
|
||||
* on success and stores the local port in *port. On error, returns -errno,
|
||||
* switches the device to XenbusStateClosing, and saves the error in XenStore.
|
||||
*/
|
||||
int xenbus_bind_evtchn(struct xenbus_device *dev, int remote_port, int *port)
|
||||
{
|
||||
struct evtchn_bind_interdomain bind_interdomain;
|
||||
int err;
|
||||
|
||||
bind_interdomain.remote_dom = dev->otherend_id;
|
||||
bind_interdomain.remote_port = remote_port;
|
||||
|
||||
err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
|
||||
&bind_interdomain);
|
||||
if (err)
|
||||
xenbus_dev_fatal(dev, err,
|
||||
"binding to event channel %d from domain %d",
|
||||
remote_port, dev->otherend_id);
|
||||
else
|
||||
*port = bind_interdomain.local_port;
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_bind_evtchn);
|
||||
|
||||
|
||||
/**
|
||||
* Free an existing event channel. Returns 0 on success or -errno on error.
|
||||
*/
|
||||
int xenbus_free_evtchn(struct xenbus_device *dev, int port)
|
||||
{
|
||||
struct evtchn_close close;
|
||||
int err;
|
||||
|
||||
close.port = port;
|
||||
|
||||
err = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
|
||||
if (err)
|
||||
xenbus_dev_error(dev, err, "freeing event channel %d", port);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_free_evtchn);
|
||||
|
||||
|
||||
/**
|
||||
* xenbus_map_ring_valloc
|
||||
* @dev: xenbus device
|
||||
* @gnt_ref: grant reference
|
||||
* @vaddr: pointer to address to be filled out by mapping
|
||||
*
|
||||
* Based on Rusty Russell's skeleton driver's map_page.
|
||||
* Map a page of memory into this domain from another domain's grant table.
|
||||
* xenbus_map_ring_valloc allocates a page of virtual address space, maps the
|
||||
* page to that address, and sets *vaddr to that address.
|
||||
* Returns 0 on success, and GNTST_* (see xen/include/interface/grant_table.h)
|
||||
* or -ENOMEM on error. If an error is returned, device will switch to
|
||||
* XenbusStateClosing and the error message will be saved in XenStore.
|
||||
*/
|
||||
int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr)
|
||||
{
|
||||
struct gnttab_map_grant_ref op = {
|
||||
.flags = GNTMAP_host_map,
|
||||
.ref = gnt_ref,
|
||||
.dom = dev->otherend_id,
|
||||
};
|
||||
struct vm_struct *area;
|
||||
|
||||
*vaddr = NULL;
|
||||
|
||||
area = alloc_vm_area(PAGE_SIZE);
|
||||
if (!area)
|
||||
return -ENOMEM;
|
||||
|
||||
op.host_addr = (unsigned long)area->addr;
|
||||
|
||||
if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
|
||||
BUG();
|
||||
|
||||
if (op.status != GNTST_okay) {
|
||||
free_vm_area(area);
|
||||
xenbus_dev_fatal(dev, op.status,
|
||||
"mapping in shared page %d from domain %d",
|
||||
gnt_ref, dev->otherend_id);
|
||||
return op.status;
|
||||
}
|
||||
|
||||
/* Stuff the handle in an unused field */
|
||||
area->phys_addr = (unsigned long)op.handle;
|
||||
|
||||
*vaddr = area->addr;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc);
|
||||
|
||||
|
||||
/**
|
||||
* xenbus_map_ring
|
||||
* @dev: xenbus device
|
||||
* @gnt_ref: grant reference
|
||||
* @handle: pointer to grant handle to be filled
|
||||
* @vaddr: address to be mapped to
|
||||
*
|
||||
* Map a page of memory into this domain from another domain's grant table.
|
||||
* xenbus_map_ring does not allocate the virtual address space (you must do
|
||||
* this yourself!). It only maps in the page to the specified address.
|
||||
* Returns 0 on success, and GNTST_* (see xen/include/interface/grant_table.h)
|
||||
* or -ENOMEM on error. If an error is returned, device will switch to
|
||||
* XenbusStateClosing and the error message will be saved in XenStore.
|
||||
*/
|
||||
int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref,
|
||||
grant_handle_t *handle, void *vaddr)
|
||||
{
|
||||
struct gnttab_map_grant_ref op = {
|
||||
.host_addr = (unsigned long)vaddr,
|
||||
.flags = GNTMAP_host_map,
|
||||
.ref = gnt_ref,
|
||||
.dom = dev->otherend_id,
|
||||
};
|
||||
|
||||
if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
|
||||
BUG();
|
||||
|
||||
if (op.status != GNTST_okay) {
|
||||
xenbus_dev_fatal(dev, op.status,
|
||||
"mapping in shared page %d from domain %d",
|
||||
gnt_ref, dev->otherend_id);
|
||||
} else
|
||||
*handle = op.handle;
|
||||
|
||||
return op.status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_map_ring);
|
||||
|
||||
|
||||
/**
|
||||
* xenbus_unmap_ring_vfree
|
||||
* @dev: xenbus device
|
||||
* @vaddr: addr to unmap
|
||||
*
|
||||
* Based on Rusty Russell's skeleton driver's unmap_page.
|
||||
* Unmap a page of memory in this domain that was imported from another domain.
|
||||
* Use xenbus_unmap_ring_vfree if you mapped in your memory with
|
||||
* xenbus_map_ring_valloc (it will free the virtual address space).
|
||||
* Returns 0 on success and returns GNTST_* on error
|
||||
* (see xen/include/interface/grant_table.h).
|
||||
*/
|
||||
int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
struct gnttab_unmap_grant_ref op = {
|
||||
.host_addr = (unsigned long)vaddr,
|
||||
};
|
||||
|
||||
/* It'd be nice if linux/vmalloc.h provided a find_vm_area(void *addr)
|
||||
* method so that we don't have to muck with vmalloc internals here.
|
||||
* We could force the user to hang on to their struct vm_struct from
|
||||
* xenbus_map_ring_valloc, but these 6 lines considerably simplify
|
||||
* this API.
|
||||
*/
|
||||
read_lock(&vmlist_lock);
|
||||
for (area = vmlist; area != NULL; area = area->next) {
|
||||
if (area->addr == vaddr)
|
||||
break;
|
||||
}
|
||||
read_unlock(&vmlist_lock);
|
||||
|
||||
if (!area) {
|
||||
xenbus_dev_error(dev, -ENOENT,
|
||||
"can't find mapped virtual address %p", vaddr);
|
||||
return GNTST_bad_virt_addr;
|
||||
}
|
||||
|
||||
op.handle = (grant_handle_t)area->phys_addr;
|
||||
|
||||
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
|
||||
BUG();
|
||||
|
||||
if (op.status == GNTST_okay)
|
||||
free_vm_area(area);
|
||||
else
|
||||
xenbus_dev_error(dev, op.status,
|
||||
"unmapping page at handle %d error %d",
|
||||
(int16_t)area->phys_addr, op.status);
|
||||
|
||||
return op.status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree);
|
||||
|
||||
|
||||
/**
|
||||
* xenbus_unmap_ring
|
||||
* @dev: xenbus device
|
||||
* @handle: grant handle
|
||||
* @vaddr: addr to unmap
|
||||
*
|
||||
* Unmap a page of memory in this domain that was imported from another domain.
|
||||
* Returns 0 on success and returns GNTST_* on error
|
||||
* (see xen/include/interface/grant_table.h).
|
||||
*/
|
||||
int xenbus_unmap_ring(struct xenbus_device *dev,
|
||||
grant_handle_t handle, void *vaddr)
|
||||
{
|
||||
struct gnttab_unmap_grant_ref op = {
|
||||
.host_addr = (unsigned long)vaddr,
|
||||
.handle = handle,
|
||||
};
|
||||
|
||||
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
|
||||
BUG();
|
||||
|
||||
if (op.status != GNTST_okay)
|
||||
xenbus_dev_error(dev, op.status,
|
||||
"unmapping page at handle %d error %d",
|
||||
handle, op.status);
|
||||
|
||||
return op.status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_unmap_ring);
|
||||
|
||||
|
||||
/**
|
||||
* xenbus_read_driver_state
|
||||
* @path: path for driver
|
||||
*
|
||||
* Return the state of the driver rooted at the given store path, or
|
||||
* XenbusStateUnknown if no state can be read.
|
||||
*/
|
||||
enum xenbus_state xenbus_read_driver_state(const char *path)
|
||||
{
|
||||
enum xenbus_state result;
|
||||
int err = xenbus_gather(XBT_NIL, path, "state", "%d", &result, NULL);
|
||||
if (err)
|
||||
result = XenbusStateUnknown;
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_read_driver_state);
|
233
drivers/xen/xenbus/xenbus_comms.c
Normal file
233
drivers/xen/xenbus/xenbus_comms.c
Normal file
|
@ -0,0 +1,233 @@
|
|||
/******************************************************************************
|
||||
* xenbus_comms.c
|
||||
*
|
||||
* Low level code to talks to Xen Store: ringbuffer and event channel.
|
||||
*
|
||||
* Copyright (C) 2005 Rusty Russell, IBM Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/wait.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/err.h>
|
||||
#include <xen/xenbus.h>
|
||||
#include <asm/xen/hypervisor.h>
|
||||
#include <xen/events.h>
|
||||
#include <xen/page.h>
|
||||
#include "xenbus_comms.h"
|
||||
|
||||
static int xenbus_irq;
|
||||
|
||||
static DECLARE_WORK(probe_work, xenbus_probe);
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(xb_waitq);
|
||||
|
||||
static irqreturn_t wake_waiting(int irq, void *unused)
|
||||
{
|
||||
if (unlikely(xenstored_ready == 0)) {
|
||||
xenstored_ready = 1;
|
||||
schedule_work(&probe_work);
|
||||
}
|
||||
|
||||
wake_up(&xb_waitq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
|
||||
{
|
||||
return ((prod - cons) <= XENSTORE_RING_SIZE);
|
||||
}
|
||||
|
||||
static void *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 *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);
|
||||
}
|
||||
|
||||
/**
|
||||
* xb_write - low level write
|
||||
* @data: buffer to send
|
||||
* @len: length of buffer
|
||||
*
|
||||
* Returns 0 on success, error otherwise.
|
||||
*/
|
||||
int xb_write(const void *data, unsigned len)
|
||||
{
|
||||
struct xenstore_domain_interface *intf = xen_store_interface;
|
||||
XENSTORE_RING_IDX cons, prod;
|
||||
int rc;
|
||||
|
||||
while (len != 0) {
|
||||
void *dst;
|
||||
unsigned int avail;
|
||||
|
||||
rc = wait_event_interruptible(
|
||||
xb_waitq,
|
||||
(intf->req_prod - intf->req_cons) !=
|
||||
XENSTORE_RING_SIZE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Read indexes, then verify. */
|
||||
cons = intf->req_cons;
|
||||
prod = intf->req_prod;
|
||||
if (!check_indexes(cons, prod)) {
|
||||
intf->req_cons = intf->req_prod = 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dst = get_output_chunk(cons, prod, intf->req, &avail);
|
||||
if (avail == 0)
|
||||
continue;
|
||||
if (avail > len)
|
||||
avail = len;
|
||||
|
||||
/* Must write data /after/ reading the consumer index. */
|
||||
mb();
|
||||
|
||||
memcpy(dst, data, avail);
|
||||
data += avail;
|
||||
len -= avail;
|
||||
|
||||
/* Other side must not see new producer until data is there. */
|
||||
wmb();
|
||||
intf->req_prod += avail;
|
||||
|
||||
/* Implies mb(): other side will see the updated producer. */
|
||||
notify_remote_via_evtchn(xen_store_evtchn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xb_data_to_read(void)
|
||||
{
|
||||
struct xenstore_domain_interface *intf = xen_store_interface;
|
||||
return (intf->rsp_cons != intf->rsp_prod);
|
||||
}
|
||||
|
||||
int xb_wait_for_data_to_read(void)
|
||||
{
|
||||
return wait_event_interruptible(xb_waitq, xb_data_to_read());
|
||||
}
|
||||
|
||||
int xb_read(void *data, unsigned len)
|
||||
{
|
||||
struct xenstore_domain_interface *intf = xen_store_interface;
|
||||
XENSTORE_RING_IDX cons, prod;
|
||||
int rc;
|
||||
|
||||
while (len != 0) {
|
||||
unsigned int avail;
|
||||
const char *src;
|
||||
|
||||
rc = xb_wait_for_data_to_read();
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Read indexes, then verify. */
|
||||
cons = intf->rsp_cons;
|
||||
prod = intf->rsp_prod;
|
||||
if (!check_indexes(cons, prod)) {
|
||||
intf->rsp_cons = intf->rsp_prod = 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
src = get_input_chunk(cons, prod, intf->rsp, &avail);
|
||||
if (avail == 0)
|
||||
continue;
|
||||
if (avail > len)
|
||||
avail = len;
|
||||
|
||||
/* Must read data /after/ reading the producer index. */
|
||||
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;
|
||||
|
||||
pr_debug("Finished read of %i bytes (%i to go)\n", avail, len);
|
||||
|
||||
/* Implies mb(): other side will see the updated consumer. */
|
||||
notify_remote_via_evtchn(xen_store_evtchn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xb_init_comms - Set up interrupt handler off store event channel.
|
||||
*/
|
||||
int xb_init_comms(void)
|
||||
{
|
||||
struct xenstore_domain_interface *intf = xen_store_interface;
|
||||
int err;
|
||||
|
||||
if (intf->req_prod != intf->req_cons)
|
||||
printk(KERN_ERR "XENBUS request ring is not quiescent "
|
||||
"(%08x:%08x)!\n", intf->req_cons, intf->req_prod);
|
||||
|
||||
if (intf->rsp_prod != intf->rsp_cons) {
|
||||
printk(KERN_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 (xenbus_irq)
|
||||
unbind_from_irqhandler(xenbus_irq, &xb_waitq);
|
||||
|
||||
err = bind_evtchn_to_irqhandler(
|
||||
xen_store_evtchn, wake_waiting,
|
||||
0, "xenbus", &xb_waitq);
|
||||
if (err <= 0) {
|
||||
printk(KERN_ERR "XENBUS request irq failed %i\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
xenbus_irq = err;
|
||||
|
||||
return 0;
|
||||
}
|
46
drivers/xen/xenbus/xenbus_comms.h
Normal file
46
drivers/xen/xenbus/xenbus_comms.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Private include for xenbus communications.
|
||||
*
|
||||
* Copyright (C) 2005 Rusty Russell, IBM Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _XENBUS_COMMS_H
|
||||
#define _XENBUS_COMMS_H
|
||||
|
||||
int xs_init(void);
|
||||
int xb_init_comms(void);
|
||||
|
||||
/* Low level routines. */
|
||||
int xb_write(const void *data, unsigned len);
|
||||
int xb_read(void *data, unsigned len);
|
||||
int xb_data_to_read(void);
|
||||
int xb_wait_for_data_to_read(void);
|
||||
int xs_input_avail(void);
|
||||
extern struct xenstore_domain_interface *xen_store_interface;
|
||||
extern int xen_store_evtchn;
|
||||
|
||||
#endif /* _XENBUS_COMMS_H */
|
935
drivers/xen/xenbus/xenbus_probe.c
Normal file
935
drivers/xen/xenbus/xenbus_probe.c
Normal file
|
@ -0,0 +1,935 @@
|
|||
/******************************************************************************
|
||||
* Talks to Xen Store to figure out what devices we have.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define DPRINTK(fmt, args...) \
|
||||
pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \
|
||||
__func__, __LINE__, ##args)
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/xen/hypervisor.h>
|
||||
#include <xen/xenbus.h>
|
||||
#include <xen/events.h>
|
||||
#include <xen/page.h>
|
||||
|
||||
#include "xenbus_comms.h"
|
||||
#include "xenbus_probe.h"
|
||||
|
||||
int xen_store_evtchn;
|
||||
struct xenstore_domain_interface *xen_store_interface;
|
||||
static unsigned long xen_store_mfn;
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(xenstore_chain);
|
||||
|
||||
static void wait_for_devices(struct xenbus_driver *xendrv);
|
||||
|
||||
static int xenbus_probe_frontend(const char *type, const char *name);
|
||||
|
||||
static void xenbus_dev_shutdown(struct device *_dev);
|
||||
|
||||
/* If something in array of ids matches this device, return it. */
|
||||
static const struct xenbus_device_id *
|
||||
match_device(const struct xenbus_device_id *arr, struct xenbus_device *dev)
|
||||
{
|
||||
for (; *arr->devicetype != '\0'; arr++) {
|
||||
if (!strcmp(arr->devicetype, dev->devicetype))
|
||||
return arr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int xenbus_match(struct device *_dev, struct device_driver *_drv)
|
||||
{
|
||||
struct xenbus_driver *drv = to_xenbus_driver(_drv);
|
||||
|
||||
if (!drv->ids)
|
||||
return 0;
|
||||
|
||||
return match_device(drv->ids, to_xenbus_device(_dev)) != NULL;
|
||||
}
|
||||
|
||||
/* device/<type>/<id> => <type>-<id> */
|
||||
static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename)
|
||||
{
|
||||
nodename = strchr(nodename, '/');
|
||||
if (!nodename || strlen(nodename + 1) >= BUS_ID_SIZE) {
|
||||
printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
strlcpy(bus_id, nodename + 1, BUS_ID_SIZE);
|
||||
if (!strchr(bus_id, '/')) {
|
||||
printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
*strchr(bus_id, '/') = '-';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void free_otherend_details(struct xenbus_device *dev)
|
||||
{
|
||||
kfree(dev->otherend);
|
||||
dev->otherend = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void free_otherend_watch(struct xenbus_device *dev)
|
||||
{
|
||||
if (dev->otherend_watch.node) {
|
||||
unregister_xenbus_watch(&dev->otherend_watch);
|
||||
kfree(dev->otherend_watch.node);
|
||||
dev->otherend_watch.node = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int read_otherend_details(struct xenbus_device *xendev,
|
||||
char *id_node, char *path_node)
|
||||
{
|
||||
int err = xenbus_gather(XBT_NIL, xendev->nodename,
|
||||
id_node, "%i", &xendev->otherend_id,
|
||||
path_node, NULL, &xendev->otherend,
|
||||
NULL);
|
||||
if (err) {
|
||||
xenbus_dev_fatal(xendev, err,
|
||||
"reading other end details from %s",
|
||||
xendev->nodename);
|
||||
return err;
|
||||
}
|
||||
if (strlen(xendev->otherend) == 0 ||
|
||||
!xenbus_exists(XBT_NIL, xendev->otherend, "")) {
|
||||
xenbus_dev_fatal(xendev, -ENOENT,
|
||||
"unable to read other end from %s. "
|
||||
"missing or inaccessible.",
|
||||
xendev->nodename);
|
||||
free_otherend_details(xendev);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int read_backend_details(struct xenbus_device *xendev)
|
||||
{
|
||||
return read_otherend_details(xendev, "backend-id", "backend");
|
||||
}
|
||||
|
||||
|
||||
/* Bus type for frontend drivers. */
|
||||
static struct xen_bus_type xenbus_frontend = {
|
||||
.root = "device",
|
||||
.levels = 2, /* device/type/<id> */
|
||||
.get_bus_id = frontend_bus_id,
|
||||
.probe = xenbus_probe_frontend,
|
||||
.bus = {
|
||||
.name = "xen",
|
||||
.match = xenbus_match,
|
||||
.probe = xenbus_dev_probe,
|
||||
.remove = xenbus_dev_remove,
|
||||
.shutdown = xenbus_dev_shutdown,
|
||||
},
|
||||
};
|
||||
|
||||
static void otherend_changed(struct xenbus_watch *watch,
|
||||
const char **vec, unsigned int len)
|
||||
{
|
||||
struct xenbus_device *dev =
|
||||
container_of(watch, struct xenbus_device, otherend_watch);
|
||||
struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver);
|
||||
enum xenbus_state state;
|
||||
|
||||
/* Protect us against watches firing on old details when the otherend
|
||||
details change, say immediately after a resume. */
|
||||
if (!dev->otherend ||
|
||||
strncmp(dev->otherend, vec[XS_WATCH_PATH],
|
||||
strlen(dev->otherend))) {
|
||||
dev_dbg(&dev->dev, "Ignoring watch at %s", vec[XS_WATCH_PATH]);
|
||||
return;
|
||||
}
|
||||
|
||||
state = xenbus_read_driver_state(dev->otherend);
|
||||
|
||||
dev_dbg(&dev->dev, "state is %d, (%s), %s, %s",
|
||||
state, xenbus_strstate(state), dev->otherend_watch.node,
|
||||
vec[XS_WATCH_PATH]);
|
||||
|
||||
/*
|
||||
* Ignore xenbus transitions during shutdown. This prevents us doing
|
||||
* work that can fail e.g., when the rootfs is gone.
|
||||
*/
|
||||
if (system_state > SYSTEM_RUNNING) {
|
||||
struct xen_bus_type *bus = bus;
|
||||
bus = container_of(dev->dev.bus, struct xen_bus_type, bus);
|
||||
/* If we're frontend, drive the state machine to Closed. */
|
||||
/* This should cause the backend to release our resources. */
|
||||
if ((bus == &xenbus_frontend) && (state == XenbusStateClosing))
|
||||
xenbus_frontend_closed(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (drv->otherend_changed)
|
||||
drv->otherend_changed(dev, state);
|
||||
}
|
||||
|
||||
|
||||
static int talk_to_otherend(struct xenbus_device *dev)
|
||||
{
|
||||
struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver);
|
||||
|
||||
free_otherend_watch(dev);
|
||||
free_otherend_details(dev);
|
||||
|
||||
return drv->read_otherend_details(dev);
|
||||
}
|
||||
|
||||
|
||||
static int watch_otherend(struct xenbus_device *dev)
|
||||
{
|
||||
return xenbus_watch_pathfmt(dev, &dev->otherend_watch, otherend_changed,
|
||||
"%s/%s", dev->otherend, "state");
|
||||
}
|
||||
|
||||
|
||||
int xenbus_dev_probe(struct device *_dev)
|
||||
{
|
||||
struct xenbus_device *dev = to_xenbus_device(_dev);
|
||||
struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
|
||||
const struct xenbus_device_id *id;
|
||||
int err;
|
||||
|
||||
DPRINTK("%s", dev->nodename);
|
||||
|
||||
if (!drv->probe) {
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
id = match_device(drv->ids, dev);
|
||||
if (!id) {
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = talk_to_otherend(dev);
|
||||
if (err) {
|
||||
dev_warn(&dev->dev, "talk_to_otherend on %s failed.\n",
|
||||
dev->nodename);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = drv->probe(dev, id);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = watch_otherend(dev);
|
||||
if (err) {
|
||||
dev_warn(&dev->dev, "watch_otherend on %s failed.\n",
|
||||
dev->nodename);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
xenbus_dev_error(dev, err, "xenbus_dev_probe on %s", dev->nodename);
|
||||
xenbus_switch_state(dev, XenbusStateClosed);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int xenbus_dev_remove(struct device *_dev)
|
||||
{
|
||||
struct xenbus_device *dev = to_xenbus_device(_dev);
|
||||
struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
|
||||
|
||||
DPRINTK("%s", dev->nodename);
|
||||
|
||||
free_otherend_watch(dev);
|
||||
free_otherend_details(dev);
|
||||
|
||||
if (drv->remove)
|
||||
drv->remove(dev);
|
||||
|
||||
xenbus_switch_state(dev, XenbusStateClosed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xenbus_dev_shutdown(struct device *_dev)
|
||||
{
|
||||
struct xenbus_device *dev = to_xenbus_device(_dev);
|
||||
unsigned long timeout = 5*HZ;
|
||||
|
||||
DPRINTK("%s", dev->nodename);
|
||||
|
||||
get_device(&dev->dev);
|
||||
if (dev->state != XenbusStateConnected) {
|
||||
printk(KERN_INFO "%s: %s: %s != Connected, skipping\n", __func__,
|
||||
dev->nodename, xenbus_strstate(dev->state));
|
||||
goto out;
|
||||
}
|
||||
xenbus_switch_state(dev, XenbusStateClosing);
|
||||
timeout = wait_for_completion_timeout(&dev->down, timeout);
|
||||
if (!timeout)
|
||||
printk(KERN_INFO "%s: %s timeout closing device\n",
|
||||
__func__, dev->nodename);
|
||||
out:
|
||||
put_device(&dev->dev);
|
||||
}
|
||||
|
||||
int xenbus_register_driver_common(struct xenbus_driver *drv,
|
||||
struct xen_bus_type *bus,
|
||||
struct module *owner,
|
||||
const char *mod_name)
|
||||
{
|
||||
drv->driver.name = drv->name;
|
||||
drv->driver.bus = &bus->bus;
|
||||
drv->driver.owner = owner;
|
||||
drv->driver.mod_name = mod_name;
|
||||
|
||||
return driver_register(&drv->driver);
|
||||
}
|
||||
|
||||
int __xenbus_register_frontend(struct xenbus_driver *drv,
|
||||
struct module *owner, const char *mod_name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
drv->read_otherend_details = read_backend_details;
|
||||
|
||||
ret = xenbus_register_driver_common(drv, &xenbus_frontend,
|
||||
owner, mod_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If this driver is loaded as a module wait for devices to attach. */
|
||||
wait_for_devices(drv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__xenbus_register_frontend);
|
||||
|
||||
void xenbus_unregister_driver(struct xenbus_driver *drv)
|
||||
{
|
||||
driver_unregister(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_unregister_driver);
|
||||
|
||||
struct xb_find_info
|
||||
{
|
||||
struct xenbus_device *dev;
|
||||
const char *nodename;
|
||||
};
|
||||
|
||||
static int cmp_dev(struct device *dev, void *data)
|
||||
{
|
||||
struct xenbus_device *xendev = to_xenbus_device(dev);
|
||||
struct xb_find_info *info = data;
|
||||
|
||||
if (!strcmp(xendev->nodename, info->nodename)) {
|
||||
info->dev = xendev;
|
||||
get_device(dev);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct xenbus_device *xenbus_device_find(const char *nodename,
|
||||
struct bus_type *bus)
|
||||
{
|
||||
struct xb_find_info info = { .dev = NULL, .nodename = nodename };
|
||||
|
||||
bus_for_each_dev(bus, NULL, &info, cmp_dev);
|
||||
return info.dev;
|
||||
}
|
||||
|
||||
static int cleanup_dev(struct device *dev, void *data)
|
||||
{
|
||||
struct xenbus_device *xendev = to_xenbus_device(dev);
|
||||
struct xb_find_info *info = data;
|
||||
int len = strlen(info->nodename);
|
||||
|
||||
DPRINTK("%s", info->nodename);
|
||||
|
||||
/* Match the info->nodename path, or any subdirectory of that path. */
|
||||
if (strncmp(xendev->nodename, info->nodename, len))
|
||||
return 0;
|
||||
|
||||
/* If the node name is longer, ensure it really is a subdirectory. */
|
||||
if ((strlen(xendev->nodename) > len) && (xendev->nodename[len] != '/'))
|
||||
return 0;
|
||||
|
||||
info->dev = xendev;
|
||||
get_device(dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void xenbus_cleanup_devices(const char *path, struct bus_type *bus)
|
||||
{
|
||||
struct xb_find_info info = { .nodename = path };
|
||||
|
||||
do {
|
||||
info.dev = NULL;
|
||||
bus_for_each_dev(bus, NULL, &info, cleanup_dev);
|
||||
if (info.dev) {
|
||||
device_unregister(&info.dev->dev);
|
||||
put_device(&info.dev->dev);
|
||||
}
|
||||
} while (info.dev);
|
||||
}
|
||||
|
||||
static void xenbus_dev_release(struct device *dev)
|
||||
{
|
||||
if (dev)
|
||||
kfree(to_xenbus_device(dev));
|
||||
}
|
||||
|
||||
static ssize_t xendev_show_nodename(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", to_xenbus_device(dev)->nodename);
|
||||
}
|
||||
DEVICE_ATTR(nodename, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_nodename, NULL);
|
||||
|
||||
static ssize_t xendev_show_devtype(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", to_xenbus_device(dev)->devicetype);
|
||||
}
|
||||
DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL);
|
||||
|
||||
|
||||
int xenbus_probe_node(struct xen_bus_type *bus,
|
||||
const char *type,
|
||||
const char *nodename)
|
||||
{
|
||||
int err;
|
||||
struct xenbus_device *xendev;
|
||||
size_t stringlen;
|
||||
char *tmpstring;
|
||||
|
||||
enum xenbus_state state = xenbus_read_driver_state(nodename);
|
||||
|
||||
if (state != XenbusStateInitialising) {
|
||||
/* Device is not new, so ignore it. This can happen if a
|
||||
device is going away after switching to Closed. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
stringlen = strlen(nodename) + 1 + strlen(type) + 1;
|
||||
xendev = kzalloc(sizeof(*xendev) + stringlen, GFP_KERNEL);
|
||||
if (!xendev)
|
||||
return -ENOMEM;
|
||||
|
||||
xendev->state = XenbusStateInitialising;
|
||||
|
||||
/* Copy the strings into the extra space. */
|
||||
|
||||
tmpstring = (char *)(xendev + 1);
|
||||
strcpy(tmpstring, nodename);
|
||||
xendev->nodename = tmpstring;
|
||||
|
||||
tmpstring += strlen(tmpstring) + 1;
|
||||
strcpy(tmpstring, type);
|
||||
xendev->devicetype = tmpstring;
|
||||
init_completion(&xendev->down);
|
||||
|
||||
xendev->dev.bus = &bus->bus;
|
||||
xendev->dev.release = xenbus_dev_release;
|
||||
|
||||
err = bus->get_bus_id(xendev->dev.bus_id, xendev->nodename);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
/* Register with generic device framework. */
|
||||
err = device_register(&xendev->dev);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = device_create_file(&xendev->dev, &dev_attr_nodename);
|
||||
if (err)
|
||||
goto fail_unregister;
|
||||
|
||||
err = device_create_file(&xendev->dev, &dev_attr_devtype);
|
||||
if (err)
|
||||
goto fail_remove_file;
|
||||
|
||||
return 0;
|
||||
fail_remove_file:
|
||||
device_remove_file(&xendev->dev, &dev_attr_nodename);
|
||||
fail_unregister:
|
||||
device_unregister(&xendev->dev);
|
||||
fail:
|
||||
kfree(xendev);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* device/<typename>/<name> */
|
||||
static int xenbus_probe_frontend(const char *type, const char *name)
|
||||
{
|
||||
char *nodename;
|
||||
int err;
|
||||
|
||||
nodename = kasprintf(GFP_KERNEL, "%s/%s/%s",
|
||||
xenbus_frontend.root, type, name);
|
||||
if (!nodename)
|
||||
return -ENOMEM;
|
||||
|
||||
DPRINTK("%s", nodename);
|
||||
|
||||
err = xenbus_probe_node(&xenbus_frontend, type, nodename);
|
||||
kfree(nodename);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int xenbus_probe_device_type(struct xen_bus_type *bus, const char *type)
|
||||
{
|
||||
int err = 0;
|
||||
char **dir;
|
||||
unsigned int dir_n = 0;
|
||||
int i;
|
||||
|
||||
dir = xenbus_directory(XBT_NIL, bus->root, type, &dir_n);
|
||||
if (IS_ERR(dir))
|
||||
return PTR_ERR(dir);
|
||||
|
||||
for (i = 0; i < dir_n; i++) {
|
||||
err = bus->probe(type, dir[i]);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
kfree(dir);
|
||||
return err;
|
||||
}
|
||||
|
||||
int xenbus_probe_devices(struct xen_bus_type *bus)
|
||||
{
|
||||
int err = 0;
|
||||
char **dir;
|
||||
unsigned int i, dir_n;
|
||||
|
||||
dir = xenbus_directory(XBT_NIL, bus->root, "", &dir_n);
|
||||
if (IS_ERR(dir))
|
||||
return PTR_ERR(dir);
|
||||
|
||||
for (i = 0; i < dir_n; i++) {
|
||||
err = xenbus_probe_device_type(bus, dir[i]);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
kfree(dir);
|
||||
return err;
|
||||
}
|
||||
|
||||
static unsigned int char_count(const char *str, char c)
|
||||
{
|
||||
unsigned int i, ret = 0;
|
||||
|
||||
for (i = 0; str[i]; i++)
|
||||
if (str[i] == c)
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int strsep_len(const char *str, char c, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; str[i]; i++)
|
||||
if (str[i] == c) {
|
||||
if (len == 0)
|
||||
return i;
|
||||
len--;
|
||||
}
|
||||
return (len == 0) ? i : -ERANGE;
|
||||
}
|
||||
|
||||
void xenbus_dev_changed(const char *node, struct xen_bus_type *bus)
|
||||
{
|
||||
int exists, rootlen;
|
||||
struct xenbus_device *dev;
|
||||
char type[BUS_ID_SIZE];
|
||||
const char *p, *root;
|
||||
|
||||
if (char_count(node, '/') < 2)
|
||||
return;
|
||||
|
||||
exists = xenbus_exists(XBT_NIL, node, "");
|
||||
if (!exists) {
|
||||
xenbus_cleanup_devices(node, &bus->bus);
|
||||
return;
|
||||
}
|
||||
|
||||
/* backend/<type>/... or device/<type>/... */
|
||||
p = strchr(node, '/') + 1;
|
||||
snprintf(type, BUS_ID_SIZE, "%.*s", (int)strcspn(p, "/"), p);
|
||||
type[BUS_ID_SIZE-1] = '\0';
|
||||
|
||||
rootlen = strsep_len(node, '/', bus->levels);
|
||||
if (rootlen < 0)
|
||||
return;
|
||||
root = kasprintf(GFP_KERNEL, "%.*s", rootlen, node);
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
dev = xenbus_device_find(root, &bus->bus);
|
||||
if (!dev)
|
||||
xenbus_probe_node(bus, type, root);
|
||||
else
|
||||
put_device(&dev->dev);
|
||||
|
||||
kfree(root);
|
||||
}
|
||||
|
||||
static void frontend_changed(struct xenbus_watch *watch,
|
||||
const char **vec, unsigned int len)
|
||||
{
|
||||
DPRINTK("");
|
||||
|
||||
xenbus_dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend);
|
||||
}
|
||||
|
||||
/* We watch for devices appearing and vanishing. */
|
||||
static struct xenbus_watch fe_watch = {
|
||||
.node = "device",
|
||||
.callback = frontend_changed,
|
||||
};
|
||||
|
||||
static int suspend_dev(struct device *dev, void *data)
|
||||
{
|
||||
int err = 0;
|
||||
struct xenbus_driver *drv;
|
||||
struct xenbus_device *xdev;
|
||||
|
||||
DPRINTK("");
|
||||
|
||||
if (dev->driver == NULL)
|
||||
return 0;
|
||||
drv = to_xenbus_driver(dev->driver);
|
||||
xdev = container_of(dev, struct xenbus_device, dev);
|
||||
if (drv->suspend)
|
||||
err = drv->suspend(xdev);
|
||||
if (err)
|
||||
printk(KERN_WARNING
|
||||
"xenbus: suspend %s failed: %i\n", dev->bus_id, err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int suspend_cancel_dev(struct device *dev, void *data)
|
||||
{
|
||||
int err = 0;
|
||||
struct xenbus_driver *drv;
|
||||
struct xenbus_device *xdev;
|
||||
|
||||
DPRINTK("");
|
||||
|
||||
if (dev->driver == NULL)
|
||||
return 0;
|
||||
drv = to_xenbus_driver(dev->driver);
|
||||
xdev = container_of(dev, struct xenbus_device, dev);
|
||||
if (drv->suspend_cancel)
|
||||
err = drv->suspend_cancel(xdev);
|
||||
if (err)
|
||||
printk(KERN_WARNING
|
||||
"xenbus: suspend_cancel %s failed: %i\n",
|
||||
dev->bus_id, err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resume_dev(struct device *dev, void *data)
|
||||
{
|
||||
int err;
|
||||
struct xenbus_driver *drv;
|
||||
struct xenbus_device *xdev;
|
||||
|
||||
DPRINTK("");
|
||||
|
||||
if (dev->driver == NULL)
|
||||
return 0;
|
||||
|
||||
drv = to_xenbus_driver(dev->driver);
|
||||
xdev = container_of(dev, struct xenbus_device, dev);
|
||||
|
||||
err = talk_to_otherend(xdev);
|
||||
if (err) {
|
||||
printk(KERN_WARNING
|
||||
"xenbus: resume (talk_to_otherend) %s failed: %i\n",
|
||||
dev->bus_id, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
xdev->state = XenbusStateInitialising;
|
||||
|
||||
if (drv->resume) {
|
||||
err = drv->resume(xdev);
|
||||
if (err) {
|
||||
printk(KERN_WARNING
|
||||
"xenbus: resume %s failed: %i\n",
|
||||
dev->bus_id, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = watch_otherend(xdev);
|
||||
if (err) {
|
||||
printk(KERN_WARNING
|
||||
"xenbus_probe: resume (watch_otherend) %s failed: "
|
||||
"%d.\n", dev->bus_id, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xenbus_suspend(void)
|
||||
{
|
||||
DPRINTK("");
|
||||
|
||||
bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_dev);
|
||||
xenbus_backend_suspend(suspend_dev);
|
||||
xs_suspend();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_suspend);
|
||||
|
||||
void xenbus_resume(void)
|
||||
{
|
||||
xb_init_comms();
|
||||
xs_resume();
|
||||
bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, resume_dev);
|
||||
xenbus_backend_resume(resume_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_resume);
|
||||
|
||||
void xenbus_suspend_cancel(void)
|
||||
{
|
||||
xs_suspend_cancel();
|
||||
bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_cancel_dev);
|
||||
xenbus_backend_resume(suspend_cancel_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_suspend_cancel);
|
||||
|
||||
/* A flag to determine if xenstored is 'ready' (i.e. has started) */
|
||||
int xenstored_ready = 0;
|
||||
|
||||
|
||||
int register_xenstore_notifier(struct notifier_block *nb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (xenstored_ready > 0)
|
||||
ret = nb->notifier_call(nb, 0, NULL);
|
||||
else
|
||||
blocking_notifier_chain_register(&xenstore_chain, nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_xenstore_notifier);
|
||||
|
||||
void unregister_xenstore_notifier(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_unregister(&xenstore_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_xenstore_notifier);
|
||||
|
||||
void xenbus_probe(struct work_struct *unused)
|
||||
{
|
||||
BUG_ON((xenstored_ready <= 0));
|
||||
|
||||
/* Enumerate devices in xenstore and watch for changes. */
|
||||
xenbus_probe_devices(&xenbus_frontend);
|
||||
register_xenbus_watch(&fe_watch);
|
||||
xenbus_backend_probe_and_watch();
|
||||
|
||||
/* Notify others that xenstore is up */
|
||||
blocking_notifier_call_chain(&xenstore_chain, 0, NULL);
|
||||
}
|
||||
|
||||
static int __init xenbus_probe_init(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
DPRINTK("");
|
||||
|
||||
err = -ENODEV;
|
||||
if (!is_running_on_xen())
|
||||
goto out_error;
|
||||
|
||||
/* Register ourselves with the kernel bus subsystem */
|
||||
err = bus_register(&xenbus_frontend.bus);
|
||||
if (err)
|
||||
goto out_error;
|
||||
|
||||
err = xenbus_backend_bus_register();
|
||||
if (err)
|
||||
goto out_unreg_front;
|
||||
|
||||
/*
|
||||
* Domain0 doesn't have a store_evtchn or store_mfn yet.
|
||||
*/
|
||||
if (is_initial_xendomain()) {
|
||||
/* dom0 not yet supported */
|
||||
} else {
|
||||
xenstored_ready = 1;
|
||||
xen_store_evtchn = xen_start_info->store_evtchn;
|
||||
xen_store_mfn = xen_start_info->store_mfn;
|
||||
}
|
||||
xen_store_interface = mfn_to_virt(xen_store_mfn);
|
||||
|
||||
/* Initialize the interface to xenstore. */
|
||||
err = xs_init();
|
||||
if (err) {
|
||||
printk(KERN_WARNING
|
||||
"XENBUS: Error initializing xenstore comms: %i\n", err);
|
||||
goto out_unreg_back;
|
||||
}
|
||||
|
||||
if (!is_initial_xendomain())
|
||||
xenbus_probe(NULL);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unreg_back:
|
||||
xenbus_backend_bus_unregister();
|
||||
|
||||
out_unreg_front:
|
||||
bus_unregister(&xenbus_frontend.bus);
|
||||
|
||||
out_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
postcore_initcall(xenbus_probe_init);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int is_disconnected_device(struct device *dev, void *data)
|
||||
{
|
||||
struct xenbus_device *xendev = to_xenbus_device(dev);
|
||||
struct device_driver *drv = data;
|
||||
|
||||
/*
|
||||
* A device with no driver will never connect. We care only about
|
||||
* devices which should currently be in the process of connecting.
|
||||
*/
|
||||
if (!dev->driver)
|
||||
return 0;
|
||||
|
||||
/* Is this search limited to a particular driver? */
|
||||
if (drv && (dev->driver != drv))
|
||||
return 0;
|
||||
|
||||
return (xendev->state != XenbusStateConnected);
|
||||
}
|
||||
|
||||
static int exists_disconnected_device(struct device_driver *drv)
|
||||
{
|
||||
return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
|
||||
is_disconnected_device);
|
||||
}
|
||||
|
||||
static int print_device_status(struct device *dev, void *data)
|
||||
{
|
||||
struct xenbus_device *xendev = to_xenbus_device(dev);
|
||||
struct device_driver *drv = data;
|
||||
|
||||
/* Is this operation limited to a particular driver? */
|
||||
if (drv && (dev->driver != drv))
|
||||
return 0;
|
||||
|
||||
if (!dev->driver) {
|
||||
/* Information only: is this too noisy? */
|
||||
printk(KERN_INFO "XENBUS: Device with no driver: %s\n",
|
||||
xendev->nodename);
|
||||
} else if (xendev->state != XenbusStateConnected) {
|
||||
printk(KERN_WARNING "XENBUS: Timeout connecting "
|
||||
"to device: %s (state %d)\n",
|
||||
xendev->nodename, xendev->state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We only wait for device setup after most initcalls have run. */
|
||||
static int ready_to_wait_for_devices;
|
||||
|
||||
/*
|
||||
* On a 10 second timeout, wait for all devices currently configured. We need
|
||||
* to do this to guarantee that the filesystems and / or network devices
|
||||
* needed for boot are available, before we can allow the boot to proceed.
|
||||
*
|
||||
* This needs to be on a late_initcall, to happen after the frontend device
|
||||
* drivers have been initialised, but before the root fs is mounted.
|
||||
*
|
||||
* A possible improvement here would be to have the tools add a per-device
|
||||
* flag to the store entry, indicating whether it is needed at boot time.
|
||||
* This would allow people who knew what they were doing to accelerate their
|
||||
* boot slightly, but of course needs tools or manual intervention to set up
|
||||
* those flags correctly.
|
||||
*/
|
||||
static void wait_for_devices(struct xenbus_driver *xendrv)
|
||||
{
|
||||
unsigned long timeout = jiffies + 10*HZ;
|
||||
struct device_driver *drv = xendrv ? &xendrv->driver : NULL;
|
||||
|
||||
if (!ready_to_wait_for_devices || !is_running_on_xen())
|
||||
return;
|
||||
|
||||
while (exists_disconnected_device(drv)) {
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
schedule_timeout_interruptible(HZ/10);
|
||||
}
|
||||
|
||||
bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
|
||||
print_device_status);
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
static int __init boot_wait_for_devices(void)
|
||||
{
|
||||
ready_to_wait_for_devices = 1;
|
||||
wait_for_devices(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(boot_wait_for_devices);
|
||||
#endif
|
74
drivers/xen/xenbus/xenbus_probe.h
Normal file
74
drivers/xen/xenbus/xenbus_probe.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/******************************************************************************
|
||||
* xenbus_probe.h
|
||||
*
|
||||
* Talks to Xen Store to figure out what devices we have.
|
||||
*
|
||||
* Copyright (C) 2005 Rusty Russell, IBM Corporation
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
#ifndef _XENBUS_PROBE_H
|
||||
#define _XENBUS_PROBE_H
|
||||
|
||||
#ifdef CONFIG_XEN_BACKEND
|
||||
extern void xenbus_backend_suspend(int (*fn)(struct device *, void *));
|
||||
extern void xenbus_backend_resume(int (*fn)(struct device *, void *));
|
||||
extern void xenbus_backend_probe_and_watch(void);
|
||||
extern int xenbus_backend_bus_register(void);
|
||||
extern void xenbus_backend_bus_unregister(void);
|
||||
#else
|
||||
static inline void xenbus_backend_suspend(int (*fn)(struct device *, void *)) {}
|
||||
static inline void xenbus_backend_resume(int (*fn)(struct device *, void *)) {}
|
||||
static inline void xenbus_backend_probe_and_watch(void) {}
|
||||
static inline int xenbus_backend_bus_register(void) { return 0; }
|
||||
static inline void xenbus_backend_bus_unregister(void) {}
|
||||
#endif
|
||||
|
||||
struct xen_bus_type
|
||||
{
|
||||
char *root;
|
||||
unsigned int levels;
|
||||
int (*get_bus_id)(char bus_id[BUS_ID_SIZE], const char *nodename);
|
||||
int (*probe)(const char *type, const char *dir);
|
||||
struct bus_type bus;
|
||||
};
|
||||
|
||||
extern int xenbus_match(struct device *_dev, struct device_driver *_drv);
|
||||
extern int xenbus_dev_probe(struct device *_dev);
|
||||
extern int xenbus_dev_remove(struct device *_dev);
|
||||
extern int xenbus_register_driver_common(struct xenbus_driver *drv,
|
||||
struct xen_bus_type *bus,
|
||||
struct module *owner,
|
||||
const char *mod_name);
|
||||
extern int xenbus_probe_node(struct xen_bus_type *bus,
|
||||
const char *type,
|
||||
const char *nodename);
|
||||
extern int xenbus_probe_devices(struct xen_bus_type *bus);
|
||||
|
||||
extern void xenbus_dev_changed(const char *node, struct xen_bus_type *bus);
|
||||
|
||||
#endif
|
861
drivers/xen/xenbus/xenbus_xs.c
Normal file
861
drivers/xen/xenbus/xenbus_xs.c
Normal file
|
@ -0,0 +1,861 @@
|
|||
/******************************************************************************
|
||||
* 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 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.
|
||||
*/
|
||||
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <xen/xenbus.h>
|
||||
#include "xenbus_comms.h"
|
||||
|
||||
struct xs_stored_msg {
|
||||
struct list_head 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. */
|
||||
struct list_head reply_list;
|
||||
spinlock_t reply_lock;
|
||||
wait_queue_head_t reply_waitq;
|
||||
|
||||
/*
|
||||
* Mutex ordering: transaction_mutex -> watch_mutex -> request_mutex.
|
||||
* response_mutex is never taken simultaneously with the other three.
|
||||
*/
|
||||
|
||||
/* One request at a time. */
|
||||
struct mutex request_mutex;
|
||||
|
||||
/* Protect xenbus reader thread against save/restore. */
|
||||
struct mutex response_mutex;
|
||||
|
||||
/* Protect transactions against save/restore. */
|
||||
struct rw_semaphore transaction_mutex;
|
||||
|
||||
/* Protect watch (de)register against save/restore. */
|
||||
struct rw_semaphore watch_mutex;
|
||||
};
|
||||
|
||||
static struct xs_handle xs_state;
|
||||
|
||||
/* List of registered watches, and a lock to protect it. */
|
||||
static LIST_HEAD(watches);
|
||||
static DEFINE_SPINLOCK(watches_lock);
|
||||
|
||||
/* List of pending watch callback events, and a lock to protect it. */
|
||||
static LIST_HEAD(watch_events);
|
||||
static DEFINE_SPINLOCK(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;
|
||||
static DEFINE_MUTEX(xenwatch_mutex);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(watch_events_waitq);
|
||||
|
||||
static int get_error(const char *errorstring)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; strcmp(errorstring, xsd_errors[i].errstring) != 0; i++) {
|
||||
if (i == ARRAY_SIZE(xsd_errors) - 1) {
|
||||
printk(KERN_WARNING
|
||||
"XENBUS xen store gave: unknown error %s",
|
||||
errorstring);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
return xsd_errors[i].errnum;
|
||||
}
|
||||
|
||||
static void *read_reply(enum xsd_sockmsg_type *type, unsigned int *len)
|
||||
{
|
||||
struct xs_stored_msg *msg;
|
||||
char *body;
|
||||
|
||||
spin_lock(&xs_state.reply_lock);
|
||||
|
||||
while (list_empty(&xs_state.reply_list)) {
|
||||
spin_unlock(&xs_state.reply_lock);
|
||||
/* XXX FIXME: Avoid synchronous wait for response here. */
|
||||
wait_event(xs_state.reply_waitq,
|
||||
!list_empty(&xs_state.reply_list));
|
||||
spin_lock(&xs_state.reply_lock);
|
||||
}
|
||||
|
||||
msg = list_entry(xs_state.reply_list.next,
|
||||
struct xs_stored_msg, list);
|
||||
list_del(&msg->list);
|
||||
|
||||
spin_unlock(&xs_state.reply_lock);
|
||||
|
||||
*type = msg->hdr.type;
|
||||
if (len)
|
||||
*len = msg->hdr.len;
|
||||
body = msg->u.reply.body;
|
||||
|
||||
kfree(msg);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg)
|
||||
{
|
||||
void *ret;
|
||||
struct xsd_sockmsg req_msg = *msg;
|
||||
int err;
|
||||
|
||||
if (req_msg.type == XS_TRANSACTION_START)
|
||||
down_read(&xs_state.transaction_mutex);
|
||||
|
||||
mutex_lock(&xs_state.request_mutex);
|
||||
|
||||
err = xb_write(msg, sizeof(*msg) + msg->len);
|
||||
if (err) {
|
||||
msg->type = XS_ERROR;
|
||||
ret = ERR_PTR(err);
|
||||
} else
|
||||
ret = read_reply(&msg->type, &msg->len);
|
||||
|
||||
mutex_unlock(&xs_state.request_mutex);
|
||||
|
||||
if ((msg->type == XS_TRANSACTION_END) ||
|
||||
((req_msg.type == XS_TRANSACTION_START) &&
|
||||
(msg->type == XS_ERROR)))
|
||||
up_read(&xs_state.transaction_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Send message to xs, get kmalloc'ed reply. ERR_PTR() on error. */
|
||||
static void *xs_talkv(struct xenbus_transaction t,
|
||||
enum xsd_sockmsg_type type,
|
||||
const struct kvec *iovec,
|
||||
unsigned int num_vecs,
|
||||
unsigned int *len)
|
||||
{
|
||||
struct xsd_sockmsg msg;
|
||||
void *ret = NULL;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
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;
|
||||
|
||||
mutex_lock(&xs_state.request_mutex);
|
||||
|
||||
err = xb_write(&msg, sizeof(msg));
|
||||
if (err) {
|
||||
mutex_unlock(&xs_state.request_mutex);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_vecs; i++) {
|
||||
err = xb_write(iovec[i].iov_base, iovec[i].iov_len);
|
||||
if (err) {
|
||||
mutex_unlock(&xs_state.request_mutex);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
}
|
||||
|
||||
ret = read_reply(&msg.type, len);
|
||||
|
||||
mutex_unlock(&xs_state.request_mutex);
|
||||
|
||||
if (IS_ERR(ret))
|
||||
return ret;
|
||||
|
||||
if (msg.type == XS_ERROR) {
|
||||
err = get_error(ret);
|
||||
kfree(ret);
|
||||
return ERR_PTR(-err);
|
||||
}
|
||||
|
||||
if (msg.type != type) {
|
||||
if (printk_ratelimit())
|
||||
printk(KERN_WARNING
|
||||
"XENBUS unexpected type [%d], expected [%d]\n",
|
||||
msg.type, type);
|
||||
kfree(ret);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Simplified version of xs_talkv: single message. */
|
||||
static void *xs_single(struct xenbus_transaction t,
|
||||
enum xsd_sockmsg_type type,
|
||||
const char *string,
|
||||
unsigned int *len)
|
||||
{
|
||||
struct kvec iovec;
|
||||
|
||||
iovec.iov_base = (void *)string;
|
||||
iovec.iov_len = strlen(string) + 1;
|
||||
return xs_talkv(t, type, &iovec, 1, len);
|
||||
}
|
||||
|
||||
/* Many commands only need an ack, don't care what it says. */
|
||||
static int xs_error(char *reply)
|
||||
{
|
||||
if (IS_ERR(reply))
|
||||
return PTR_ERR(reply);
|
||||
kfree(reply);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (strlen(name) == 0)
|
||||
buffer = kasprintf(GFP_KERNEL, "%s", dir);
|
||||
else
|
||||
buffer = kasprintf(GFP_KERNEL, "%s/%s", dir, name);
|
||||
return (!buffer) ? ERR_PTR(-ENOMEM) : buffer;
|
||||
}
|
||||
|
||||
static char **split(char *strings, unsigned int len, unsigned int *num)
|
||||
{
|
||||
char *p, **ret;
|
||||
|
||||
/* Count the strings. */
|
||||
*num = count_strings(strings, len);
|
||||
|
||||
/* Transfer to one big alloc for easy freeing. */
|
||||
ret = kmalloc(*num * sizeof(char *) + len, GFP_KERNEL);
|
||||
if (!ret) {
|
||||
kfree(strings);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
memcpy(&ret[*num], strings, len);
|
||||
kfree(strings);
|
||||
|
||||
strings = (char *)&ret[*num];
|
||||
for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1)
|
||||
ret[(*num)++] = p;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char **xenbus_directory(struct xenbus_transaction t,
|
||||
const char *dir, const char *node, unsigned int *num)
|
||||
{
|
||||
char *strings, *path;
|
||||
unsigned int len;
|
||||
|
||||
path = join(dir, node);
|
||||
if (IS_ERR(path))
|
||||
return (char **)path;
|
||||
|
||||
strings = xs_single(t, XS_DIRECTORY, path, &len);
|
||||
kfree(path);
|
||||
if (IS_ERR(strings))
|
||||
return (char **)strings;
|
||||
|
||||
return split(strings, len, num);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_directory);
|
||||
|
||||
/* 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 dir_n;
|
||||
|
||||
d = xenbus_directory(t, dir, node, &dir_n);
|
||||
if (IS_ERR(d))
|
||||
return 0;
|
||||
kfree(d);
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_exists);
|
||||
|
||||
/* Get the value of a single file.
|
||||
* Returns a kmalloced value: call free() on it after use.
|
||||
* len indicates length in bytes.
|
||||
*/
|
||||
void *xenbus_read(struct xenbus_transaction t,
|
||||
const char *dir, const char *node, unsigned int *len)
|
||||
{
|
||||
char *path;
|
||||
void *ret;
|
||||
|
||||
path = join(dir, node);
|
||||
if (IS_ERR(path))
|
||||
return (void *)path;
|
||||
|
||||
ret = xs_single(t, XS_READ, path, len);
|
||||
kfree(path);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_read);
|
||||
|
||||
/* Write the value of a single file.
|
||||
* Returns -err on failure.
|
||||
*/
|
||||
int xenbus_write(struct xenbus_transaction t,
|
||||
const char *dir, const char *node, const char *string)
|
||||
{
|
||||
const char *path;
|
||||
struct kvec iovec[2];
|
||||
int ret;
|
||||
|
||||
path = join(dir, node);
|
||||
if (IS_ERR(path))
|
||||
return PTR_ERR(path);
|
||||
|
||||
iovec[0].iov_base = (void *)path;
|
||||
iovec[0].iov_len = strlen(path) + 1;
|
||||
iovec[1].iov_base = (void *)string;
|
||||
iovec[1].iov_len = strlen(string);
|
||||
|
||||
ret = xs_error(xs_talkv(t, XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL));
|
||||
kfree(path);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_write);
|
||||
|
||||
/* 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);
|
||||
if (IS_ERR(path))
|
||||
return PTR_ERR(path);
|
||||
|
||||
ret = xs_error(xs_single(t, XS_MKDIR, path, NULL));
|
||||
kfree(path);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_mkdir);
|
||||
|
||||
/* 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);
|
||||
if (IS_ERR(path))
|
||||
return PTR_ERR(path);
|
||||
|
||||
ret = xs_error(xs_single(t, XS_RM, path, NULL));
|
||||
kfree(path);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_rm);
|
||||
|
||||
/* 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;
|
||||
|
||||
down_read(&xs_state.transaction_mutex);
|
||||
|
||||
id_str = xs_single(XBT_NIL, XS_TRANSACTION_START, "", NULL);
|
||||
if (IS_ERR(id_str)) {
|
||||
up_read(&xs_state.transaction_mutex);
|
||||
return PTR_ERR(id_str);
|
||||
}
|
||||
|
||||
t->id = simple_strtoul(id_str, NULL, 0);
|
||||
kfree(id_str);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_transaction_start);
|
||||
|
||||
/* 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 err;
|
||||
|
||||
if (abort)
|
||||
strcpy(abortstr, "F");
|
||||
else
|
||||
strcpy(abortstr, "T");
|
||||
|
||||
err = xs_error(xs_single(t, XS_TRANSACTION_END, abortstr, NULL));
|
||||
|
||||
up_read(&xs_state.transaction_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_transaction_end);
|
||||
|
||||
/* Single read and scanf: returns -errno or num scanned. */
|
||||
int xenbus_scanf(struct xenbus_transaction t,
|
||||
const char *dir, const char *node, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
char *val;
|
||||
|
||||
val = xenbus_read(t, dir, node, NULL);
|
||||
if (IS_ERR(val))
|
||||
return PTR_ERR(val);
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vsscanf(val, fmt, ap);
|
||||
va_end(ap);
|
||||
kfree(val);
|
||||
/* Distinctive errno. */
|
||||
if (ret == 0)
|
||||
return -ERANGE;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_scanf);
|
||||
|
||||
/* Single printf and write: returns -errno or 0. */
|
||||
int xenbus_printf(struct xenbus_transaction t,
|
||||
const char *dir, const char *node, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
#define PRINTF_BUFFER_SIZE 4096
|
||||
char *printf_buffer;
|
||||
|
||||
printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (printf_buffer == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
BUG_ON(ret > PRINTF_BUFFER_SIZE-1);
|
||||
ret = xenbus_write(t, dir, node, printf_buffer);
|
||||
|
||||
kfree(printf_buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_printf);
|
||||
|
||||
/* 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 ret = 0;
|
||||
|
||||
va_start(ap, dir);
|
||||
while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
|
||||
const char *fmt = va_arg(ap, char *);
|
||||
void *result = va_arg(ap, void *);
|
||||
char *p;
|
||||
|
||||
p = xenbus_read(t, dir, name, NULL);
|
||||
if (IS_ERR(p)) {
|
||||
ret = PTR_ERR(p);
|
||||
break;
|
||||
}
|
||||
if (fmt) {
|
||||
if (sscanf(p, fmt, result) == 0)
|
||||
ret = -EINVAL;
|
||||
kfree(p);
|
||||
} else
|
||||
*(char **)result = p;
|
||||
}
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xenbus_gather);
|
||||
|
||||
static int xs_watch(const char *path, const char *token)
|
||||
{
|
||||
struct kvec iov[2];
|
||||
|
||||
iov[0].iov_base = (void *)path;
|
||||
iov[0].iov_len = strlen(path) + 1;
|
||||
iov[1].iov_base = (void *)token;
|
||||
iov[1].iov_len = strlen(token) + 1;
|
||||
|
||||
return xs_error(xs_talkv(XBT_NIL, XS_WATCH, iov,
|
||||
ARRAY_SIZE(iov), NULL));
|
||||
}
|
||||
|
||||
static int xs_unwatch(const char *path, const char *token)
|
||||
{
|
||||
struct kvec iov[2];
|
||||
|
||||
iov[0].iov_base = (char *)path;
|
||||
iov[0].iov_len = strlen(path) + 1;
|
||||
iov[1].iov_base = (char *)token;
|
||||
iov[1].iov_len = strlen(token) + 1;
|
||||
|
||||
return xs_error(xs_talkv(XBT_NIL, XS_UNWATCH, iov,
|
||||
ARRAY_SIZE(iov), NULL));
|
||||
}
|
||||
|
||||
static struct xenbus_watch *find_watch(const char *token)
|
||||
{
|
||||
struct xenbus_watch *i, *cmp;
|
||||
|
||||
cmp = (void *)simple_strtoul(token, NULL, 16);
|
||||
|
||||
list_for_each_entry(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 err;
|
||||
|
||||
sprintf(token, "%lX", (long)watch);
|
||||
|
||||
down_read(&xs_state.watch_mutex);
|
||||
|
||||
spin_lock(&watches_lock);
|
||||
BUG_ON(find_watch(token));
|
||||
list_add(&watch->list, &watches);
|
||||
spin_unlock(&watches_lock);
|
||||
|
||||
err = xs_watch(watch->node, token);
|
||||
|
||||
/* Ignore errors due to multiple registration. */
|
||||
if ((err != 0) && (err != -EEXIST)) {
|
||||
spin_lock(&watches_lock);
|
||||
list_del(&watch->list);
|
||||
spin_unlock(&watches_lock);
|
||||
}
|
||||
|
||||
up_read(&xs_state.watch_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_xenbus_watch);
|
||||
|
||||
void unregister_xenbus_watch(struct xenbus_watch *watch)
|
||||
{
|
||||
struct xs_stored_msg *msg, *tmp;
|
||||
char token[sizeof(watch) * 2 + 1];
|
||||
int err;
|
||||
|
||||
sprintf(token, "%lX", (long)watch);
|
||||
|
||||
down_read(&xs_state.watch_mutex);
|
||||
|
||||
spin_lock(&watches_lock);
|
||||
BUG_ON(!find_watch(token));
|
||||
list_del(&watch->list);
|
||||
spin_unlock(&watches_lock);
|
||||
|
||||
err = xs_unwatch(watch->node, token);
|
||||
if (err)
|
||||
printk(KERN_WARNING
|
||||
"XENBUS Failed to release watch %s: %i\n",
|
||||
watch->node, err);
|
||||
|
||||
up_read(&xs_state.watch_mutex);
|
||||
|
||||
/* Make sure there are no callbacks running currently (unless
|
||||
its us) */
|
||||
if (current->pid != xenwatch_pid)
|
||||
mutex_lock(&xenwatch_mutex);
|
||||
|
||||
/* Cancel pending watch events. */
|
||||
spin_lock(&watch_events_lock);
|
||||
list_for_each_entry_safe(msg, tmp, &watch_events, list) {
|
||||
if (msg->u.watch.handle != watch)
|
||||
continue;
|
||||
list_del(&msg->list);
|
||||
kfree(msg->u.watch.vec);
|
||||
kfree(msg);
|
||||
}
|
||||
spin_unlock(&watch_events_lock);
|
||||
|
||||
if (current->pid != xenwatch_pid)
|
||||
mutex_unlock(&xenwatch_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_xenbus_watch);
|
||||
|
||||
void xs_suspend(void)
|
||||
{
|
||||
down_write(&xs_state.transaction_mutex);
|
||||
down_write(&xs_state.watch_mutex);
|
||||
mutex_lock(&xs_state.request_mutex);
|
||||
mutex_lock(&xs_state.response_mutex);
|
||||
}
|
||||
|
||||
void xs_resume(void)
|
||||
{
|
||||
struct xenbus_watch *watch;
|
||||
char token[sizeof(watch) * 2 + 1];
|
||||
|
||||
mutex_unlock(&xs_state.response_mutex);
|
||||
mutex_unlock(&xs_state.request_mutex);
|
||||
up_write(&xs_state.transaction_mutex);
|
||||
|
||||
/* No need for watches_lock: the watch_mutex is sufficient. */
|
||||
list_for_each_entry(watch, &watches, list) {
|
||||
sprintf(token, "%lX", (long)watch);
|
||||
xs_watch(watch->node, token);
|
||||
}
|
||||
|
||||
up_write(&xs_state.watch_mutex);
|
||||
}
|
||||
|
||||
void xs_suspend_cancel(void)
|
||||
{
|
||||
mutex_unlock(&xs_state.response_mutex);
|
||||
mutex_unlock(&xs_state.request_mutex);
|
||||
up_write(&xs_state.watch_mutex);
|
||||
up_write(&xs_state.transaction_mutex);
|
||||
}
|
||||
|
||||
static int xenwatch_thread(void *unused)
|
||||
{
|
||||
struct list_head *ent;
|
||||
struct xs_stored_msg *msg;
|
||||
|
||||
for (;;) {
|
||||
wait_event_interruptible(watch_events_waitq,
|
||||
!list_empty(&watch_events));
|
||||
|
||||
if (kthread_should_stop())
|
||||
break;
|
||||
|
||||
mutex_lock(&xenwatch_mutex);
|
||||
|
||||
spin_lock(&watch_events_lock);
|
||||
ent = watch_events.next;
|
||||
if (ent != &watch_events)
|
||||
list_del(ent);
|
||||
spin_unlock(&watch_events_lock);
|
||||
|
||||
if (ent != &watch_events) {
|
||||
msg = list_entry(ent, struct xs_stored_msg, list);
|
||||
msg->u.watch.handle->callback(
|
||||
msg->u.watch.handle,
|
||||
(const char **)msg->u.watch.vec,
|
||||
msg->u.watch.vec_size);
|
||||
kfree(msg->u.watch.vec);
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
mutex_unlock(&xenwatch_mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_msg(void)
|
||||
{
|
||||
struct xs_stored_msg *msg;
|
||||
char *body;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* We must disallow save/restore while reading a xenstore message.
|
||||
* A partial read across s/r leaves us out of sync with xenstored.
|
||||
*/
|
||||
for (;;) {
|
||||
err = xb_wait_for_data_to_read();
|
||||
if (err)
|
||||
return err;
|
||||
mutex_lock(&xs_state.response_mutex);
|
||||
if (xb_data_to_read())
|
||||
break;
|
||||
/* We raced with save/restore: pending data 'disappeared'. */
|
||||
mutex_unlock(&xs_state.response_mutex);
|
||||
}
|
||||
|
||||
|
||||
msg = kmalloc(sizeof(*msg), GFP_KERNEL);
|
||||
if (msg == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = xb_read(&msg->hdr, sizeof(msg->hdr));
|
||||
if (err) {
|
||||
kfree(msg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
body = kmalloc(msg->hdr.len + 1, GFP_KERNEL);
|
||||
if (body == NULL) {
|
||||
kfree(msg);
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = xb_read(body, msg->hdr.len);
|
||||
if (err) {
|
||||
kfree(body);
|
||||
kfree(msg);
|
||||
goto out;
|
||||
}
|
||||
body[msg->hdr.len] = '\0';
|
||||
|
||||
if (msg->hdr.type == XS_WATCH_EVENT) {
|
||||
msg->u.watch.vec = split(body, msg->hdr.len,
|
||||
&msg->u.watch.vec_size);
|
||||
if (IS_ERR(msg->u.watch.vec)) {
|
||||
kfree(msg);
|
||||
err = PTR_ERR(msg->u.watch.vec);
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&watches_lock);
|
||||
msg->u.watch.handle = find_watch(
|
||||
msg->u.watch.vec[XS_WATCH_TOKEN]);
|
||||
if (msg->u.watch.handle != NULL) {
|
||||
spin_lock(&watch_events_lock);
|
||||
list_add_tail(&msg->list, &watch_events);
|
||||
wake_up(&watch_events_waitq);
|
||||
spin_unlock(&watch_events_lock);
|
||||
} else {
|
||||
kfree(msg->u.watch.vec);
|
||||
kfree(msg);
|
||||
}
|
||||
spin_unlock(&watches_lock);
|
||||
} else {
|
||||
msg->u.reply.body = body;
|
||||
spin_lock(&xs_state.reply_lock);
|
||||
list_add_tail(&msg->list, &xs_state.reply_list);
|
||||
spin_unlock(&xs_state.reply_lock);
|
||||
wake_up(&xs_state.reply_waitq);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&xs_state.response_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int xenbus_thread(void *unused)
|
||||
{
|
||||
int err;
|
||||
|
||||
for (;;) {
|
||||
err = process_msg();
|
||||
if (err)
|
||||
printk(KERN_WARNING "XENBUS error %d while reading "
|
||||
"message\n", err);
|
||||
if (kthread_should_stop())
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xs_init(void)
|
||||
{
|
||||
int err;
|
||||
struct task_struct *task;
|
||||
|
||||
INIT_LIST_HEAD(&xs_state.reply_list);
|
||||
spin_lock_init(&xs_state.reply_lock);
|
||||
init_waitqueue_head(&xs_state.reply_waitq);
|
||||
|
||||
mutex_init(&xs_state.request_mutex);
|
||||
mutex_init(&xs_state.response_mutex);
|
||||
init_rwsem(&xs_state.transaction_mutex);
|
||||
init_rwsem(&xs_state.watch_mutex);
|
||||
|
||||
/* Initialize the shared memory rings to talk to xenstored */
|
||||
err = xb_init_comms();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
task = kthread_run(xenwatch_thread, NULL, "xenwatch");
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
xenwatch_pid = task->pid;
|
||||
|
||||
task = kthread_run(xenbus_thread, NULL, "xenbus");
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/desc.h>
|
||||
#if defined(__i386__)
|
||||
# ifdef CONFIG_X86_PAE
|
||||
# include <asm-generic/pgtable-nopud.h>
|
||||
|
|
234
include/xen/xenbus.h
Normal file
234
include/xen/xenbus.h
Normal file
|
@ -0,0 +1,234 @@
|
|||
/******************************************************************************
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _XEN_XENBUS_H
|
||||
#define _XEN_XENBUS_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/init.h>
|
||||
#include <xen/interface/xen.h>
|
||||
#include <xen/interface/grant_table.h>
|
||||
#include <xen/interface/io/xenbus.h>
|
||||
#include <xen/interface/io/xs_wire.h>
|
||||
|
||||
/* Register callback to watch this node. */
|
||||
struct xenbus_watch
|
||||
{
|
||||
struct list_head list;
|
||||
|
||||
/* Path being watched. */
|
||||
const char *node;
|
||||
|
||||
/* Callback (executed in a process context with no locks held). */
|
||||
void (*callback)(struct xenbus_watch *,
|
||||
const char **vec, unsigned int len);
|
||||
};
|
||||
|
||||
|
||||
/* A xenbus device. */
|
||||
struct xenbus_device {
|
||||
const char *devicetype;
|
||||
const char *nodename;
|
||||
const char *otherend;
|
||||
int otherend_id;
|
||||
struct xenbus_watch otherend_watch;
|
||||
struct device dev;
|
||||
enum xenbus_state state;
|
||||
struct completion down;
|
||||
};
|
||||
|
||||
static inline struct xenbus_device *to_xenbus_device(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct xenbus_device, dev);
|
||||
}
|
||||
|
||||
struct xenbus_device_id
|
||||
{
|
||||
/* .../device/<device_type>/<identifier> */
|
||||
char devicetype[32]; /* General class of device. */
|
||||
};
|
||||
|
||||
/* A xenbus driver. */
|
||||
struct xenbus_driver {
|
||||
char *name;
|
||||
struct module *owner;
|
||||
const struct xenbus_device_id *ids;
|
||||
int (*probe)(struct xenbus_device *dev,
|
||||
const struct xenbus_device_id *id);
|
||||
void (*otherend_changed)(struct xenbus_device *dev,
|
||||
enum xenbus_state backend_state);
|
||||
int (*remove)(struct xenbus_device *dev);
|
||||
int (*suspend)(struct xenbus_device *dev);
|
||||
int (*suspend_cancel)(struct xenbus_device *dev);
|
||||
int (*resume)(struct xenbus_device *dev);
|
||||
int (*uevent)(struct xenbus_device *, char **, int, char *, int);
|
||||
struct device_driver driver;
|
||||
int (*read_otherend_details)(struct xenbus_device *dev);
|
||||
};
|
||||
|
||||
static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv)
|
||||
{
|
||||
return container_of(drv, struct xenbus_driver, driver);
|
||||
}
|
||||
|
||||
int __must_check __xenbus_register_frontend(struct xenbus_driver *drv,
|
||||
struct module *owner,
|
||||
const char *mod_name);
|
||||
|
||||
static inline int __must_check
|
||||
xenbus_register_frontend(struct xenbus_driver *drv)
|
||||
{
|
||||
WARN_ON(drv->owner != THIS_MODULE);
|
||||
return __xenbus_register_frontend(drv, THIS_MODULE, KBUILD_MODNAME);
|
||||
}
|
||||
|
||||
int __must_check __xenbus_register_backend(struct xenbus_driver *drv,
|
||||
struct module *owner,
|
||||
const char *mod_name);
|
||||
static inline int __must_check
|
||||
xenbus_register_backend(struct xenbus_driver *drv)
|
||||
{
|
||||
WARN_ON(drv->owner != THIS_MODULE);
|
||||
return __xenbus_register_backend(drv, THIS_MODULE, KBUILD_MODNAME);
|
||||
}
|
||||
|
||||
void xenbus_unregister_driver(struct xenbus_driver *drv);
|
||||
|
||||
struct xenbus_transaction
|
||||
{
|
||||
u32 id;
|
||||
};
|
||||
|
||||
/* Nil transaction ID. */
|
||||
#define XBT_NIL ((struct xenbus_transaction) { 0 })
|
||||
|
||||
int __init xenbus_dev_init(void);
|
||||
|
||||
char **xenbus_directory(struct xenbus_transaction t,
|
||||
const char *dir, const char *node, unsigned int *num);
|
||||
void *xenbus_read(struct xenbus_transaction t,
|
||||
const char *dir, const char *node, unsigned int *len);
|
||||
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 num scanned if > 0. */
|
||||
int xenbus_scanf(struct xenbus_transaction t,
|
||||
const char *dir, const char *node, const char *fmt, ...)
|
||||
__attribute__((format(scanf, 4, 5)));
|
||||
|
||||
/* 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.*/
|
||||
int xenbus_gather(struct xenbus_transaction t, const char *dir, ...);
|
||||
|
||||
/* notifer routines for when the xenstore comes up */
|
||||
extern int xenstored_ready;
|
||||
int register_xenstore_notifier(struct notifier_block *nb);
|
||||
void unregister_xenstore_notifier(struct notifier_block *nb);
|
||||
|
||||
int register_xenbus_watch(struct xenbus_watch *watch);
|
||||
void unregister_xenbus_watch(struct xenbus_watch *watch);
|
||||
void xs_suspend(void);
|
||||
void xs_resume(void);
|
||||
void xs_suspend_cancel(void);
|
||||
|
||||
/* Used by xenbus_dev to borrow kernel's store connection. */
|
||||
void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg);
|
||||
|
||||
struct work_struct;
|
||||
|
||||
/* Prepare for domain suspend: then resume or cancel the suspend. */
|
||||
void xenbus_suspend(void);
|
||||
void xenbus_resume(void);
|
||||
void xenbus_probe(struct work_struct *);
|
||||
void xenbus_suspend_cancel(void);
|
||||
|
||||
#define XENBUS_IS_ERR_READ(str) ({ \
|
||||
if (!IS_ERR(str) && strlen(str) == 0) { \
|
||||
kfree(str); \
|
||||
str = ERR_PTR(-ERANGE); \
|
||||
} \
|
||||
IS_ERR(str); \
|
||||
})
|
||||
|
||||
#define XENBUS_EXIST_ERR(err) ((err) == -ENOENT || (err) == -ERANGE)
|
||||
|
||||
int xenbus_watch_path(struct xenbus_device *dev, const char *path,
|
||||
struct xenbus_watch *watch,
|
||||
void (*callback)(struct xenbus_watch *,
|
||||
const char **, unsigned int));
|
||||
int xenbus_watch_pathfmt(struct xenbus_device *dev, struct xenbus_watch *watch,
|
||||
void (*callback)(struct xenbus_watch *,
|
||||
const char **, unsigned int),
|
||||
const char *pathfmt, ...)
|
||||
__attribute__ ((format (printf, 4, 5)));
|
||||
|
||||
int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state new_state);
|
||||
int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn);
|
||||
int xenbus_map_ring_valloc(struct xenbus_device *dev,
|
||||
int gnt_ref, void **vaddr);
|
||||
int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref,
|
||||
grant_handle_t *handle, void *vaddr);
|
||||
|
||||
int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr);
|
||||
int xenbus_unmap_ring(struct xenbus_device *dev,
|
||||
grant_handle_t handle, void *vaddr);
|
||||
|
||||
int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port);
|
||||
int xenbus_bind_evtchn(struct xenbus_device *dev, int remote_port, int *port);
|
||||
int xenbus_free_evtchn(struct xenbus_device *dev, int port);
|
||||
|
||||
enum xenbus_state xenbus_read_driver_state(const char *path);
|
||||
|
||||
void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...);
|
||||
void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, ...);
|
||||
|
||||
const char *xenbus_strstate(enum xenbus_state state);
|
||||
int xenbus_dev_is_online(struct xenbus_device *dev);
|
||||
int xenbus_frontend_closed(struct xenbus_device *dev);
|
||||
|
||||
#endif /* _XEN_XENBUS_H */
|
Loading…
Reference in a new issue