mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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