Hello,
One little thing:
If I am not wrong, xenbus_register_backend() is
not exported, and it seems to me that we
should have EXPORT_SYMBOL(xenbus_register_backend) so
it will build.
-- MR
On 11/2/05, Rusty Russell <rusty@xxxxxxxxxxxxxxx> wrote:
> 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
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|