mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +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	+= 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/ptrace.h>
 | 
				
			||||||
#include <asm/page.h>
 | 
					#include <asm/page.h>
 | 
				
			||||||
 | 
					#include <asm/desc.h>
 | 
				
			||||||
#if defined(__i386__)
 | 
					#if defined(__i386__)
 | 
				
			||||||
#  ifdef CONFIG_X86_PAE
 | 
					#  ifdef CONFIG_X86_PAE
 | 
				
			||||||
#   include <asm-generic/pgtable-nopud.h>
 | 
					#   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