All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] skeleton frontend/backend examples and a deadlock
@ 2005-11-02  5:22 Rusty Russell
  2005-11-02 12:37 ` Harry Butterworth
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Rusty Russell @ 2005-11-02  5:22 UTC (permalink / raw)
  To: Xen Mailing List

Here are example frontend and backend driver skeletons.  They're
*designed* to handle driver restart and module unloading.  However,
device_unregister deadlocks.  I guess noone tried testing
unregister_xenbus_watch when not called from a watch callback.

The unregistration code in the skeleton driver calls
unregister_xenbus_watch, which deadlocks on the xenwatch_mutex (against
dev_changed->device_find->bus_for_each_dev).  Bring up a skeleton
device, then modprobe -r skeleton_fe to see the deadlock.

Here is the helper script which creates the skeleton device:
#! /bin/sh

if [ $# -ne 2 ]; then
        echo Usage: $0 frontend backend
        exit 1
fi

xenstore-write /local/domain/$1/device/skeleton/100/backend /local/domain/$2/backend/skeleton/$1/100 /local/domain/$1/device/skeleton/100/backend-id $2 /local/domain/$2/backend/skeleton/$1/100/frontend /local/domain/$1/device/skeleton/100 /local/domain/$2/backend/skeleton/$1/100/frontend-id $1
# hotplug scripts in backend would normally do this
xenstore-write /local/domain/$2/backend/skeleton/$1/100/config 777

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>

diff -r 20d1a79ebe31 linux-2.6-xen-sparse/arch/xen/Kconfig
--- a/linux-2.6-xen-sparse/arch/xen/Kconfig	Wed Oct 26 15:59:13 2005
+++ b/linux-2.6-xen-sparse/arch/xen/Kconfig	Wed Nov  2 15:24:59 2005
@@ -155,6 +155,22 @@
 	  If security is not a concern then you may increase performance by
 	  saying N.
 
+config XEN_SKELETON_FE
+	tristate "Compile Xen skeleton example frontend driver code"
+	default m
+	help
+	  There is an example skeleton driver frontend in drivers/xen/skeleton
+	  which you can use as a basis for your own xenbus-aware
+	  drivers.
+
+config XEN_SKELETON_BE
+	tristate "Compile Xen skeleton example backend driver code"
+	default m
+	help
+	  There is an example skeleton driver backend in drivers/xen/skeleton
+	  which you can use as a basis for your own xenbus-aware
+	  drivers.
+
 choice
 	prompt "Processor Type"
 	default XEN_X86
diff -r 20d1a79ebe31 linux-2.6-xen-sparse/drivers/xen/Makefile
--- a/linux-2.6-xen-sparse/drivers/xen/Makefile	Wed Oct 26 15:59:13 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/Makefile	Wed Nov  2 15:24:59 2005
@@ -6,6 +6,7 @@
 obj-y	+= balloon/
 obj-y	+= privcmd/
 obj-y	+= xenbus/
+obj-y	+= skeleton/
 
 obj-$(CONFIG_XEN_BLKDEV_BACKEND)	+= blkback/
 obj-$(CONFIG_XEN_NETDEV_BACKEND)	+= netback/
diff -r 20d1a79ebe31 linux-2.6-xen-sparse/drivers/xen/skeleton/Makefile
--- /dev/null	Wed Oct 26 15:59:13 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/skeleton/Makefile	Wed Nov  2 15:24:59 2005
@@ -0,0 +1,2 @@
+obj-$(CONFIG_XEN_SKELETON_FE)	+= skeleton_fe.o
+obj-$(CONFIG_XEN_SKELETON_BE)	+= skeleton_be.o
diff -r 20d1a79ebe31 linux-2.6-xen-sparse/drivers/xen/skeleton/skeleton_be.c
--- /dev/null	Wed Oct 26 15:59:13 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/skeleton/skeleton_be.c	Wed Nov  2 15:24:59 2005
@@ -0,0 +1,443 @@
+/*  Example backend driver which simply shares a page with the front end.
+    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 as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+*/
+
+/* The "skeleton" device is an example.  The store layout looks like:
+ * In the frontend directory:
+ *    NAME		CREATOR		PURPOSE
+ *    backend		xend		Path to backend to get backend data
+ *    backend-id	xend		Domain ID to give evtchn/grantrefs
+ *    ring-reference    frontend	Tells backend about shared page
+ *    event-channel     frontend	Tells backend about event channel
+ *
+ * In the backend directory:
+ *    NAME		CREATOR		PURPOSE
+ *    frontend		xend		Path to frontend to get frontend data
+ *    frontend-id	xend		ID to accept evtchn/grantrefs from
+ *    config		xend/hotplug	Configuration info for backend.
+ *    stuff		backend		Tells frontend about, um, useful stuff
+ *
+ * As the frontend can be saved/restored, it must handle the "backend" fields
+ * changing (after the ->resume callback).  As either end's driver could go
+ * away (module unload), both frontend and backend must handle the dynamic
+ * fields (ring-reference & event-channel, or stuff) vanishing, and appearing.
+ */
+
+#include <linux/stringify.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <asm-xen/xenbus.h>
+#include <asm-xen/evtchn.h>
+#include <asm-xen/driver_util.h>
+#include <asm-xen/gnttab.h>
+
+/* WAITING: our directory exists, fields aren't there yet.
+ * EXISTS: the tools/udev has written fields we need.
+ * READY: we have written our info into the store for the frontend to read.
+ *        We can only enter this state once frontend is not "connected", to
+ *        cover the case of module reload (frontend might not have noticed us
+ *        going away yet).
+ * CONNECTED: we have read frontend information from the store.  We create the
+ *        "connected" node.
+ */
+enum state
+{
+	WAITING,
+	EXISTS,
+	READY,
+	CONNECTED,
+};
+
+/* Private information about this device */
+struct skeleton_be_info
+{
+	/* xenbus device we belong to */
+	struct xenbus_device *dev;
+
+	/* frontend path */
+	char *frontend;
+
+	/* frontend id */
+	int frontend_id;
+
+	/* Mapping for frontend page */
+	struct vm_struct *vm;
+	u16 shmem_handle;
+
+	/* grant table reference to page frontend offered */
+	int ring_ref;
+
+	/* event channel to send interrupts to frontend */
+	int evtchn;
+	int fe_evtchn;
+
+	/* Watch we place on frontend */
+	struct xenbus_watch watch;
+
+	/* Watch we place on ourselves. */
+	struct xenbus_watch be_watch;
+
+	/* If we are fully connected to backend. */
+	enum state state;
+
+	/* Device-specific (eg. net, block) stuff. */
+	struct device_specific_info *info;
+};
+
+static const char *state(struct skeleton_be_info *info)
+{
+	return info->state == WAITING ? "WAITING" :
+		info->state == EXISTS ? "EXISTS" :
+		info->state == READY ? "READY" :
+		info->state == CONNECTED ? "CONNECTED" : "UNKNOWN";
+}
+
+static inline int bind_event_channel(domid_t id, int evtchn)
+{
+	int err;
+	evtchn_op_t op = {
+		.cmd = EVTCHNOP_bind_interdomain,
+		.u.bind_interdomain.remote_dom = id,
+		.u.bind_interdomain.remote_port = evtchn };
+	err = HYPERVISOR_event_channel_op(&op);
+	if (err)
+		return err;
+	return op.u.bind_interdomain.local_port;
+}
+
+static struct device_specific_info *
+setup_device_specific_crap(struct xenbus_device *dev)
+{
+	int ret, dummy;
+
+	/* Read any local info set up by tools or hotplug/udev
+	 * (eg. device to serve) */
+	ret = xenbus_scanf(NULL, dev->nodename, "config", "%i", &dummy);
+	if (ret != 1)
+		return NULL;
+
+	/* Request net/block/usb/etc device from kernel. */
+	return (void *)1;
+}
+
+static void free_device_specific_crap(struct device_specific_info *crap)
+{
+	/* Release net/block/usb/etc device from kernel. */
+}
+
+static void stop_device_replies(struct device_specific_info *crap)
+{
+	/* Frontend has gone away, we should drop outstanding replies. */
+}
+
+/* Write the information out to the store for the frontend to read, and
+ * know we're ready. */
+static int publish_info(struct skeleton_be_info *info)
+{
+	return xenbus_printf(NULL, info->dev->nodename, "stuff", "%u", 7);
+}
+
+/* Frontend gone/going away.  Clean up. */
+static void skeleton_stop(struct skeleton_be_info *info)
+{
+	printk("%s: state %s\n", __func__, state(info));
+	stop_device_replies(info->info);
+
+	xenbus_rm(NULL, info->dev->nodename, "stuff");
+}
+
+static struct vm_struct *map_page(int ref, domid_t id, u16 *handle)
+{
+	struct gnttab_map_grant_ref op;
+	struct vm_struct *vm;
+
+	vm = alloc_vm_area(PAGE_SIZE);
+	if (!vm)
+		return ERR_PTR(-ENOMEM);
+
+	op.host_addr = (unsigned long)vm->addr;
+	op.flags     = GNTMAP_host_map;
+	op.ref       = ref;
+	op.dom       = id;
+
+	lock_vm_area(vm);
+	BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1));
+	unlock_vm_area(vm);
+
+	if (op.handle < 0) {
+		free_vm_area(vm);
+		return ERR_PTR(op.handle);
+	}
+
+	*handle = op.handle;
+	return vm;
+}
+
+static void unmap_page(struct vm_struct *vm, u16 handle)
+{
+	struct gnttab_unmap_grant_ref op;
+
+	printk("%s enter\n", __func__);
+	op.host_addr    = (unsigned long)vm->addr;
+	op.handle       = handle;
+	op.dev_bus_addr = 0;
+
+	lock_vm_area(vm);
+	BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
+	unlock_vm_area(vm);
+	printk("%s exit\n", __func__);
+}
+
+/* FIXME: This can be called before skeleton_probe() finished. */
+static void frontend_changed(struct xenbus_watch *watch,
+			    const char **vec, unsigned int len)
+{
+	struct skeleton_be_info *info;
+	struct xenbus_transaction *trans;
+	int err;
+
+	info = container_of(watch, struct skeleton_be_info, watch);
+
+	/* If frontend has gone away, shut down. */
+	if (!xenbus_exists(NULL, info->frontend, "")) {
+		device_unregister(&info->dev->dev);
+		return;
+	}
+
+	switch (info->state) {
+	case WAITING:
+		break;
+
+	case EXISTS:
+		if (xenbus_exists(NULL, info->frontend, "connected")) {
+			xenbus_dev_error(info->dev, -EBUSY,
+					 "frontend still connected");
+			return;
+		}
+
+		err = publish_info(info);
+		if (err) {
+			xenbus_dev_error(info->dev, err,
+					 "writing information");
+			return;
+		}			
+		info->state = READY;
+		/* fall thru */
+
+	case READY:
+		/* Try to read frontend stuff. */
+	again:
+		trans = xenbus_transaction_start();
+		if (IS_ERR(trans)) {
+			xenbus_dev_error(info->dev, PTR_ERR(trans),
+					 "starting transaction");
+			return;
+		}
+		err = xenbus_gather(NULL, info->frontend,
+				    "ring-reference", "%u", &info->ring_ref,
+				    "event-channel", "%u", &info->fe_evtchn,
+				    NULL);
+		if (!err) {
+			err = xenbus_transaction_end(trans, 0);
+			if (err == -EAGAIN)
+				goto again;
+		}
+		if (err) {
+			xenbus_dev_error(info->dev, err, "reading from %s",
+					 info->frontend);
+			return;
+		}
+
+		err = bind_event_channel(info->frontend_id, info->fe_evtchn);
+		if (err < 0) {
+			xenbus_dev_error(info->dev, err,
+					 "binding event channel");
+			return;
+		}
+		info->fe_evtchn = err;
+
+		info->vm = map_page(info->ring_ref, info->frontend_id,
+				    &info->shmem_handle);
+		if (IS_ERR(info->vm)) {
+			xenbus_dev_error(info->dev, PTR_ERR(info->vm),
+					 "mapping page");
+			return;
+		}
+		info->state = CONNECTED;
+		/* Clear any previous errors. */
+		xenbus_dev_ok(info->dev);
+		xenbus_printf(NULL, info->dev->nodename, "connected", "ok");
+		break;
+
+	case CONNECTED:
+		/* Did frontend driver shut down? */
+		if (!xenbus_exists(NULL, info->frontend, "ring-reference")) {
+			xenbus_dev_error(info->dev, -ENOENT,
+					 "frontend disconnected");
+			xenbus_rm(NULL, info->dev->nodename, "connected");
+			unmap_page(info->vm, info->shmem_handle);
+			skeleton_stop(info);
+			info->state = EXISTS;
+		}
+	}
+}
+
+static int skeleton_watch_front(struct xenbus_device *dev,
+				struct skeleton_be_info *info)
+{
+	int err;
+
+	printk("%s: state %s\n", __func__, state(info));
+
+	/* We need frontend-id and path. */
+	err = xenbus_gather(NULL, dev->nodename,
+			    "frontend-id", "%i", &info->frontend_id,
+			    "frontend", NULL, &info->frontend,
+			    NULL);
+	if (err) {
+		xenbus_dev_error(dev, err, "reading frontend or frontend-id");
+		goto out;
+	}
+
+	info->watch.node = info->frontend;
+	info->watch.callback = frontend_changed;
+	err = register_xenbus_watch(&info->watch);
+	if (err) {
+		xenbus_dev_error(dev, err, "placing watch on %s",
+				 info->frontend);
+		goto free_frontend;
+	}
+	/* frontend_changed called immediately: stuff might be there already.*/
+	frontend_changed(&info->watch, NULL, 0);
+	return 0;
+
+free_frontend:
+	kfree(info->frontend);
+out:
+	return err;
+}
+
+static void self_changed(struct xenbus_watch *watch,
+			 const char **vec, unsigned int len)
+{
+	struct skeleton_be_info *info;
+	int err;
+
+	info = container_of(watch, struct skeleton_be_info, be_watch);
+	printk("%s: state %s\n", __func__, state(info));
+
+	/* We only need this while we're waiting for config. */
+	if (info->state != WAITING)
+		return;
+
+	/* Not there yet?  Keep waiting. */
+	info->info = setup_device_specific_crap(info->dev);
+	if (!info->info)
+		return;
+
+	info->state = EXISTS;
+	err = skeleton_watch_front(info->dev, info);
+	if (err) {
+		free_device_specific_crap(info->info);
+		return;
+	}
+}
+
+static int skeleton_probe(struct xenbus_device *dev,
+			  const struct xenbus_device_id *id)
+{
+	int err;
+	struct skeleton_be_info *info;
+
+	printk("skeleton_probe_be: %s\n", dev->nodename);
+
+	dev->data = info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		xenbus_dev_error(dev, -ENOMEM, "allocating info structure");
+		return -ENOMEM;
+	}
+	info->dev = dev;
+	info->state = WAITING;
+
+	/* Try for config (might need to wait for udev). */
+	info->be_watch.node = dev->nodename;
+	info->be_watch.callback = self_changed;
+	err = register_xenbus_watch(&info->be_watch);
+	if (err) {
+		xenbus_dev_error(dev, err, "placing watch on self %s",
+				 dev->nodename);
+		kfree(info);
+		return err;
+	}
+	self_changed(&info->be_watch, NULL, 0);
+	return 0;
+}
+
+static int skeleton_remove(struct xenbus_device *dev)
+{
+	struct skeleton_be_info *info = dev->data;
+
+	switch (info->state) {
+	case CONNECTED:
+		unmap_page(info->vm, info->shmem_handle);
+		/* fall thru */
+	case READY:
+		skeleton_stop(info);
+		/* Must remove this after other fields. */
+		xenbus_rm(NULL, dev->nodename, "connected");
+		/* fall thru */
+	case EXISTS:
+		unregister_xenbus_watch(&info->watch);
+		free_device_specific_crap(info->info);
+		/* fall thru */
+	case WAITING:
+		unregister_xenbus_watch(&info->be_watch);
+	}
+
+	kfree(info);
+
+	return 0;
+}
+
+static struct xenbus_device_id skeleton_ids[] = {
+	{ "skeleton" },
+	{ { 0 } },
+};
+
+/* A xenbus backend driver. */
+static struct xenbus_driver driver = {
+	/* Makefile defines KBUILD_MODNAME (in this case, skeleton_be) */
+	.name = __stringify(KBUILD_MODNAME),
+	.owner = THIS_MODULE,
+	.ids = skeleton_ids,
+	.probe = skeleton_probe,
+	.remove = skeleton_remove,
+};
+
+static int init(void)
+{
+	return xenbus_register_backend(&driver);
+}
+
+static void fini(void)
+{
+	xenbus_unregister_driver(&driver);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff -r 20d1a79ebe31 linux-2.6-xen-sparse/drivers/xen/skeleton/skeleton_fe.c
--- /dev/null	Wed Oct 26 15:59:13 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/skeleton/skeleton_fe.c	Wed Nov  2 15:24:59 2005
@@ -0,0 +1,407 @@
+/*  Example frontend driver which simply shares a page with the back end.
+    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 as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+*/
+
+/* The "skeleton" device is an example.  The store layout looks like:
+ * In the frontend directory:
+ *    NAME		CREATOR		PURPOSE
+ *    backend		xend		Path to backend to get backend data
+ *    backend-id	xend		Domain ID to give evtchn/grantrefs
+ *    ring-reference    frontend	Tells backend about shared page
+ *    event-channel     frontend	Tells backend about event channel
+ *
+ * In the backend directory:
+ *    NAME		CREATOR		PURPOSE
+ *    frontend		xend		Path to frontend to get frontend data
+ *    frontend-id	xend		ID to accept evtchn/grantrefs from
+ *    config		xend/hotplug	Configuration info for backend.
+ *    stuff		backend		Tells frontend about, um, useful stuff
+ *
+ * As the frontend can be saved/restored, it must handle the "backend" fields
+ * changing (after the ->resume callback).  As either end's driver could go
+ * away (module unload), both frontend and backend must handle the dynamic
+ * fields (ring-reference & event-channel, or stuff) vanishing, and appearing.
+ */
+
+#include <linux/stringify.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <asm-xen/xenbus.h>
+#include <asm-xen/evtchn.h>
+#include <asm-xen/gnttab.h>
+
+/* EXISTS: our directory exists, with the tool-written stuff in it.
+ * READY: we have written our info into the store for the backend to read.
+ *        We can only enter this state once backend is not "connected", to
+ *        cover the case of module reload (backend might not have noticed us
+ *        going away yet!).
+ * CONNECTED: we have read backend information from the store.  We create the
+ *        "connected" node.
+ */
+enum state
+{
+	EXISTS,
+	READY,
+	CONNECTED,
+};
+
+/* Private information about this device */
+struct skeleton_info
+{
+	/* xenbus device we belong to */
+	struct xenbus_device *dev;
+
+	/* backend path */
+	char *backend;
+
+	/* backend id */
+	int backend_id;
+
+	/* page we offer to share */
+	void *page;
+
+	/* grant table reference to page we offer to backend */
+	int ring_ref;
+
+	/* event channel to send interrupts to backend */
+	int evtchn;
+
+	/* Watch we place on backend */
+	struct xenbus_watch watch;
+
+	/* If we are fully connected to backend. */
+	enum state state;
+
+	/* Information given by the backend */
+	int backend_stuff;
+
+	/* Device-specific (eg. net, block) stuff. */
+	struct device_specific_info *info;
+};
+
+static const char *state(struct skeleton_info *info)
+{
+	return info->state == EXISTS ? "EXISTS" :
+		info->state == READY ? "READY" :
+		info->state == CONNECTED ? "CONNECTED" : "UNKNOWN";
+}
+
+static inline int allocate_event_channel(domid_t id)
+{
+	int err;
+	evtchn_op_t op = {
+		.cmd = EVTCHNOP_alloc_unbound,
+		.u.alloc_unbound.dom = DOMID_SELF,
+		.u.alloc_unbound.remote_dom = id };
+
+	err = HYPERVISOR_event_channel_op(&op);
+	if (err)
+		return err;
+	return op.u.alloc_unbound.port;
+}
+
+static inline void free_event_channel(int evtchn)
+{
+	evtchn_op_t op = {
+		.cmd = EVTCHNOP_close,
+		.u.close.port = evtchn };
+	HYPERVISOR_event_channel_op(&op);
+}
+
+static struct device_specific_info *
+setup_device_specific_crap(struct xenbus_device *dev)
+{
+	/* Read any local info set up by tools (eg. mac address) on creation */
+
+	/* Register as a net/block/usb/etc device with kernel. */
+
+	return (void *)1;
+}
+
+static void free_device_specific_crap(struct device_specific_info *crap)
+{
+	/* Unregister as a net/block/usb/etc device with kernel. */
+}
+
+static void stop_device_requests(struct device_specific_info *crap)
+{
+	/* Backend has gone away, we should queue requests. */
+}
+
+/* Write the information out to the store for the backend to read, and
+ * know we're ready. */
+static int publish_info(struct skeleton_info *info)
+{
+	struct xenbus_transaction *trans;
+	int err;
+
+	printk("%s: state %s\n", __func__, state(info));
+	/* Transactions can fail spuriously, which means we loop. */
+again:
+	trans = xenbus_transaction_start();
+	if (IS_ERR(trans))
+		return PTR_ERR(trans);
+
+	err = xenbus_printf(trans, info->dev->nodename, "ring-reference", "%u",
+			    info->ring_ref);
+	if (!err)
+		err = xenbus_printf(trans, info->dev->nodename,
+				    "event-channel", "%u", info->evtchn);
+
+	if (err) {
+		xenbus_transaction_end(trans, 1);
+		return err;
+	}
+	err = xenbus_transaction_end(trans, 0);
+	if (err == -EAGAIN)
+		goto again;
+	return err;
+}
+
+/* Backend gone/going away.  Clean up. */
+static void skeleton_stop(struct skeleton_info *info)
+{
+	printk("%s: state %s\n", __func__, state(info));
+	stop_device_requests(info->info);
+
+	/* FIXME: can't use transaction, it requires alloc. */
+	xenbus_rm(NULL, info->dev->nodename, "ring-reference");
+	xenbus_rm(NULL, info->dev->nodename, "event-channel");
+}
+
+/* FIXME: This can be called before skeleton_probe() finished. */
+static void backend_changed(struct xenbus_watch *watch,
+			    const char **vec, unsigned int len)
+{
+	struct skeleton_info *info;
+	int err;
+
+	info = container_of(watch, struct skeleton_info, watch);
+
+	printk("%s: state %s\n", __func__, state(info));
+
+	switch (info->state) {
+	case EXISTS:
+		if (xenbus_exists(NULL, info->backend, "connected")) {
+			xenbus_dev_error(info->dev, -EBUSY,
+					 "backend still connected");
+			return;
+		}
+
+		err = publish_info(info);
+		if (err) {
+			xenbus_dev_error(info->dev, err,
+					 "writing information");
+			return;
+		}			
+		info->state = READY;
+		/* fall thru */
+
+	case READY:
+		/* Try to read backend stuff. */
+		err = xenbus_scanf(NULL, info->backend, "stuff", "%u",
+				   &info->backend_stuff);
+		if (err < 0) {
+			xenbus_dev_error(info->dev, err, "reading %s/stuff",
+					 info->backend);
+			return;
+		}
+		info->state = CONNECTED;
+		/* Clear any previous errors. */
+		xenbus_dev_ok(info->dev);
+		xenbus_printf(NULL, info->dev->nodename, "connected", "ok");
+		break;
+
+	case CONNECTED:
+		/* Did backend driver shut down? */
+		if (!xenbus_exists(NULL, info->backend, "stuff")) {
+			xenbus_dev_error(info->dev, -ENOENT,
+					 "backend disconnected");
+			xenbus_rm(NULL, info->dev->nodename, "connected");
+			skeleton_stop(info);
+			info->state = EXISTS;
+		}
+	}
+}
+
+static int skeleton_resume(struct xenbus_device *dev)
+{
+	int err;
+	struct skeleton_info *info = dev->data;
+
+	printk("%s: state %s\n", __func__, state(info));
+
+	/* We need backend-id and path. */
+	err = xenbus_gather(NULL, dev->nodename,
+			    "backend-id", "%i", &info->backend_id,
+			    "backend", NULL, &info->backend,
+			    NULL);
+	if (err) {
+		xenbus_dev_error(dev, err, "reading backend or backend-id");
+		goto out;
+	}
+
+	/* We need to allocate a page and event channel. */
+	info->page = (void *)__get_free_page(GFP_KERNEL);
+	if (!info->page) {
+		err = -ENOMEM;
+		xenbus_dev_error(dev, err, "allocating shared page");
+		goto free_backend;
+	}
+
+	err = gnttab_grant_foreign_access(info->backend_id,
+					  virt_to_mfn(info->page), 0);
+	if (err < 0) {
+		xenbus_dev_error(dev, err, "granting page");
+		goto free_page;
+	}
+	info->ring_ref = err;
+
+	err = allocate_event_channel(info->backend_id);
+	if (err < 0) {
+		xenbus_dev_error(dev, err, "allocating event channel");
+		goto ungrant_page;
+	}
+	info->evtchn = err;
+
+	info->watch.node = info->backend;
+	info->watch.callback = backend_changed;
+	err = register_xenbus_watch(&info->watch);
+	if (err) {
+		xenbus_dev_error(dev, err, "placing watch on %s", info->backend);
+		goto free_event_channel;
+	}
+	/* backend_changed called immediately: stuff might be there already. */
+	backend_changed(&info->watch, NULL, 0);
+	return 0;
+
+free_event_channel:
+	free_event_channel(info->evtchn);
+ungrant_page:
+	/* FIXME: Need infrastructure to handle otherside holding onto page. */
+	gnttab_end_foreign_access(info->ring_ref, 0);
+free_page:
+	free_page((unsigned long)info->page);
+free_backend:
+	kfree(info->backend);
+out:
+	return err;
+}
+
+static int skeleton_probe(struct xenbus_device *dev,
+			  const struct xenbus_device_id *id)
+{
+	int err;
+	struct skeleton_info *info;
+
+	printk("skeleton_probe for %s\n", dev->nodename);
+
+	dev->data = info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		xenbus_dev_error(dev, -ENOMEM, "allocating info structure");
+		return -ENOMEM;
+	}
+	info->dev = dev;
+	info->state = EXISTS;
+
+	info->info = setup_device_specific_crap(dev);
+	if (IS_ERR(info->info)) {
+		err = PTR_ERR(info->info);
+		xenbus_dev_error(dev, err, "setting up device");
+		kfree(info);
+		return err;
+	}
+
+	err = skeleton_resume(dev);
+	if (err) {
+		free_device_specific_crap(info->info);
+		kfree(info);
+	}
+	return err;
+}
+
+/* Clean up: will re-init and connect to backend on resume. */
+static int skeleton_suspend(struct xenbus_device *dev)
+{
+	struct skeleton_info *info = dev->data;
+
+	printk("%s: state %s\n", __func__, state(info));
+	switch (info->state) {
+	case CONNECTED:
+		xenbus_rm(NULL, dev->nodename, "connected");
+		info->state = READY;
+		/* fall thru */
+	case READY:
+		skeleton_stop(info);
+		info->state = EXISTS;
+	case EXISTS:
+		;		/* Nothing to do */
+	}
+
+	/* FIXME: Need infrastructure to handle otherside holding onto page. */
+	gnttab_end_foreign_access(info->ring_ref, 0);
+	free_page((unsigned long)info->page);
+	kfree(info->backend);
+	printk("%s:%i\n", __func__, __LINE__);
+	unregister_xenbus_watch(&info->watch);
+	printk("%s:%i\n", __func__, __LINE__);
+	return 0;
+}
+
+static int skeleton_remove(struct xenbus_device *dev)
+{
+	struct skeleton_info *info = dev->data;
+
+	printk("%s: state %s\n", __func__, state(info));
+	skeleton_suspend(dev);
+	free_device_specific_crap(info->info);
+	kfree(info);
+	printk("%s exiting\n", __func__);
+
+	return 0;
+}
+
+static struct xenbus_device_id skeleton_ids[] = {
+	{ "skeleton" },
+	{ { 0 } },
+};
+
+/* A xenbus driver. */
+static struct xenbus_driver driver = {
+	/* Makefile defines KBUILD_MODNAME (in this case, skeleton_fe) */
+	.name = __stringify(KBUILD_MODNAME),
+	.owner = THIS_MODULE,
+	.ids = skeleton_ids,
+	.probe = skeleton_probe,
+	.remove = skeleton_remove,
+	.suspend = skeleton_suspend,
+	.resume = skeleton_resume,
+};
+
+static int init(void)
+{
+	return xenbus_register_driver(&driver);
+}
+
+static void fini(void)
+{
+	xenbus_unregister_driver(&driver);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");

-- 
A bad analogy is like a leaky screwdriver -- Richard Braakman

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2005-11-15  0:16 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-11-02  5:22 [PATCH] skeleton frontend/backend examples and a deadlock Rusty Russell
2005-11-02 12:37 ` Harry Butterworth
2005-11-03  1:36   ` Rusty Russell
2005-11-03  2:17   ` Mark Williamson
2005-11-02 12:52 ` Mark Ryden
2005-11-03  1:23   ` Rusty Russell
2005-11-15  0:16 ` Ewan Mellor

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.