WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH] skeleton frontend/backend examples and a deadlock

To: Xen Mailing List <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH] skeleton frontend/backend examples and a deadlock
From: Rusty Russell <rusty@xxxxxxxxxxxxxxx>
Date: Wed, 02 Nov 2005 16:22:44 +1100
Delivery-date: Wed, 02 Nov 2005 05:19:55 +0000
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
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@xxxxxxxxxxxxxxx>

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


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel