[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH] skeleton frontend/backend examples and a deadlock
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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |