[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] New Xenbus driver structure, bringing an explicit state machine to the table.
# HG changeset patch # User emellor@xxxxxxxxxxxxxxxxxxxxxx # Node ID bf7c16e761fcb27806f4793fe017e3259775b8c0 # Parent e2e7f47e6f79721d97fd7a1f84c5299661aa9ad9 New Xenbus driver structure, bringing an explicit state machine to the table. This will make the drivers more robust to failure of the other end, and makes current driver state more visible. It also allows us to bring very similar pieces of code from the drivers together into the xenbus driver. This should be the start of a fix to the problem with block and net device hotplugging, because the code was previously confused about the difference between the connection state of the kernel device layer, and the state of the xenbus communication. This confusion is addressed here. Much of this work is influenced by Rusty Russell's skeleton driver, recently posted to xen-devel, as that piece made suggestions as to structure and arrangement that have been taken on board, albeit pushed down into the xenbus layer. A new file xenbus_client.c has been added. This file implements the interface between the xenbus layer and the layer specific to the driver. Access to lower layers is still allowed for those who do not need all the facilities of the new driver. xenbus_dev_error has moved from xenbus_xs.c into xenbus_client.c, and xenbus_dev_ok has been removed, in favour of xenbus_switch_state. DevController.py now closes down a device by writing a change to the Closing state on the backend, to allow the frontend time to close down properly. Its also writes the frontend and backend details within the same transaction. The timeout on the hotplug scripts has been cranked down from 2 minutes to 5 seconds. The diagnose script has been updated to parse the new states. The blktab and tpm drivers have yet to be ported to this structure. Signed-off-by: Ewan Mellor <ewan@xxxxxxxxxxxxx> diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c --- a/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c Fri Nov 11 20:59:05 2005 @@ -1,5 +1,6 @@ /* Xenbus code for blkif backend Copyright (C) 2005 Rusty Russell <rusty@xxxxxxxxxxxxxxx> + Copyright (C) 2005 XenSource Ltd This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,160 +16,142 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + #include <stdarg.h> #include <linux/module.h> #include <asm-xen/xenbus.h> #include "common.h" + +#if 0 +#undef DPRINTK +#define DPRINTK(fmt, args...) \ + printk("blkback/xenbus (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args) +#endif + + struct backend_info { struct xenbus_device *dev; - - /* our communications channel */ blkif_t *blkif; - - long int frontend_id; + struct xenbus_watch backend_watch; + long int pdev; long int readonly; - - /* watch back end for changes */ - struct xenbus_watch backend_watch; - - /* watch front end for changes */ - struct xenbus_watch watch; - char *frontpath; }; + +static void maybe_connect(struct backend_info *); +static void connect(struct backend_info *); +static int connect_ring(struct backend_info *); +static void backend_changed(struct xenbus_watch *, const char **, + unsigned int); + + static int blkback_remove(struct xenbus_device *dev) { struct backend_info *be = dev->data; - if (be->watch.node) - unregister_xenbus_watch(&be->watch); - unregister_xenbus_watch(&be->backend_watch); - if (be->blkif) + DPRINTK(""); + + if (be->backend_watch.node) { + unregister_xenbus_watch(&be->backend_watch); + kfree(be->backend_watch.node); + be->backend_watch.node = NULL; + } + if (be->blkif) { blkif_put(be->blkif); - kfree(be->frontpath); + be->blkif = NULL; + } kfree(be); + dev->data = NULL; return 0; } -/* Front end tells us frame. */ -static void frontend_changed(struct xenbus_watch *watch, - const char **vec, unsigned int len) -{ - unsigned long ring_ref; - unsigned int evtchn; - int err; - struct xenbus_transaction *xbt; - struct backend_info *be - = container_of(watch, struct backend_info, watch); - - /* If other end is gone, delete ourself. */ - if (vec && !xenbus_exists(NULL, be->frontpath, "")) { - device_unregister(&be->dev->dev); - return; - } - if (be->blkif == NULL || be->blkif->status == CONNECTED) - return; - - err = xenbus_gather(NULL, be->frontpath, "ring-ref", "%lu", &ring_ref, - "event-channel", "%u", &evtchn, NULL); - if (err) { - xenbus_dev_error(be->dev, err, - "reading %s/ring-ref and event-channel", - be->frontpath); - return; - } - - /* Map the shared frame, irq etc. */ - err = blkif_map(be->blkif, ring_ref, evtchn); - if (err) { - xenbus_dev_error(be->dev, err, - "mapping ring-ref %lu port %u", - ring_ref, evtchn); - return; - } - /* XXX From here on should 'blkif_unmap' on error. */ - -again: - /* Supply the information about the device the frontend needs */ - xbt = xenbus_transaction_start(); - if (IS_ERR(xbt)) { - xenbus_dev_error(be->dev, err, "starting transaction"); - return; - } - - err = xenbus_printf(xbt, be->dev->nodename, "sectors", "%lu", - vbd_size(&be->blkif->vbd)); - if (err) { - xenbus_dev_error(be->dev, err, "writing %s/sectors", - be->dev->nodename); - goto abort; - } - - /* FIXME: use a typename instead */ - err = xenbus_printf(xbt, be->dev->nodename, "info", "%u", - vbd_info(&be->blkif->vbd)); - if (err) { - xenbus_dev_error(be->dev, err, "writing %s/info", - be->dev->nodename); - goto abort; - } - err = xenbus_printf(xbt, be->dev->nodename, "sector-size", "%lu", - vbd_secsize(&be->blkif->vbd)); - if (err) { - xenbus_dev_error(be->dev, err, "writing %s/sector-size", - be->dev->nodename); - goto abort; - } - - err = xenbus_transaction_end(xbt, 0); - if (err == -EAGAIN) - goto again; - if (err) { - xenbus_dev_error(be->dev, err, "ending transaction", - ring_ref, evtchn); - goto abort; - } - - xenbus_dev_ok(be->dev); - - return; - - abort: - xenbus_transaction_end(xbt, 1); -} - -/* - Setup supplies physical device. - We provide event channel and device details to front end. - Frontend supplies shared frame and event channel. + +/** + * Entry point to this code when a new device is created. Allocate the basic + * structures, and watch the store waiting for the hotplug scripts to tell us + * the device's physical-device. Switch to InitWait. + */ +static int blkback_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err; + struct backend_info *be = kmalloc(sizeof(struct backend_info), + GFP_KERNEL); + if (!be) { + xenbus_dev_fatal(dev, -ENOMEM, + "allocating backend structure"); + return -ENOMEM; + } + memset(be, 0, sizeof(*be)); + + be->dev = dev; + dev->data = be; + + be->blkif = alloc_blkif(dev->otherend_id); + if (IS_ERR(be->blkif)) { + err = PTR_ERR(be->blkif); + be->blkif = NULL; + xenbus_dev_fatal(dev, err, "creating block interface"); + goto fail; + } + + err = xenbus_watch_path2(dev, dev->nodename, "physical-device", + &be->backend_watch, backend_changed); + if (err) + goto fail; + + err = xenbus_switch_state(dev, NULL, XenbusStateInitWait); + if (err) + goto fail; + + return 0; + +fail: + DPRINTK("failed"); + blkback_remove(dev); + return err; +} + + +/** + * Callback received when the hotplug scripts have placed the physical-device + * node. Read it and the read-only node, and create a vbd. If the frontend + * is ready, connect. */ static void backend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len) { int err; char *p; - long int handle, pdev; + long pdev; struct backend_info *be = container_of(watch, struct backend_info, backend_watch); struct xenbus_device *dev = be->dev; + DPRINTK(""); + err = xenbus_scanf(NULL, dev->nodename, "physical-device", "%li", &pdev); - if (XENBUS_EXIST_ERR(err)) + if (XENBUS_EXIST_ERR(err)) { + /* Since this watch will fire once immediately after it is + registered, we expect this. Ignore it, and wait for the + hotplug scripts. */ return; - if (err < 0) { - xenbus_dev_error(dev, err, "reading physical-device"); + } + if (err != 1) { + xenbus_dev_fatal(dev, err, "reading physical-device"); return; } if (be->pdev && be->pdev != pdev) { printk(KERN_WARNING - "changing physical-device not supported\n"); + "blkback: changing physical-device (from %ld to %ld) " + "not supported.\n", be->pdev, pdev); return; } - be->pdev = pdev; /* If there's a read-only node, we're read only. */ p = xenbus_read(NULL, dev->nodename, "read-only", NULL); @@ -177,111 +160,181 @@ kfree(p); } - if (be->blkif == NULL) { + if (be->pdev == 0L) { /* Front end dir is a number, which is used as the handle. */ - p = strrchr(be->frontpath, '/') + 1; + + long handle; + + p = strrchr(dev->otherend, '/') + 1; handle = simple_strtoul(p, NULL, 0); - be->blkif = alloc_blkif(be->frontend_id); - if (IS_ERR(be->blkif)) { - err = PTR_ERR(be->blkif); - be->blkif = NULL; - xenbus_dev_error(dev, err, - "creating block interface"); - return; - } + be->pdev = pdev; err = vbd_create(be->blkif, handle, be->pdev, be->readonly); if (err) { - blkif_put(be->blkif); - be->blkif = NULL; - xenbus_dev_error(dev, err, + be->pdev = 0L; + xenbus_dev_fatal(dev, err, "creating vbd structure"); return; } - /* Pass in NULL node to skip exist test. */ - frontend_changed(&be->watch, NULL, 0); - } -} - -static int blkback_probe(struct xenbus_device *dev, - const struct xenbus_device_id *id) -{ - struct backend_info *be; - char *frontend; - int err; - - be = kmalloc(sizeof(*be), GFP_KERNEL); - if (!be) { - xenbus_dev_error(dev, -ENOMEM, - "allocating backend structure"); - return -ENOMEM; - } - memset(be, 0, sizeof(*be)); - - frontend = NULL; - err = xenbus_gather(NULL, dev->nodename, - "frontend-id", "%li", &be->frontend_id, - "frontend", NULL, &frontend, - NULL); - if (XENBUS_EXIST_ERR(err)) - goto free_be; - if (err < 0) { - xenbus_dev_error(dev, err, - "reading %s/frontend or frontend-id", + maybe_connect(be); + } +} + + +/** + * Callback received when the frontend's state changes. + */ +static void frontend_changed(struct xenbus_device *dev, + XenbusState frontend_state) +{ + struct backend_info *be = dev->data; + int err; + + DPRINTK(""); + + switch (frontend_state) { + case XenbusStateInitialising: + case XenbusStateConnected: + break; + + case XenbusStateInitialised: + err = connect_ring(be); + if (err) { + return; + } + maybe_connect(be); + break; + + case XenbusStateClosing: + xenbus_switch_state(dev, NULL, XenbusStateClosing); + break; + + case XenbusStateClosed: + device_unregister(&dev->dev); + break; + + case XenbusStateUnknown: + case XenbusStateInitWait: + default: + xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend", + frontend_state); + break; + } +} + + +/* ** Connection ** */ + + +static void maybe_connect(struct backend_info *be) +{ + if (be->pdev != 0L && be->blkif->status == CONNECTED) { + connect(be); + } +} + + +/** + * Write the physical details regarding the block device to the store, and + * switch to Connected state. + */ +static void connect(struct backend_info *be) +{ + struct xenbus_transaction *xbt; + int err; + struct xenbus_device *dev = be->dev; + + DPRINTK("%s", dev->otherend); + + /* Supply the information about the device the frontend needs */ +again: + xbt = xenbus_transaction_start(); + + if (IS_ERR(xbt)) { + err = PTR_ERR(xbt); + xenbus_dev_fatal(dev, err, "starting transaction"); + return; + } + + err = xenbus_printf(xbt, dev->nodename, "sectors", "%lu", + vbd_size(&be->blkif->vbd)); + if (err) { + xenbus_dev_fatal(dev, err, "writing %s/sectors", dev->nodename); - goto free_be; - } - if (strlen(frontend) == 0 || !xenbus_exists(NULL, frontend, "")) { - /* If we can't get a frontend path and a frontend-id, - * then our bus-id is no longer valid and we need to - * destroy the backend device. - */ - err = -ENOENT; - goto free_be; - } - - be->dev = dev; - be->backend_watch.node = dev->nodename; - be->backend_watch.callback = backend_changed; - /* Will implicitly call backend_changed once. */ - err = register_xenbus_watch(&be->backend_watch); - if (err) { - be->backend_watch.node = NULL; - xenbus_dev_error(dev, err, - "adding backend watch on %s", + goto abort; + } + + /* FIXME: use a typename instead */ + err = xenbus_printf(xbt, dev->nodename, "info", "%u", + vbd_info(&be->blkif->vbd)); + if (err) { + xenbus_dev_fatal(dev, err, "writing %s/info", dev->nodename); - goto free_be; - } - - be->frontpath = frontend; - be->watch.node = be->frontpath; - be->watch.callback = frontend_changed; - err = register_xenbus_watch(&be->watch); - if (err) { - be->watch.node = NULL; - xenbus_dev_error(dev, err, - "adding frontend watch on %s", - be->frontpath); - goto free_be; - } - - dev->data = be; + goto abort; + } + err = xenbus_printf(xbt, dev->nodename, "sector-size", "%lu", + vbd_secsize(&be->blkif->vbd)); + if (err) { + xenbus_dev_fatal(dev, err, "writing %s/sector-size", + dev->nodename); + goto abort; + } + + err = xenbus_switch_state(dev, xbt, XenbusStateConnected); + if (err) + goto abort; + + err = xenbus_transaction_end(xbt, 0); + if (err == -EAGAIN) + goto again; + if (err) + xenbus_dev_fatal(dev, err, "ending transaction"); + return; + abort: + xenbus_transaction_end(xbt, 1); +} + + +static int connect_ring(struct backend_info *be) +{ + struct xenbus_device *dev = be->dev; + unsigned long ring_ref; + unsigned int evtchn; + int err; + + DPRINTK("%s", dev->otherend); + + err = xenbus_gather(NULL, dev->otherend, "ring-ref", "%lu", &ring_ref, + "event-channel", "%u", &evtchn, NULL); + if (err) { + xenbus_dev_fatal(dev, err, + "reading %s/ring-ref and event-channel", + dev->otherend); + return err; + } + + /* Map the shared frame, irq etc. */ + err = blkif_map(be->blkif, ring_ref, evtchn); + if (err) { + xenbus_dev_fatal(dev, err, "mapping ring-ref %lu port %u", + ring_ref, evtchn); + return err; + } + return 0; - - free_be: - if (be->backend_watch.node) - unregister_xenbus_watch(&be->backend_watch); - kfree(frontend); - kfree(be); - return err; -} +} + + +/* ** Driver Registration ** */ + static struct xenbus_device_id blkback_ids[] = { { "vbd" }, { "" } }; + static struct xenbus_driver blkback = { .name = "vbd", @@ -289,12 +342,15 @@ .ids = blkback_ids, .probe = blkback_probe, .remove = blkback_remove, + .otherend_changed = frontend_changed }; + void blkif_xenbus_init(void) { xenbus_register_backend(&blkback); } + /* * Local variables: diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c --- a/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c Fri Nov 11 20:59:05 2005 @@ -8,6 +8,7 @@ * Copyright (c) 2004, Christian Limpach * Copyright (c) 2004, Andrew Warfield * Copyright (c) 2005, Christopher Clark + * Copyright (c) 2005, XenSource Ltd * * This file may be distributed separately from the Linux kernel, or * incorporated into other software packages, subject to the following license: @@ -31,6 +32,7 @@ * IN THE SOFTWARE. */ + #if 1 #define ASSERT(p) \ if (!(p)) { printk("Assertion '%s' failed, line %d, file %s", #p , \ @@ -38,6 +40,7 @@ #else #define ASSERT(_p) #endif + #include <linux/version.h> #include "block.h" @@ -51,6 +54,7 @@ #include <asm-xen/gnttab.h> #include <asm/hypervisor.h> + #define BLKIF_STATE_DISCONNECTED 0 #define BLKIF_STATE_CONNECTED 1 @@ -58,9 +62,304 @@ (BLKIF_MAX_SEGMENTS_PER_REQUEST * BLKIF_RING_SIZE) #define GRANT_INVALID_REF 0 -static void kick_pending_request_queues(struct blkfront_info *info); - -static void blkif_completion(struct blk_shadow *s); + +static void connect(struct blkfront_info *); +static void blkfront_closing(struct xenbus_device *); +static int blkfront_remove(struct xenbus_device *); +static int talk_to_backend(struct xenbus_device *, struct blkfront_info *); +static int setup_blkring(struct xenbus_device *, struct blkfront_info *); + +static void kick_pending_request_queues(struct blkfront_info *); + +static irqreturn_t blkif_int(int irq, void *dev_id, struct pt_regs *ptregs); +static void blkif_restart_queue(void *arg); +static void blkif_recover(struct blkfront_info *); +static void blkif_completion(struct blk_shadow *); +static void blkif_free(struct blkfront_info *); + + +/** + * Entry point to this code when a new device is created. Allocate the basic + * structures and the ring buffer for communication with the backend, and + * inform the backend of the appropriate details for those. Switch to + * Initialised state. + */ +static int blkfront_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err, vdevice, i; + struct blkfront_info *info; + + /* FIXME: Use dynamic device id if this is not set. */ + err = xenbus_scanf(NULL, dev->nodename, + "virtual-device", "%i", &vdevice); + if (err != 1) { + xenbus_dev_fatal(dev, err, "reading virtual-device"); + return err; + } + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); + return -ENOMEM; + } + info->xbdev = dev; + info->vdevice = vdevice; + info->connected = BLKIF_STATE_DISCONNECTED; + info->mi = NULL; + info->gd = NULL; + INIT_WORK(&info->work, blkif_restart_queue, (void *)info); + + info->shadow_free = 0; + memset(info->shadow, 0, sizeof(info->shadow)); + for (i = 0; i < BLK_RING_SIZE; i++) + info->shadow[i].req.id = i+1; + info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff; + + /* Front end dir is a number, which is used as the id. */ + info->handle = simple_strtoul(strrchr(dev->nodename,'/')+1, NULL, 0); + dev->data = info; + + err = talk_to_backend(dev, info); + if (err) { + kfree(info); + dev->data = NULL; + return err; + } + + return 0; +} + + +/** + * We are reconnecting to the backend, due to a suspend/resume, or a backend + * driver restart. We tear down our blkif structure and recreate it, but + * leave the device-layer structures intact so that this is transparent to the + * rest of the kernel. + */ +static int blkfront_resume(struct xenbus_device *dev) +{ + struct blkfront_info *info = dev->data; + int err; + + DPRINTK("blkfront_resume: %s\n", dev->nodename); + + blkif_free(info); + + err = talk_to_backend(dev, info); + if (!err) + blkif_recover(info); + + return err; +} + + +/* Common code used when first setting up, and when resuming. */ +static int talk_to_backend(struct xenbus_device *dev, + struct blkfront_info *info) +{ + const char *message = NULL; + struct xenbus_transaction *xbt; + int err; + + /* Create shared ring, alloc event channel. */ + err = setup_blkring(dev, info); + if (err) + goto out; + +again: + xbt = xenbus_transaction_start(); + if (IS_ERR(xbt)) { + xenbus_dev_fatal(dev, err, "starting transaction"); + goto destroy_blkring; + } + + err = xenbus_printf(xbt, dev->nodename, + "ring-ref","%u", info->ring_ref); + if (err) { + message = "writing ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, dev->nodename, + "event-channel", "%u", info->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_switch_state(dev, xbt, XenbusStateInitialised); + if (err) { + goto abort_transaction; + } + + err = xenbus_transaction_end(xbt, 0); + if (err) { + if (err == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, err, "completing transaction"); + goto destroy_blkring; + } + + return 0; + + abort_transaction: + xenbus_transaction_end(xbt, 1); + if (message) + xenbus_dev_fatal(dev, err, "%s", message); + destroy_blkring: + blkif_free(info); + out: + return err; +} + + +static int setup_blkring(struct xenbus_device *dev, + struct blkfront_info *info) +{ + blkif_sring_t *sring; + int err; + + info->ring_ref = GRANT_INVALID_REF; + + sring = (blkif_sring_t *)__get_free_page(GFP_KERNEL); + if (!sring) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring"); + return -ENOMEM; + } + SHARED_RING_INIT(sring); + FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); + + err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring)); + if (err < 0) { + free_page((unsigned long)sring); + info->ring.sring = NULL; + goto fail; + } + info->ring_ref = err; + + err = xenbus_alloc_evtchn(dev, &info->evtchn); + if (err) + goto fail; + + err = bind_evtchn_to_irqhandler( + info->evtchn, blkif_int, SA_SAMPLE_RANDOM, "blkif", info); + if (err <= 0) { + xenbus_dev_fatal(dev, err, + "bind_evtchn_to_irqhandler failed"); + goto fail; + } + info->irq = err; + + return 0; +fail: + blkif_free(info); + return err; +} + + +/** + * Callback received when the backend's state changes. + */ +static void backend_changed(struct xenbus_device *dev, + XenbusState backend_state) +{ + struct blkfront_info *info = dev->data; + + DPRINTK("blkfront:backend_changed.\n"); + + switch (backend_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateClosed: + break; + + case XenbusStateConnected: + connect(info); + break; + + case XenbusStateClosing: + blkfront_closing(dev); + break; + } +} + + +/* ** Connection ** */ + + +static void connect(struct blkfront_info *info) +{ + unsigned long sectors, sector_size; + unsigned int binfo; + + if (info->connected == BLKIF_STATE_CONNECTED) + return; + + DPRINTK("blkfront.c:connect:%s.\n", info->xbdev->otherend); + + int err = xenbus_gather(NULL, info->xbdev->otherend, + "sectors", "%lu", §ors, + "info", "%u", &binfo, + "sector-size", "%lu", §or_size, + NULL); + if (err) { + xenbus_dev_fatal(info->xbdev, err, + "reading backend fields at %s", + info->xbdev->otherend); + return; + } + + info->connected = BLKIF_STATE_CONNECTED; + xlvbd_add(sectors, info->vdevice, binfo, sector_size, info); + + err = xenbus_switch_state(info->xbdev, NULL, XenbusStateConnected); + if (err) + return; + + /* Kick pending requests. */ + spin_lock_irq(&blkif_io_lock); + kick_pending_request_queues(info); + spin_unlock_irq(&blkif_io_lock); +} + + +/** + * Handle the change of state of the backend to Closing. We must delete our + * device-layer structures now, to ensure that writes are flushed through to + * the backend. Once is this done, we can switch to Closed in + * acknowledgement. + */ +static void blkfront_closing(struct xenbus_device *dev) +{ + struct blkfront_info *info = dev->data; + + DPRINTK("blkfront_closing: %s removed\n", dev->nodename); + + if (info->mi) { + DPRINTK("Calling xlvbd_del\n"); + xlvbd_del(info); + info->mi = NULL; + } + + xenbus_switch_state(dev, NULL, XenbusStateClosed); +} + + +static int blkfront_remove(struct xenbus_device *dev) +{ + DPRINTK("blkfront_remove: %s removed\n", dev->nodename); + + struct blkfront_info *info = dev->data; + + blkif_free(info); + + kfree(info); + + return 0; +} + static inline int GET_ID_FROM_FREELIST( struct blkfront_info *info) @@ -214,7 +513,7 @@ gnttab_grant_foreign_access_ref( ref, - info->backend_id, + info->xbdev->otherend_id, buffer_mfn, rq_data_dir(req) ); @@ -360,6 +659,15 @@ if (info->irq) unbind_from_irqhandler(info->irq, info); info->evtchn = info->irq = 0; + +} + +static void blkif_completion(struct blk_shadow *s) +{ + int i; + for (i = 0; i < s->req.nr_segments; i++) + gnttab_end_foreign_access( + blkif_gref_from_fas(s->req.frame_and_sects[i]), 0, 0UL); } static void blkif_recover(struct blkfront_info *info) @@ -400,7 +708,7 @@ for (j = 0; j < req->nr_segments; j++) gnttab_grant_foreign_access_ref( blkif_gref_from_fas(req->frame_and_sects[j]), - info->backend_id, + info->xbdev->otherend_id, pfn_to_mfn(info->shadow[req->id].frame[j]), rq_data_dir( (struct request *) @@ -422,21 +730,8 @@ info->connected = BLKIF_STATE_CONNECTED; } -static void blkif_connect(struct blkfront_info *info, u16 evtchn) -{ - int err = 0; - - info->evtchn = evtchn; - - err = bind_evtchn_to_irqhandler( - info->evtchn, blkif_int, SA_SAMPLE_RANDOM, "blkif", info); - if (err <= 0) { - WPRINTK("bind_evtchn_to_irqhandler failed (err=%d)\n", err); - return; - } - - info->irq = err; -} + +/* ** Driver Registration ** */ static struct xenbus_device_id blkfront_ids[] = { @@ -444,278 +739,6 @@ { "" } }; -static void watch_for_status(struct xenbus_watch *watch, - const char **vec, unsigned int len) -{ - struct blkfront_info *info; - unsigned int binfo; - unsigned long sectors, sector_size; - int err; - const char *node; - - node = vec[XS_WATCH_PATH]; - - info = container_of(watch, struct blkfront_info, watch); - node += strlen(watch->node); - - /* FIXME: clean up when error on the other end. */ - if ((info->connected == BLKIF_STATE_CONNECTED) || info->mi) - return; - - err = xenbus_gather(NULL, watch->node, - "sectors", "%lu", §ors, - "info", "%u", &binfo, - "sector-size", "%lu", §or_size, - NULL); - if (err) { - xenbus_dev_error(info->xbdev, err, - "reading backend fields at %s", watch->node); - return; - } - - info->connected = BLKIF_STATE_CONNECTED; - xlvbd_add(sectors, info->vdevice, binfo, sector_size, info); - - xenbus_dev_ok(info->xbdev); - - /* Kick pending requests. */ - spin_lock_irq(&blkif_io_lock); - kick_pending_request_queues(info); - spin_unlock_irq(&blkif_io_lock); -} - -static int setup_blkring(struct xenbus_device *dev, struct blkfront_info *info) -{ - blkif_sring_t *sring; - int err; - evtchn_op_t op = { - .cmd = EVTCHNOP_alloc_unbound, - .u.alloc_unbound.dom = DOMID_SELF, - .u.alloc_unbound.remote_dom = info->backend_id }; - - info->ring_ref = GRANT_INVALID_REF; - - sring = (void *)__get_free_page(GFP_KERNEL); - if (!sring) { - xenbus_dev_error(dev, -ENOMEM, "allocating shared ring"); - return -ENOMEM; - } - SHARED_RING_INIT(sring); - FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); - - err = gnttab_grant_foreign_access(info->backend_id, - virt_to_mfn(info->ring.sring), 0); - if (err == -ENOSPC) { - free_page((unsigned long)info->ring.sring); - info->ring.sring = 0; - xenbus_dev_error(dev, err, "granting access to ring page"); - return err; - } - info->ring_ref = err; - - err = HYPERVISOR_event_channel_op(&op); - if (err) { - gnttab_end_foreign_access(info->ring_ref, 0, - (unsigned long)info->ring.sring); - info->ring_ref = GRANT_INVALID_REF; - info->ring.sring = NULL; - xenbus_dev_error(dev, err, "allocating event channel"); - return err; - } - - blkif_connect(info, op.u.alloc_unbound.port); - - return 0; -} - -/* Common code used when first setting up, and when resuming. */ -static int talk_to_backend(struct xenbus_device *dev, - struct blkfront_info *info) -{ - char *backend; - const char *message; - struct xenbus_transaction *xbt; - int err; - - backend = NULL; - err = xenbus_gather(NULL, dev->nodename, - "backend-id", "%i", &info->backend_id, - "backend", NULL, &backend, - NULL); - if (XENBUS_EXIST_ERR(err)) - goto out; - if (backend && strlen(backend) == 0) { - err = -ENOENT; - goto out; - } - if (err < 0) { - xenbus_dev_error(dev, err, "reading %s/backend or backend-id", - dev->nodename); - goto out; - } - - /* Create shared ring, alloc event channel. */ - err = setup_blkring(dev, info); - if (err) { - xenbus_dev_error(dev, err, "setting up block ring"); - goto out; - } - -again: - xbt = xenbus_transaction_start(); - if (IS_ERR(xbt)) { - xenbus_dev_error(dev, err, "starting transaction"); - goto destroy_blkring; - } - - err = xenbus_printf(xbt, dev->nodename, - "ring-ref","%u", info->ring_ref); - if (err) { - message = "writing ring-ref"; - goto abort_transaction; - } - err = xenbus_printf(xbt, dev->nodename, - "event-channel", "%u", info->evtchn); - if (err) { - message = "writing event-channel"; - goto abort_transaction; - } - - err = xenbus_transaction_end(xbt, 0); - if (err) { - if (err == -EAGAIN) - goto again; - xenbus_dev_error(dev, err, "completing transaction"); - goto destroy_blkring; - } - - info->watch.node = backend; - info->watch.callback = watch_for_status; - err = register_xenbus_watch(&info->watch); - if (err) { - message = "registering watch on backend"; - goto destroy_blkring; - } - - info->backend = backend; - - return 0; - - abort_transaction: - xenbus_transaction_end(xbt, 1); - xenbus_dev_error(dev, err, "%s", message); - destroy_blkring: - blkif_free(info); - out: - kfree(backend); - return err; -} - -/* Setup supplies the backend dir, virtual device. - - We place an event channel and shared frame entries. - We watch backend to wait if it's ok. */ -static int blkfront_probe(struct xenbus_device *dev, - const struct xenbus_device_id *id) -{ - int err, vdevice, i; - struct blkfront_info *info; - - /* FIXME: Use dynamic device id if this is not set. */ - err = xenbus_scanf(NULL, dev->nodename, - "virtual-device", "%i", &vdevice); - if (XENBUS_EXIST_ERR(err)) - return err; - if (err < 0) { - xenbus_dev_error(dev, err, "reading virtual-device"); - return err; - } - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - xenbus_dev_error(dev, err, "allocating info structure"); - return err; - } - info->xbdev = dev; - info->vdevice = vdevice; - info->connected = BLKIF_STATE_DISCONNECTED; - info->mi = NULL; - info->gd = NULL; - INIT_WORK(&info->work, blkif_restart_queue, (void *)info); - - info->shadow_free = 0; - memset(info->shadow, 0, sizeof(info->shadow)); - for (i = 0; i < BLK_RING_SIZE; i++) - info->shadow[i].req.id = i+1; - info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff; - - /* Front end dir is a number, which is used as the id. */ - info->handle = simple_strtoul(strrchr(dev->nodename,'/')+1, NULL, 0); - dev->data = info; - - err = talk_to_backend(dev, info); - if (err) { - kfree(info); - dev->data = NULL; - return err; - } - - { - unsigned int len = max(XS_WATCH_PATH, XS_WATCH_TOKEN) + 1; - const char *vec[len]; - - vec[XS_WATCH_PATH] = info->watch.node; - vec[XS_WATCH_TOKEN] = NULL; - - /* Call once in case entries already there. */ - watch_for_status(&info->watch, vec, len); - } - - return 0; -} - -static int blkfront_remove(struct xenbus_device *dev) -{ - struct blkfront_info *info = dev->data; - - if (info->backend) - unregister_xenbus_watch(&info->watch); - - if (info->mi) - xlvbd_del(info); - - blkif_free(info); - - kfree(info->backend); - kfree(info); - - return 0; -} - -static int blkfront_suspend(struct xenbus_device *dev) -{ - struct blkfront_info *info = dev->data; - - unregister_xenbus_watch(&info->watch); - kfree(info->backend); - info->backend = NULL; - - return 0; -} - -static int blkfront_resume(struct xenbus_device *dev) -{ - struct blkfront_info *info = dev->data; - int err; - - blkif_free(info); - - err = talk_to_backend(dev, info); - if (!err) - blkif_recover(info); - - return err; -} static struct xenbus_driver blkfront = { .name = "vbd", @@ -724,27 +747,28 @@ .probe = blkfront_probe, .remove = blkfront_remove, .resume = blkfront_resume, - .suspend = blkfront_suspend, + .otherend_changed = backend_changed, }; + static int __init xlblk_init(void) { if (xen_init() < 0) return -ENODEV; - xenbus_register_driver(&blkfront); - return 0; -} - + return xenbus_register_frontend(&blkfront); +} module_init(xlblk_init); -static void blkif_completion(struct blk_shadow *s) -{ - int i; - for (i = 0; i < s->req.nr_segments; i++) - gnttab_end_foreign_access( - blkif_gref_from_fas(s->req.frame_and_sects[i]), 0, 0UL); -} + +static void xlblk_exit(void) +{ + return xenbus_unregister_driver(&blkfront); +} +module_exit(xlblk_exit); + +MODULE_LICENSE("BSD"); + /* * Local variables: diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/blkfront/block.h --- a/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h Fri Nov 11 20:59:05 2005 @@ -113,15 +113,11 @@ struct blkfront_info { struct xenbus_device *xbdev; - /* We watch the backend */ - struct xenbus_watch watch; dev_t dev; struct gendisk *gd; int vdevice; blkif_vdev_t handle; int connected; - char *backend; - int backend_id; int ring_ref; blkif_front_ring_t ring; unsigned int evtchn, irq; diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c --- a/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c Fri Nov 11 20:59:05 2005 @@ -1,5 +1,6 @@ /* Xenbus code for netif backend Copyright (C) 2005 Rusty Russell <rusty@xxxxxxxxxxxxxxx> + Copyright (C) 2005 XenSource Ltd This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,148 +16,102 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + #include <stdarg.h> #include <linux/module.h> #include <asm-xen/xenbus.h> +#include <asm-xen/net_driver_util.h> #include "common.h" + +#if 0 +#undef DPRINTK +#define DPRINTK(fmt, args...) \ + printk("netback/xenbus (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args) +#endif + + struct backend_info { struct xenbus_device *dev; - - /* our communications channel */ netif_t *netif; - - long int frontend_id; - - /* watch back end for changes */ struct xenbus_watch backend_watch; - - /* watch front end for changes */ - struct xenbus_watch watch; - char *frontpath; + XenbusState frontend_state; }; + +static int connect_rings(struct backend_info *); +static void connect(struct backend_info *); +static void maybe_connect(struct backend_info *); +static void backend_changed(struct xenbus_watch *, const char **, + unsigned int); + + static int netback_remove(struct xenbus_device *dev) { struct backend_info *be = dev->data; - if (be->watch.node) - unregister_xenbus_watch(&be->watch); - unregister_xenbus_watch(&be->backend_watch); - if (be->netif) + if (be->backend_watch.node) { + unregister_xenbus_watch(&be->backend_watch); + kfree(be->backend_watch.node); + be->backend_watch.node = NULL; + } + if (be->netif) { netif_disconnect(be->netif); - kfree(be->frontpath); + be->netif = NULL; + } kfree(be); + dev->data = NULL; return 0; } -/* Front end tells us frame. */ -static void frontend_changed(struct xenbus_watch *watch, - const char **vec, unsigned int len) -{ - unsigned long tx_ring_ref, rx_ring_ref; - unsigned int evtchn; + +/** + * Entry point to this code when a new device is created. Allocate the basic + * structures, and watch the store waiting for the hotplug scripts to tell us + * the device's handle. Switch to InitWait. + */ +static int netback_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ int err; - struct backend_info *be - = container_of(watch, struct backend_info, watch); - char *mac, *e, *s; - int i; - - /* If other end is gone, delete ourself. */ - if (vec && !xenbus_exists(NULL, be->frontpath, "")) { - xenbus_rm(NULL, be->dev->nodename, ""); - device_unregister(&be->dev->dev); - return; - } - if (be->netif == NULL || be->netif->status == CONNECTED) - return; - - mac = xenbus_read(NULL, be->frontpath, "mac", NULL); - if (IS_ERR(mac)) { - err = PTR_ERR(mac); - xenbus_dev_error(be->dev, err, "reading %s/mac", - be->dev->nodename); - return; - } - s = mac; - for (i = 0; i < ETH_ALEN; i++) { - be->netif->fe_dev_addr[i] = simple_strtoul(s, &e, 16); - if (s == e || (e[0] != ':' && e[0] != 0)) { - kfree(mac); - err = -ENOENT; - xenbus_dev_error(be->dev, err, "parsing %s/mac", - be->dev->nodename); - return; - } - s = &e[1]; - } - kfree(mac); - - err = xenbus_gather(NULL, be->frontpath, - "tx-ring-ref", "%lu", &tx_ring_ref, - "rx-ring-ref", "%lu", &rx_ring_ref, - "event-channel", "%u", &evtchn, NULL); + struct backend_info *be = kmalloc(sizeof(struct backend_info), + GFP_KERNEL); + if (!be) { + xenbus_dev_fatal(dev, -ENOMEM, + "allocating backend structure"); + return -ENOMEM; + } + memset(be, 0, sizeof(*be)); + + be->dev = dev; + dev->data = be; + + err = xenbus_watch_path2(dev, dev->nodename, "handle", + &be->backend_watch, backend_changed); + if (err) + goto fail; + + err = xenbus_switch_state(dev, NULL, XenbusStateInitWait); if (err) { - xenbus_dev_error(be->dev, err, - "reading %s/ring-ref and event-channel", - be->frontpath); - return; - } - - /* Map the shared frame, irq etc. */ - err = netif_map(be->netif, tx_ring_ref, rx_ring_ref, evtchn); - if (err) { - xenbus_dev_error(be->dev, err, - "mapping shared-frames %lu/%lu port %u", - tx_ring_ref, rx_ring_ref, evtchn); - return; - } - - xenbus_dev_ok(be->dev); - - return; -} - -/* - Setup supplies physical device. - We provide event channel and device details to front end. - Frontend supplies shared frame and event channel. - */ -static void backend_changed(struct xenbus_watch *watch, - const char **vec, unsigned int len) -{ - int err; - long int handle; - struct backend_info *be - = container_of(watch, struct backend_info, backend_watch); - struct xenbus_device *dev = be->dev; - u8 be_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; - - err = xenbus_scanf(NULL, dev->nodename, "handle", "%li", &handle); - if (XENBUS_EXIST_ERR(err)) - return; - if (err < 0) { - xenbus_dev_error(dev, err, "reading handle"); - return; - } - - if (be->netif == NULL) { - be->netif = alloc_netif(be->frontend_id, handle, be_mac); - if (IS_ERR(be->netif)) { - err = PTR_ERR(be->netif); - be->netif = NULL; - xenbus_dev_error(dev, err, "creating interface"); - return; - } - - kobject_hotplug(&dev->dev.kobj, KOBJ_ONLINE); - - /* Pass in NULL node to skip exist test. */ - frontend_changed(&be->watch, NULL, 0); - } -} - + goto fail; + } + + return 0; + +fail: + DPRINTK("failed"); + netback_remove(dev); + return err; +} + + +/** + * Handle the creation of the hotplug script environment. We add the script + * and vif variables to the environment, for the benefit of the vif-* hotplug + * scripts. + */ static int netback_hotplug(struct xenbus_device *xdev, char **envp, int num_envp, char *buffer, int buffer_size) { @@ -164,10 +119,12 @@ netif_t *netif = be->netif; int i = 0, length = 0; + DPRINTK("netback_hotplug"); + char *val = xenbus_read(NULL, xdev->nodename, "script", NULL); if (IS_ERR(val)) { int err = PTR_ERR(val); - xenbus_dev_error(xdev, err, "reading script"); + xenbus_dev_fatal(xdev, err, "reading script"); return err; } else { @@ -186,81 +143,161 @@ return 0; } -static int netback_probe(struct xenbus_device *dev, - const struct xenbus_device_id *id) -{ - struct backend_info *be; - char *frontend; + +/** + * Callback received when the hotplug scripts have placed the handle node. + * Read it, and create a netif structure. If the frontend is ready, connect. + */ +static void backend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ int err; - - be = kmalloc(sizeof(*be), GFP_KERNEL); - if (!be) { - xenbus_dev_error(dev, -ENOMEM, "allocating backend structure"); - return -ENOMEM; - } - memset(be, 0, sizeof(*be)); - - frontend = NULL; - err = xenbus_gather(NULL, dev->nodename, - "frontend-id", "%li", &be->frontend_id, - "frontend", NULL, &frontend, - NULL); - if (XENBUS_EXIST_ERR(err)) - goto free_be; - if (err < 0) { - xenbus_dev_error(dev, err, - "reading %s/frontend or frontend-id", - dev->nodename); - goto free_be; - } - if (strlen(frontend) == 0 || !xenbus_exists(NULL, frontend, "")) { - /* If we can't get a frontend path and a frontend-id, - * then our bus-id is no longer valid and we need to - * destroy the backend device. - */ - err = -ENOENT; - goto free_be; - } - - be->dev = dev; - be->backend_watch.node = dev->nodename; - be->backend_watch.callback = backend_changed; - /* Registration implicitly calls backend_changed. */ - err = register_xenbus_watch(&be->backend_watch); + long handle; + struct backend_info *be + = container_of(watch, struct backend_info, backend_watch); + struct xenbus_device *dev = be->dev; + + DPRINTK(""); + + err = xenbus_scanf(NULL, dev->nodename, "handle", "%li", &handle); + if (XENBUS_EXIST_ERR(err)) { + /* Since this watch will fire once immediately after it is + registered, we expect this. Ignore it, and wait for the + hotplug scripts. */ + return; + } + if (err != 1) { + xenbus_dev_fatal(dev, err, "reading handle"); + return; + } + + if (be->netif == NULL) { + u8 be_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + + be->netif = alloc_netif(dev->otherend_id, handle, be_mac); + if (IS_ERR(be->netif)) { + err = PTR_ERR(be->netif); + be->netif = NULL; + xenbus_dev_fatal(dev, err, "creating interface"); + return; + } + + kobject_hotplug(&dev->dev.kobj, KOBJ_ONLINE); + + maybe_connect(be); + } +} + + +/** + * Callback received when the frontend's state changes. + */ +static void frontend_changed(struct xenbus_device *dev, + XenbusState frontend_state) +{ + struct backend_info *be = dev->data; + + DPRINTK(""); + + be->frontend_state = frontend_state; + + switch (frontend_state) { + case XenbusStateInitialising: + case XenbusStateInitialised: + break; + + case XenbusStateConnected: + maybe_connect(be); + break; + + case XenbusStateClosing: + xenbus_switch_state(dev, NULL, XenbusStateClosing); + break; + + case XenbusStateClosed: + device_unregister(&be->dev->dev); + break; + + case XenbusStateUnknown: + case XenbusStateInitWait: + default: + xenbus_dev_fatal(be->dev, -EINVAL, "saw state %d at frontend", + frontend_state); + break; + } +} + + +/* ** Connection ** */ + + +static void maybe_connect(struct backend_info *be) +{ + if (be->netif != NULL && be->frontend_state == XenbusStateConnected) { + connect(be); + } +} + + +static void connect(struct backend_info *be) +{ + int err; + struct xenbus_device *dev = be->dev; + + err = connect_rings(be); + if (err) + return; + + err = xen_net_read_mac(dev, be->netif->fe_dev_addr); if (err) { - be->backend_watch.node = NULL; - xenbus_dev_error(dev, err, "adding backend watch on %s", - dev->nodename); - goto free_be; - } - - be->frontpath = frontend; - be->watch.node = be->frontpath; - be->watch.callback = frontend_changed; - err = register_xenbus_watch(&be->watch); + xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); + return; + } + + xenbus_switch_state(dev, NULL, XenbusStateConnected); +} + + +static int connect_rings(struct backend_info *be) +{ + struct xenbus_device *dev = be->dev; + unsigned long tx_ring_ref, rx_ring_ref; + unsigned int evtchn; + int err; + + DPRINTK(""); + + err = xenbus_gather(NULL, dev->otherend, + "tx-ring-ref", "%lu", &tx_ring_ref, + "rx-ring-ref", "%lu", &rx_ring_ref, + "event-channel", "%u", &evtchn, NULL); if (err) { - be->watch.node = NULL; - xenbus_dev_error(dev, err, - "adding frontend watch on %s", - be->frontpath); - goto free_be; - } - - dev->data = be; + xenbus_dev_fatal(dev, err, + "reading %s/ring-ref and event-channel", + dev->otherend); + return err; + } + + /* Map the shared frame, irq etc. */ + err = netif_map(be->netif, tx_ring_ref, rx_ring_ref, evtchn); + if (err) { + xenbus_dev_fatal(dev, err, + "mapping shared-frames %lu/%lu port %u", + tx_ring_ref, rx_ring_ref, evtchn); + return err; + } return 0; - - free_be: - if (be->backend_watch.node) - unregister_xenbus_watch(&be->backend_watch); - kfree(frontend); - kfree(be); - return err; -} +} + + +/* ** Driver Registration ** */ + static struct xenbus_device_id netback_ids[] = { { "vif" }, { "" } }; + static struct xenbus_driver netback = { .name = "vif", @@ -269,12 +306,15 @@ .probe = netback_probe, .remove = netback_remove, .hotplug = netback_hotplug, + .otherend_changed = frontend_changed, }; + void netif_xenbus_init(void) { xenbus_register_backend(&netback); } + /* * Local variables: diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c --- a/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c Fri Nov 11 20:59:05 2005 @@ -2,6 +2,7 @@ * Virtual network driver for conversing with remote driver backends. * * Copyright (c) 2002-2005, K A Fraser + * Copyright (c) 2005, XenSource Ltd * * This file may be distributed separately from the Linux kernel, or * incorporated into other software packages, subject to the following license: @@ -56,6 +57,7 @@ #include <asm/uaccess.h> #include <asm-xen/xen-public/grant_table.h> #include <asm-xen/gnttab.h> +#include <asm-xen/net_driver_util.h> #define GRANT_INVALID_REF 0 @@ -87,13 +89,111 @@ #define TX_TEST_IDX req_cons /* conservative: not seen all our requests? */ #endif - -static void network_tx_buf_gc(struct net_device *dev); -static void network_alloc_rx_buffers(struct net_device *dev); - static unsigned long rx_pfn_array[NETIF_RX_RING_SIZE]; static multicall_entry_t rx_mcl[NETIF_RX_RING_SIZE+1]; static mmu_update_t rx_mmu[NETIF_RX_RING_SIZE]; + +struct netfront_info +{ + struct list_head list; + struct net_device *netdev; + + struct net_device_stats stats; + NETIF_RING_IDX rx_resp_cons, tx_resp_cons; + unsigned int tx_full; + + netif_tx_interface_t *tx; + netif_rx_interface_t *rx; + + spinlock_t tx_lock; + spinlock_t rx_lock; + + unsigned int handle; + unsigned int evtchn, irq; + + /* What is the status of our connection to the remote backend? */ +#define BEST_CLOSED 0 +#define BEST_DISCONNECTED 1 +#define BEST_CONNECTED 2 + unsigned int backend_state; + + /* Is this interface open or closed (down or up)? */ +#define UST_CLOSED 0 +#define UST_OPEN 1 + unsigned int user_state; + + /* Receive-ring batched refills. */ +#define RX_MIN_TARGET 8 +#define RX_MAX_TARGET NETIF_RX_RING_SIZE + int rx_min_target, rx_max_target, rx_target; + struct sk_buff_head rx_batch; + + /* + * {tx,rx}_skbs store outstanding skbuffs. The first entry in each + * array is an index into a chain of free entries. + */ + struct sk_buff *tx_skbs[NETIF_TX_RING_SIZE+1]; + struct sk_buff *rx_skbs[NETIF_RX_RING_SIZE+1]; + + grant_ref_t gref_tx_head; + grant_ref_t grant_tx_ref[NETIF_TX_RING_SIZE + 1]; + grant_ref_t gref_rx_head; + grant_ref_t grant_rx_ref[NETIF_TX_RING_SIZE + 1]; + + struct xenbus_device *xbdev; + int tx_ring_ref; + int rx_ring_ref; + u8 mac[ETH_ALEN]; +}; + +/* Access macros for acquiring freeing slots in {tx,rx}_skbs[]. */ +#define ADD_ID_TO_FREELIST(_list, _id) \ + (_list)[(_id)] = (_list)[0]; \ + (_list)[0] = (void *)(unsigned long)(_id); +#define GET_ID_FROM_FREELIST(_list) \ + ({ unsigned long _id = (unsigned long)(_list)[0]; \ + (_list)[0] = (_list)[_id]; \ + (unsigned short)_id; }) + +#ifdef DEBUG +static char *be_state_name[] = { + [BEST_CLOSED] = "closed", + [BEST_DISCONNECTED] = "disconnected", + [BEST_CONNECTED] = "connected", +}; +#endif + +#ifdef DEBUG +#define DPRINTK(fmt, args...) \ + printk(KERN_ALERT "netfront (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args) +#else +#define DPRINTK(fmt, args...) ((void)0) +#endif +#define IPRINTK(fmt, args...) \ + printk(KERN_INFO "netfront: " fmt, ##args) +#define WPRINTK(fmt, args...) \ + printk(KERN_WARNING "netfront: " fmt, ##args) + + +static int talk_to_backend(struct xenbus_device *, struct netfront_info *); +static int setup_device(struct xenbus_device *, struct netfront_info *); +static int create_netdev(int, struct xenbus_device *, struct net_device **); + +static void netfront_closing(struct xenbus_device *); + +static void end_access(int, void *); +static void netif_disconnect_backend(struct netfront_info *); +static void close_netdev(struct netfront_info *); +static void netif_free(struct netfront_info *); + +static void show_device(struct netfront_info *); + +static void network_connect(struct net_device *); +static void network_tx_buf_gc(struct net_device *); +static void network_alloc_rx_buffers(struct net_device *); +static int send_fake_arp(struct net_device *); + +static irqreturn_t netif_int(int irq, void *dev_id, struct pt_regs *ptregs); #ifdef CONFIG_PROC_FS static int xennet_proc_init(void); @@ -105,92 +205,216 @@ #define xennet_proc_delif(d) ((void)0) #endif -#define netfront_info net_private -struct net_private -{ - struct list_head list; + +/** + * Entry point to this code when a new device is created. Allocate the basic + * structures and the ring buffers for communication with the backend, and + * inform the backend of the appropriate details for those. Switch to + * Connected state. + */ +static int netfront_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err; struct net_device *netdev; - - struct net_device_stats stats; - NETIF_RING_IDX rx_resp_cons, tx_resp_cons; - unsigned int tx_full; - - netif_tx_interface_t *tx; - netif_rx_interface_t *rx; - - spinlock_t tx_lock; - spinlock_t rx_lock; - + struct netfront_info *info; unsigned int handle; - unsigned int evtchn, irq; - - /* What is the status of our connection to the remote backend? */ -#define BEST_CLOSED 0 -#define BEST_DISCONNECTED 1 -#define BEST_CONNECTED 2 - unsigned int backend_state; - - /* Is this interface open or closed (down or up)? */ -#define UST_CLOSED 0 -#define UST_OPEN 1 - unsigned int user_state; - - /* Receive-ring batched refills. */ -#define RX_MIN_TARGET 8 -#define RX_MAX_TARGET NETIF_RX_RING_SIZE - int rx_min_target, rx_max_target, rx_target; - struct sk_buff_head rx_batch; - - /* - * {tx,rx}_skbs store outstanding skbuffs. The first entry in each - * array is an index into a chain of free entries. - */ - struct sk_buff *tx_skbs[NETIF_TX_RING_SIZE+1]; - struct sk_buff *rx_skbs[NETIF_RX_RING_SIZE+1]; - - grant_ref_t gref_tx_head; - grant_ref_t grant_tx_ref[NETIF_TX_RING_SIZE + 1]; - grant_ref_t gref_rx_head; - grant_ref_t grant_rx_ref[NETIF_TX_RING_SIZE + 1]; - - struct xenbus_device *xbdev; - char *backend; - int backend_id; - struct xenbus_watch watch; - int tx_ring_ref; - int rx_ring_ref; - u8 mac[ETH_ALEN]; -}; - -/* Access macros for acquiring freeing slots in {tx,rx}_skbs[]. */ -#define ADD_ID_TO_FREELIST(_list, _id) \ - (_list)[(_id)] = (_list)[0]; \ - (_list)[0] = (void *)(unsigned long)(_id); -#define GET_ID_FROM_FREELIST(_list) \ - ({ unsigned long _id = (unsigned long)(_list)[0]; \ - (_list)[0] = (_list)[_id]; \ - (unsigned short)_id; }) - -#ifdef DEBUG -static char *be_state_name[] = { - [BEST_CLOSED] = "closed", - [BEST_DISCONNECTED] = "disconnected", - [BEST_CONNECTED] = "connected", -}; -#endif - -#ifdef DEBUG -#define DPRINTK(fmt, args...) \ - printk(KERN_ALERT "xen_net (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args) -#else -#define DPRINTK(fmt, args...) ((void)0) -#endif -#define IPRINTK(fmt, args...) \ - printk(KERN_INFO "xen_net: " fmt, ##args) -#define WPRINTK(fmt, args...) \ - printk(KERN_WARNING "xen_net: " fmt, ##args) - -static void netif_free(struct netfront_info *info); + + err = xenbus_scanf(NULL, dev->nodename, "handle", "%u", &handle); + if (err != 1) { + xenbus_dev_fatal(dev, err, "reading handle"); + return err; + } + + err = create_netdev(handle, dev, &netdev); + if (err) { + xenbus_dev_fatal(dev, err, "creating netdev"); + return err; + } + + info = netdev_priv(netdev); + dev->data = info; + + err = talk_to_backend(dev, info); + if (err) { + kfree(info); + dev->data = NULL; + return err; + } + + return 0; +} + + +/** + * We are reconnecting to the backend, due to a suspend/resume, or a backend + * driver restart. We tear down our netif structure and recreate it, but + * leave the device-layer structures intact so that this is transparent to the + * rest of the kernel. + */ +static int netfront_resume(struct xenbus_device *dev) +{ + struct netfront_info *info = dev->data; + + DPRINTK("%s\n", dev->nodename); + + netif_disconnect_backend(info); + return talk_to_backend(dev, info); +} + + +/* Common code used when first setting up, and when resuming. */ +static int talk_to_backend(struct xenbus_device *dev, + struct netfront_info *info) +{ + const char *message; + struct xenbus_transaction *xbt; + int err; + + err = xen_net_read_mac(dev, info->mac); + if (err) { + xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); + goto out; + } + + /* Create shared ring, alloc event channel. */ + err = setup_device(dev, info); + if (err) + goto out; + +again: + xbt = xenbus_transaction_start(); + if (IS_ERR(xbt)) { + xenbus_dev_fatal(dev, err, "starting transaction"); + goto destroy_ring; + } + + err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref","%u", + info->tx_ring_ref); + if (err) { + message = "writing tx ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref","%u", + info->rx_ring_ref); + if (err) { + message = "writing rx ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, dev->nodename, + "event-channel", "%u", info->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, + "state", "%d", XenbusStateConnected); + if (err) { + message = "writing frontend XenbusStateConnected"; + goto abort_transaction; + } + + err = xenbus_transaction_end(xbt, 0); + if (err) { + if (err == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, err, "completing transaction"); + goto destroy_ring; + } + + return 0; + + abort_transaction: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, err, "%s", message); + destroy_ring: + netif_free(info); + out: + return err; +} + + +static int setup_device(struct xenbus_device *dev, struct netfront_info *info) +{ + int err; + struct net_device *netdev = info->netdev; + + info->tx_ring_ref = GRANT_INVALID_REF; + info->rx_ring_ref = GRANT_INVALID_REF; + info->rx = NULL; + info->tx = NULL; + info->irq = 0; + + info->tx = (netif_tx_interface_t *)__get_free_page(GFP_KERNEL); + if (!info->tx) { + err = -ENOMEM; + xenbus_dev_fatal(dev, err, "allocating tx ring page"); + goto fail; + } + info->rx = (netif_rx_interface_t *)__get_free_page(GFP_KERNEL); + if (!info->rx) { + err = -ENOMEM; + xenbus_dev_fatal(dev, err, "allocating rx ring page"); + goto fail; + } + memset(info->tx, 0, PAGE_SIZE); + memset(info->rx, 0, PAGE_SIZE); + info->backend_state = BEST_DISCONNECTED; + + err = xenbus_grant_ring(dev, virt_to_mfn(info->tx)); + if (err < 0) + goto fail; + info->tx_ring_ref = err; + + err = xenbus_grant_ring(dev, virt_to_mfn(info->rx)); + if (err < 0) + goto fail; + info->rx_ring_ref = err; + + err = xenbus_alloc_evtchn(dev, &info->evtchn); + if (err) + goto fail; + + memcpy(netdev->dev_addr, info->mac, ETH_ALEN); + network_connect(netdev); + info->irq = bind_evtchn_to_irqhandler( + info->evtchn, netif_int, SA_SAMPLE_RANDOM, netdev->name, + netdev); + (void)send_fake_arp(netdev); + show_device(info); + + return 0; + + fail: + netif_free(info); + return err; +} + + +/** + * Callback received when the backend's state changes. + */ +static void backend_changed(struct xenbus_device *dev, + XenbusState backend_state) +{ + DPRINTK("\n"); + + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateConnected: + case XenbusStateUnknown: + case XenbusStateClosed: + break; + + case XenbusStateClosing: + netfront_closing(dev); + break; + } +} + /** Send a packet on a net device to encourage switches to learn the * MAC. We send a fake ARP request. @@ -220,9 +444,10 @@ return dev_queue_xmit(skb); } + static int network_open(struct net_device *dev) { - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); memset(&np->stats, 0, sizeof(np->stats)); @@ -240,7 +465,7 @@ { NETIF_RING_IDX i, prod; unsigned short id; - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); struct sk_buff *skb; if (np->backend_state != BEST_CONNECTED) @@ -295,7 +520,7 @@ static void network_alloc_rx_buffers(struct net_device *dev) { unsigned short id; - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); struct sk_buff *skb; int i, batch_target; NETIF_RING_IDX req_prod = np->rx->req_prod; @@ -337,7 +562,8 @@ ref = gnttab_claim_grant_reference(&np->gref_rx_head); BUG_ON((signed short)ref < 0); np->grant_rx_ref[id] = ref; - gnttab_grant_foreign_transfer_ref(ref, np->backend_id); + gnttab_grant_foreign_transfer_ref(ref, + np->xbdev->otherend_id); np->rx->ring[MASK_NETIF_RX_IDX(req_prod + i)].req.gref = ref; rx_pfn_array[i] = virt_to_mfn(skb->head); @@ -385,7 +611,7 @@ static int network_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned short id; - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); netif_tx_request_t *tx; NETIF_RING_IDX i; grant_ref_t ref; @@ -429,7 +655,7 @@ BUG_ON((signed short)ref < 0); mfn = virt_to_mfn(skb->data); gnttab_grant_foreign_access_ref( - ref, np->backend_id, mfn, GNTMAP_readonly); + ref, np->xbdev->otherend_id, mfn, GNTMAP_readonly); tx->gref = np->grant_tx_ref[id] = ref; tx->offset = (unsigned long)skb->data & ~PAGE_MASK; tx->size = skb->len; @@ -466,7 +692,7 @@ static irqreturn_t netif_int(int irq, void *dev_id, struct pt_regs *ptregs) { struct net_device *dev = dev_id; - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); unsigned long flags; spin_lock_irqsave(&np->tx_lock, flags); @@ -483,7 +709,7 @@ static int netif_poll(struct net_device *dev, int *pbudget) { - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); struct sk_buff *skb, *nskb; netif_rx_response_t *rx; NETIF_RING_IDX i, rp; @@ -534,7 +760,7 @@ if(ref == GRANT_INVALID_REF) { printk(KERN_WARNING "Bad rx grant reference %d " "from dom %d.\n", - ref, np->backend_id); + ref, np->xbdev->otherend_id); np->rx->ring[MASK_NETIF_RX_IDX(np->rx->req_prod)]. req.id = rx->id; wmb(); @@ -678,7 +904,7 @@ static int network_close(struct net_device *dev) { - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); np->user_state = UST_CLOSED; netif_stop_queue(np->netdev); return 0; @@ -687,13 +913,13 @@ static struct net_device_stats *network_get_stats(struct net_device *dev) { - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); return &np->stats; } static void network_connect(struct net_device *dev) { - struct net_private *np; + struct netfront_info *np; int i, requeue_idx; netif_tx_request_t *tx; struct sk_buff *skb; @@ -736,7 +962,7 @@ tx->id = i; gnttab_grant_foreign_access_ref( - np->grant_tx_ref[i], np->backend_id, + np->grant_tx_ref[i], np->xbdev->otherend_id, virt_to_mfn(np->tx_skbs[i]->data), GNTMAP_readonly); tx->gref = np->grant_tx_ref[i]; @@ -755,7 +981,7 @@ if ((unsigned long)np->rx_skbs[i] < __PAGE_OFFSET) continue; gnttab_grant_foreign_transfer_ref( - np->grant_rx_ref[i], np->backend_id); + np->grant_rx_ref[i], np->xbdev->otherend_id); np->rx->ring[requeue_idx].req.gref = np->grant_rx_ref[i]; np->rx->ring[requeue_idx].req.id = i; @@ -782,7 +1008,7 @@ spin_unlock_irq(&np->tx_lock); } -static void show_device(struct net_private *np) +static void show_device(struct netfront_info *np) { #ifdef DEBUG if (np) { @@ -799,27 +1025,9 @@ #endif } -/* - * Move the vif into connected state. - * Sets the mac and event channel from the message. - * Binds the irq to the event channel. - */ -static void -connect_device(struct net_private *np, unsigned int evtchn) -{ - struct net_device *dev = np->netdev; - memcpy(dev->dev_addr, np->mac, ETH_ALEN); - np->evtchn = evtchn; - network_connect(dev); - np->irq = bind_evtchn_to_irqhandler( - np->evtchn, netif_int, SA_SAMPLE_RANDOM, dev->name, dev); - (void)send_fake_arp(dev); - show_device(np); -} - static void netif_uninit(struct net_device *dev) { - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); gnttab_free_grant_references(np->gref_tx_head); gnttab_free_grant_references(np->gref_rx_head); } @@ -840,9 +1048,9 @@ { int i, err = 0; struct net_device *netdev = NULL; - struct net_private *np = NULL; - - if ((netdev = alloc_etherdev(sizeof(struct net_private))) == NULL) { + struct netfront_info *np = NULL; + + if ((netdev = alloc_etherdev(sizeof(struct netfront_info))) == NULL) { printk(KERN_WARNING "%s> alloc_etherdev failed.\n", __FUNCTION__); err = -ENOMEM; @@ -929,15 +1137,6 @@ goto exit; } -static int destroy_netdev(struct net_device *netdev) -{ -#ifdef CONFIG_PROC_FS - xennet_proc_delif(netdev); -#endif - unregister_netdev(netdev); - return 0; -} - /* * We use this notifier to send out a fake ARP reply to reset switches and * router ARP caches when an IP interface is brought up on a VIF. @@ -955,91 +1154,70 @@ return NOTIFY_DONE; } -static struct notifier_block notifier_inetdev = { - .notifier_call = inetdev_notify, - .next = NULL, - .priority = 0 -}; - -static struct xenbus_device_id netfront_ids[] = { - { "vif" }, - { "" } -}; - -static void watch_for_status(struct xenbus_watch *watch, - const char **vec, unsigned int len) -{ -} - -static int setup_device(struct xenbus_device *dev, struct netfront_info *info) -{ - int err; - evtchn_op_t op = { - .cmd = EVTCHNOP_alloc_unbound, - .u.alloc_unbound.dom = DOMID_SELF, - .u.alloc_unbound.remote_dom = info->backend_id }; - - info->tx_ring_ref = GRANT_INVALID_REF; - info->rx_ring_ref = GRANT_INVALID_REF; - info->rx = NULL; - info->tx = NULL; - info->irq = 0; - - info->tx = (netif_tx_interface_t *)__get_free_page(GFP_KERNEL); - if (info->tx == 0) { - err = -ENOMEM; - xenbus_dev_error(dev, err, "allocating tx ring page"); - goto out; - } - info->rx = (netif_rx_interface_t *)__get_free_page(GFP_KERNEL); - if (info->rx == 0) { - err = -ENOMEM; - xenbus_dev_error(dev, err, "allocating rx ring page"); - goto out; - } - memset(info->tx, 0, PAGE_SIZE); - memset(info->rx, 0, PAGE_SIZE); - info->backend_state = BEST_DISCONNECTED; - - err = gnttab_grant_foreign_access(info->backend_id, - virt_to_mfn(info->tx), 0); - if (err < 0) { - xenbus_dev_error(dev, err, "granting access to tx ring page"); - goto out; - } - info->tx_ring_ref = err; - - err = gnttab_grant_foreign_access(info->backend_id, - virt_to_mfn(info->rx), 0); - if (err < 0) { - xenbus_dev_error(dev, err, "granting access to rx ring page"); - goto out; - } - info->rx_ring_ref = err; - - err = HYPERVISOR_event_channel_op(&op); - if (err) { - xenbus_dev_error(dev, err, "allocating event channel"); - goto out; - } - - connect_device(info, op.u.alloc_unbound.port); + +/* ** Close down ** */ + + +/** + * Handle the change of state of the backend to Closing. We must delete our + * device-layer structures now, to ensure that writes are flushed through to + * the backend. Once is this done, we can switch to Closed in + * acknowledgement. + */ +static void netfront_closing(struct xenbus_device *dev) +{ + struct netfront_info *info = dev->data; + + DPRINTK("netfront_closing: %s removed\n", dev->nodename); + + close_netdev(info); + + xenbus_switch_state(dev, NULL, XenbusStateClosed); +} + + +static int netfront_remove(struct xenbus_device *dev) +{ + struct netfront_info *info = dev->data; + + DPRINTK("%s\n", dev->nodename); + + netif_free(info); + kfree(info); return 0; - - out: - netif_free(info); - return err; -} - -static void end_access(int ref, void *page) -{ - if (ref != GRANT_INVALID_REF) - gnttab_end_foreign_access(ref, 0, (unsigned long)page); -} +} + static void netif_free(struct netfront_info *info) { + netif_disconnect_backend(info); + close_netdev(info); +} + + +static void close_netdev(struct netfront_info *info) +{ + if (info->netdev) { +#ifdef CONFIG_PROC_FS + xennet_proc_delif(info->netdev); +#endif + unregister_netdev(info->netdev); + info->netdev = NULL; + } +} + + +static void netif_disconnect_backend(struct netfront_info *info) +{ + /* Stop old i/f to prevent errors whilst we rebuild the state. */ + spin_lock_irq(&info->tx_lock); + spin_lock(&info->rx_lock); + netif_stop_queue(info->netdev); + /* info->backend_state = BEST_DISCONNECTED; */ + spin_unlock(&info->rx_lock); + spin_unlock_irq(&info->tx_lock); + end_access(info->tx_ring_ref, info->tx); end_access(info->rx_ring_ref, info->rx); info->tx_ring_ref = GRANT_INVALID_REF; @@ -1052,202 +1230,22 @@ info->evtchn = info->irq = 0; } -/* Stop network device and free tx/rx queues and irq. */ -static void shutdown_device(struct net_private *np) -{ - /* Stop old i/f to prevent errors whilst we rebuild the state. */ - spin_lock_irq(&np->tx_lock); - spin_lock(&np->rx_lock); - netif_stop_queue(np->netdev); - /* np->backend_state = BEST_DISCONNECTED; */ - spin_unlock(&np->rx_lock); - spin_unlock_irq(&np->tx_lock); - - /* Free resources. */ - netif_free(np); -} - -/* Common code used when first setting up, and when resuming. */ -static int talk_to_backend(struct xenbus_device *dev, - struct netfront_info *info) -{ - char *backend, *mac, *e, *s; - const char *message; - struct xenbus_transaction *xbt; - int err, i; - - backend = NULL; - err = xenbus_gather(NULL, dev->nodename, - "backend-id", "%i", &info->backend_id, - "backend", NULL, &backend, - NULL); - if (XENBUS_EXIST_ERR(err)) - goto out; - if (backend && strlen(backend) == 0) { - err = -ENOENT; - goto out; - } - if (err < 0) { - xenbus_dev_error(dev, err, "reading %s/backend or backend-id", - dev->nodename); - goto out; - } - - mac = xenbus_read(NULL, dev->nodename, "mac", NULL); - if (IS_ERR(mac)) { - err = PTR_ERR(mac); - xenbus_dev_error(dev, err, "reading %s/mac", - dev->nodename); - goto out; - } - s = mac; - for (i = 0; i < ETH_ALEN; i++) { - info->mac[i] = simple_strtoul(s, &e, 16); - if (s == e || (e[0] != ':' && e[0] != 0)) { - kfree(mac); - err = -ENOENT; - xenbus_dev_error(dev, err, "parsing %s/mac", - dev->nodename); - goto out; - } - s = &e[1]; - } - kfree(mac); - - /* Create shared ring, alloc event channel. */ - err = setup_device(dev, info); - if (err) { - xenbus_dev_error(dev, err, "setting up ring"); - goto out; - } - -again: - xbt = xenbus_transaction_start(); - if (IS_ERR(xbt)) { - xenbus_dev_error(dev, err, "starting transaction"); - goto destroy_ring; - } - - err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref","%u", - info->tx_ring_ref); - if (err) { - message = "writing tx ring-ref"; - goto abort_transaction; - } - err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref","%u", - info->rx_ring_ref); - if (err) { - message = "writing rx ring-ref"; - goto abort_transaction; - } - err = xenbus_printf(xbt, dev->nodename, - "event-channel", "%u", info->evtchn); - if (err) { - message = "writing event-channel"; - goto abort_transaction; - } - - err = xenbus_transaction_end(xbt, 0); - if (err) { - if (err == -EAGAIN) - goto again; - xenbus_dev_error(dev, err, "completing transaction"); - goto destroy_ring; - } - - info->watch.node = backend; - info->watch.callback = watch_for_status; - err = register_xenbus_watch(&info->watch); - if (err) { - message = "registering watch on backend"; - goto destroy_ring; - } - - info->backend = backend; - - return 0; - - abort_transaction: - xenbus_transaction_end(xbt, 1); - xenbus_dev_error(dev, err, "%s", message); - destroy_ring: - shutdown_device(info); - out: - kfree(backend); - return err; -} - -/* - * Setup supplies the backend dir, virtual device. - * We place an event channel and shared frame entries. - * We watch backend to wait if it's ok. - */ -static int netfront_probe(struct xenbus_device *dev, - const struct xenbus_device_id *id) -{ - int err; - struct net_device *netdev; - struct netfront_info *info; - unsigned int handle; - - err = xenbus_scanf(NULL, dev->nodename, "handle", "%u", &handle); - if (XENBUS_EXIST_ERR(err)) - return err; - if (err < 0) { - xenbus_dev_error(dev, err, "reading handle"); - return err; - } - - err = create_netdev(handle, dev, &netdev); - if (err) { - xenbus_dev_error(dev, err, "creating netdev"); - return err; - } - - info = netdev_priv(netdev); - dev->data = info; - - err = talk_to_backend(dev, info); - if (err) { - destroy_netdev(netdev); - kfree(netdev); - dev->data = NULL; - return err; - } - - return 0; -} - -static int netfront_remove(struct xenbus_device *dev) -{ - struct netfront_info *info = dev->data; - - if (info->backend) - unregister_xenbus_watch(&info->watch); - - netif_free(info); - - kfree(info->backend); - kfree(info); - - return 0; -} - -static int netfront_suspend(struct xenbus_device *dev) -{ - struct netfront_info *info = dev->data; - unregister_xenbus_watch(&info->watch); - kfree(info->backend); - info->backend = NULL; - return 0; -} - -static int netfront_resume(struct xenbus_device *dev) -{ - struct netfront_info *info = dev->data; - netif_free(info); - return talk_to_backend(dev, info); -} + +static void end_access(int ref, void *page) +{ + if (ref != GRANT_INVALID_REF) + gnttab_end_foreign_access(ref, 0, (unsigned long)page); +} + + +/* ** Driver registration ** */ + + +static struct xenbus_device_id netfront_ids[] = { + { "vif" }, + { "" } +}; + static struct xenbus_driver netfront = { .name = "vif", @@ -1256,13 +1254,15 @@ .probe = netfront_probe, .remove = netfront_remove, .resume = netfront_resume, - .suspend = netfront_suspend, + .otherend_changed = backend_changed, }; -static void __init init_net_xenbus(void) -{ - xenbus_register_driver(&netfront); -} + +static struct notifier_block notifier_inetdev = { + .notifier_call = inetdev_notify, + .next = NULL, + .priority = 0 +}; static int __init netif_init(void) { @@ -1278,14 +1278,24 @@ (void)register_inetaddr_notifier(¬ifier_inetdev); - init_net_xenbus(); - - return err; -} + return xenbus_register_frontend(&netfront); +} +module_init(netif_init); + static void netif_exit(void) { -} + unregister_inetaddr_notifier(¬ifier_inetdev); + + return xenbus_unregister_driver(&netfront); +} +module_exit(netif_exit); + +MODULE_LICENSE("BSD"); + + +/* ** /proc **/ + #ifdef CONFIG_PROC_FS @@ -1298,7 +1308,7 @@ { struct net_device *dev = (struct net_device *)((unsigned long)data & ~3UL); - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); int len = 0, which_target = (long)data & 3; switch (which_target) @@ -1324,7 +1334,7 @@ { struct net_device *dev = (struct net_device *)((unsigned long)data & ~3UL); - struct net_private *np = netdev_priv(dev); + struct netfront_info *np = netdev_priv(dev); int which_target = (long)data & 3; char string[64]; long target; @@ -1438,8 +1448,6 @@ #endif -module_init(netif_init); -module_exit(netif_exit); /* * Local variables: diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile Fri Nov 11 20:59:05 2005 @@ -1,6 +1,7 @@ obj-y += xenbus.o xenbus-objs = +xenbus-objs += xenbus_client.o xenbus-objs += xenbus_comms.o xenbus-objs += xenbus_xs.o xenbus-objs += xenbus_probe.o diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c Fri Nov 11 20:59:05 2005 @@ -3,6 +3,7 @@ * * Copyright (C) 2005 Rusty Russell, IBM Corporation * Copyright (C) 2005 Mike Wray, Hewlett-Packard + * Copyright (C) 2005 XenSource Ltd * * This file may be distributed separately from the Linux kernel, or * incorporated into other software packages, subject to the following license: @@ -25,7 +26,13 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#define DEBUG + +#if 0 +#define DPRINTK(fmt, args...) \ + printk("xenbus_probe (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args) +#else +#define DPRINTK(fmt, args...) ((void)0) +#endif #include <linux/kernel.h> #include <linux/err.h> @@ -85,6 +92,7 @@ struct device dev; }; + /* device/<type>/<id> => <type>-<id> */ static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename) { @@ -102,6 +110,62 @@ *strchr(bus_id, '/') = '-'; return 0; } + + +static int read_otherend_details(struct xenbus_device *xendev, + char *id_node, char *path_node) +{ + int err = xenbus_gather(NULL, xendev->nodename, + id_node, "%i", &xendev->otherend_id, + path_node, NULL, &xendev->otherend, + NULL); + if (err) { + xenbus_dev_fatal(xendev, err, + "reading other end details from %s", + xendev->nodename); + return err; + } + if (strlen(xendev->otherend) == 0 || + !xenbus_exists(NULL, xendev->otherend, "")) { + xenbus_dev_fatal(xendev, -ENOENT, "missing other end from %s", + xendev->nodename); + kfree(xendev->otherend); + xendev->otherend = NULL; + return -ENOENT; + } + + return 0; +} + + +static int read_backend_details(struct xenbus_device *xendev) +{ + return read_otherend_details(xendev, "backend-id", "backend"); +} + + +static int read_frontend_details(struct xenbus_device *xendev) +{ + return read_otherend_details(xendev, "frontend-id", "frontend"); +} + + +static void free_otherend_details(struct xenbus_device *dev) +{ + kfree(dev->otherend); + dev->otherend = NULL; +} + + +static void free_otherend_watch(struct xenbus_device *dev) +{ + if (dev->otherend_watch.node) { + unregister_xenbus_watch(&dev->otherend_watch); + kfree(dev->otherend_watch.node); + dev->otherend_watch.node = NULL; + } +} + /* Bus type for frontend drivers. */ static int xenbus_probe_frontend(const char *type, const char *name); @@ -165,6 +229,8 @@ int i = 0; int length = 0; + DPRINTK(""); + if (dev == NULL) return -ENODEV; @@ -211,20 +277,86 @@ }, }; + +static void otherend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + struct xenbus_device *dev = + container_of(watch, struct xenbus_device, otherend_watch); + struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); + + /* Protect us against watches firing on old details when the otherend + details change, say immediately after a resume. */ + if (!dev->otherend || + strncmp(dev->otherend, vec[XS_WATCH_PATH], + strlen(dev->otherend))) { + DPRINTK("Ignoring watch at %s", vec[XS_WATCH_PATH]); + return; + } + + XenbusState state = xenbus_read_driver_state(dev->otherend); + + DPRINTK("state is %d, %s, %s", state, dev->otherend_watch.node, vec[XS_WATCH_PATH]); + + drv->otherend_changed(dev, state); +} + + +static int talk_to_otherend(struct xenbus_device *dev) +{ + struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); + int err; + + free_otherend_watch(dev); + free_otherend_details(dev); + + err = drv->read_otherend_details(dev); + if (err) + return err; + + return xenbus_watch_path2(dev, dev->otherend, "state", + &dev->otherend_watch, otherend_changed); +} + + static int xenbus_dev_probe(struct device *_dev) { struct xenbus_device *dev = to_xenbus_device(_dev); struct xenbus_driver *drv = to_xenbus_driver(_dev->driver); const struct xenbus_device_id *id; - - if (!drv->probe) - return -ENODEV; + int err; + + DPRINTK(""); + + err = talk_to_otherend(dev); + if (err) { + printk(KERN_WARNING + "xenbus_probe: talk_to_otherend on %s failed.\n", + dev->nodename); + return err; + } + + if (!drv->probe) { + err = -ENODEV; + goto fail; + } id = match_device(drv->ids, dev); - if (!id) - return -ENODEV; - - return drv->probe(dev, id); + if (!id) { + err = -ENODEV; + goto fail; + } + + err = drv->probe(dev, id); + if (err) + goto fail; + + return 0; +fail: + xenbus_dev_error(dev, err, "xenbus_dev_probe on %s", dev->nodename); + xenbus_switch_state(dev, NULL, XenbusStateClosed); + return -ENODEV; + } static int xenbus_dev_remove(struct device *_dev) @@ -232,9 +364,16 @@ struct xenbus_device *dev = to_xenbus_device(_dev); struct xenbus_driver *drv = to_xenbus_driver(_dev->driver); - if (!drv->remove) - return 0; - return drv->remove(dev); + DPRINTK(""); + + free_otherend_watch(dev); + free_otherend_details(dev); + + if (drv->remove) + drv->remove(dev); + + xenbus_switch_state(dev, NULL, XenbusStateClosed); + return 0; } static int xenbus_register_driver_common(struct xenbus_driver *drv, @@ -254,16 +393,21 @@ return ret; } -int xenbus_register_driver(struct xenbus_driver *drv) -{ +int xenbus_register_frontend(struct xenbus_driver *drv) +{ + drv->read_otherend_details = read_backend_details; + return xenbus_register_driver_common(drv, &xenbus_frontend); } -EXPORT_SYMBOL(xenbus_register_driver); +EXPORT_SYMBOL(xenbus_register_frontend); int xenbus_register_backend(struct xenbus_driver *drv) { + drv->read_otherend_details = read_frontend_details; + return xenbus_register_driver_common(drv, &xenbus_backend); } +EXPORT_SYMBOL(xenbus_register_backend); void xenbus_unregister_driver(struct xenbus_driver *drv) { @@ -304,6 +448,8 @@ struct xenbus_device *xendev = to_xenbus_device(dev); struct xb_find_info *info = data; int len = strlen(info->nodename); + + DPRINTK("%s", info->nodename); if (!strncmp(xendev->nodename, info->nodename, len)) { info->dev = xendev; @@ -327,12 +473,15 @@ } while (info.dev); } -static void xenbus_release_device(struct device *dev) +static void xenbus_dev_free(struct xenbus_device *xendev) +{ + kfree(xendev); +} + +static void xenbus_dev_release(struct device *dev) { if (dev) { - struct xenbus_device *xendev = to_xenbus_device(dev); - - kfree(xendev); + xenbus_dev_free(to_xenbus_device(dev)); } } @@ -369,13 +518,31 @@ } DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL); + static int xenbus_probe_node(struct xen_bus_type *bus, const char *type, const char *nodename) { +#define CHECK_FAIL \ + do { \ + if (err) \ + goto fail; \ + } \ + while (0) \ + + int err; struct xenbus_device *xendev; - unsigned int stringlen; + size_t stringlen; + char *tmpstring; + + XenbusState state = xenbus_read_driver_state(nodename); + + if (state != XenbusStateInitialising) { + /* Device is not new, so ignore it. This can happen if a + device is going away after switching to Closed. */ + return 0; + } stringlen = strlen(nodename) + 1 + strlen(type) + 1; xendev = kmalloc(sizeof(*xendev) + stringlen, GFP_KERNEL); @@ -384,31 +551,35 @@ memset(xendev, 0, sizeof(*xendev)); /* Copy the strings into the extra space. */ - xendev->nodename = (char *)(xendev + 1); - strcpy(xendev->nodename, nodename); - xendev->devicetype = xendev->nodename + strlen(xendev->nodename) + 1; - strcpy(xendev->devicetype, type); + + tmpstring = (char *)(xendev + 1); + strcpy(tmpstring, nodename); + xendev->nodename = tmpstring; + + tmpstring += strlen(tmpstring) + 1; + strcpy(tmpstring, type); + xendev->devicetype = tmpstring; xendev->dev.parent = &bus->dev; xendev->dev.bus = &bus->bus; - xendev->dev.release = xenbus_release_device; + xendev->dev.release = xenbus_dev_release; err = bus->get_bus_id(xendev->dev.bus_id, xendev->nodename); - if (err) { - kfree(xendev); - return err; - } + CHECK_FAIL; /* Register with generic device framework. */ err = device_register(&xendev->dev); - if (err) { - printk("XENBUS: Registering %s device %s: error %i\n", - bus->bus.name, xendev->dev.bus_id, err); - kfree(xendev); - } else { - device_create_file(&xendev->dev, &dev_attr_nodename); - device_create_file(&xendev->dev, &dev_attr_devtype); - } + CHECK_FAIL; + + device_create_file(&xendev->dev, &dev_attr_nodename); + device_create_file(&xendev->dev, &dev_attr_devtype); + + return 0; + +#undef CHECK_FAIL + +fail: + xenbus_dev_free(xendev); return err; } @@ -422,6 +593,8 @@ if (!nodename) return -ENOMEM; + DPRINTK("%s", nodename); + err = xenbus_probe_node(&xenbus_frontend, type, nodename); kfree(nodename); return err; @@ -439,6 +612,8 @@ if (!nodename) return -ENOMEM; + DPRINTK("%s\n", nodename); + err = xenbus_probe_node(&xenbus_backend, type, nodename); kfree(nodename); return err; @@ -451,6 +626,8 @@ int err = 0; char **dir; unsigned int i, dir_n = 0; + + DPRINTK(""); nodename = kasprintf("%s/%s/%s", xenbus_backend.root, type, domid); if (!nodename) @@ -574,12 +751,16 @@ static void frontend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len) { + DPRINTK(""); + dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend); } static void backend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len) { + DPRINTK(""); + dev_changed(vec[XS_WATCH_PATH], &xenbus_backend); } @@ -599,6 +780,8 @@ int err = 0; struct xenbus_driver *drv; struct xenbus_device *xdev; + + DPRINTK(""); if (dev->driver == NULL) return 0; @@ -607,33 +790,49 @@ if (drv->suspend) err = drv->suspend(xdev); if (err) - printk("xenbus: suspend %s failed: %i\n", dev->bus_id, err); + printk(KERN_WARNING + "xenbus: suspend %s failed: %i\n", dev->bus_id, err); return 0; } static int resume_dev(struct device *dev, void *data) { - int err = 0; + int err; struct xenbus_driver *drv; struct xenbus_device *xdev; + + DPRINTK(""); if (dev->driver == NULL) return 0; drv = to_xenbus_driver(dev->driver); xdev = container_of(dev, struct xenbus_device, dev); + + err = talk_to_otherend(xdev); + if (err) { + printk(KERN_WARNING + "xenbus: resume (talk_to_otherend) %s failed: %i\n", + dev->bus_id, err); + return err; + } + if (drv->resume) err = drv->resume(xdev); if (err) - printk("xenbus: resume %s failed: %i\n", dev->bus_id, err); - return 0; + printk(KERN_WARNING + "xenbus: resume %s failed: %i\n", dev->bus_id, err); + return err; } void xenbus_suspend(void) { + DPRINTK(""); + bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_dev); bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, suspend_dev); xs_suspend(); } +EXPORT_SYMBOL(xenbus_suspend); void xenbus_resume(void) { @@ -642,6 +841,7 @@ bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, resume_dev); bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, resume_dev); } +EXPORT_SYMBOL(xenbus_resume); /* A flag to determine if xenstored is 'ready' (i.e. has started) */ @@ -683,8 +883,6 @@ /* Notify others that xenstore is up */ notifier_call_chain(&xenstore_chain, 0, 0); - - return; } @@ -716,10 +914,10 @@ { int err = 0, dom0; - printk("xenbus_probe_init\n"); + DPRINTK(""); if (xen_init() < 0) { - printk("xen_init failed\n"); + DPRINTK("failed"); return -ENODEV; } @@ -773,7 +971,8 @@ /* Initialize the interface to xenstore. */ err = xs_init(); if (err) { - printk("XENBUS: Error initializing xenstore comms: %i\n", err); + printk(KERN_WARNING + "XENBUS: Error initializing xenstore comms: %i\n", err); return err; } diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c Fri Nov 11 20:59:05 2005 @@ -517,87 +517,6 @@ } EXPORT_SYMBOL(xenbus_printf); -/** - * Return the path to the error node for the given device, or NULL on failure. - * If the value returned is non-NULL, then it is the caller's to kfree. - */ -static char *error_path(struct xenbus_device *dev) -{ - char *path_buffer = kmalloc(strlen("error/") + strlen(dev->nodename) + - 1, GFP_KERNEL); - if (path_buffer == NULL) { - return NULL; - } - - strcpy(path_buffer, "error/"); - strcpy(path_buffer + strlen("error/"), dev->nodename); - - return path_buffer; -} - -/* Report a (negative) errno into the store, with explanation. */ -void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...) -{ - va_list ap; - int ret; - unsigned int len; - char *printf_buffer = NULL, *path_buffer = NULL; - - printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL); - if (printf_buffer == NULL) - goto fail; - - len = sprintf(printf_buffer, "%i ", -err); - va_start(ap, fmt); - ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap); - va_end(ap); - - BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1); - dev->has_error = 1; - - path_buffer = error_path(dev); - - if (path_buffer == NULL) { - printk("xenbus: failed to write error node for %s (%s)\n", - dev->nodename, printf_buffer); - goto fail; - } - - if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) { - printk("xenbus: failed to write error node for %s (%s)\n", - dev->nodename, printf_buffer); - goto fail; - } - -fail: - kfree(printf_buffer); - kfree(path_buffer); -} -EXPORT_SYMBOL(xenbus_dev_error); - -/* Clear any error. */ -void xenbus_dev_ok(struct xenbus_device *dev) -{ - if (dev->has_error) { - char *path_buffer = error_path(dev); - - if (path_buffer == NULL) { - printk("xenbus: failed to clear error node for %s\n", - dev->nodename); - return; - } - - if (xenbus_rm(NULL, path_buffer, "error") != 0) - printk("xenbus: failed to clear error node for %s\n", - dev->nodename); - else - dev->has_error = 0; - - kfree(path_buffer); - } -} -EXPORT_SYMBOL(xenbus_dev_ok); - /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */ int xenbus_gather(struct xenbus_transaction *t, const char *dir, ...) { diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/include/asm-xen/xenbus.h --- a/linux-2.6-xen-sparse/include/asm-xen/xenbus.h Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenbus.h Fri Nov 11 20:59:05 2005 @@ -35,10 +35,48 @@ #include <asm/semaphore.h> #include <asm-xen/xen-public/io/xs_wire.h> +/* Register callback to watch this node. */ +struct xenbus_watch +{ + struct list_head list; + + /* Path being watched. */ + const char *node; + + /* Callback (executed in a process context with no locks held). */ + void (*callback)(struct xenbus_watch *, + const char **vec, unsigned int len); +}; + + +/* The state of either end of the Xenbus, i.e. the current communication + status of initialisation across the bus. States here imply nothing about + the state of the connection between the driver and the kernel's device + layers. */ +typedef enum +{ + XenbusStateUnknown = 0, + XenbusStateInitialising = 1, + XenbusStateInitWait = 2, /* Finished early initialisation, but waiting + for information from the peer or hotplug + scripts. */ + XenbusStateInitialised = 3, /* Initialised and waiting for a connection + from the peer. */ + XenbusStateConnected = 4, + XenbusStateClosing = 5, /* The device is being closed due to an error + or an unplug event. */ + XenbusStateClosed = 6 + +} XenbusState; + + /* A xenbus device. */ struct xenbus_device { - char *devicetype; - char *nodename; + const char *devicetype; + const char *nodename; + const char *otherend; + int otherend_id; + struct xenbus_watch otherend_watch; struct device dev; int has_error; void *data; @@ -62,11 +100,14 @@ const struct xenbus_device_id *ids; int (*probe)(struct xenbus_device *dev, const struct xenbus_device_id *id); + void (*otherend_changed)(struct xenbus_device *dev, + XenbusState backend_state); int (*remove)(struct xenbus_device *dev); int (*suspend)(struct xenbus_device *dev); int (*resume)(struct xenbus_device *dev); int (*hotplug)(struct xenbus_device *, char **, int, char *, int); struct device_driver driver; + int (*read_otherend_details)(struct xenbus_device *dev); }; static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv) @@ -74,7 +115,7 @@ return container_of(drv, struct xenbus_driver, driver); } -int xenbus_register_driver(struct xenbus_driver *drv); +int xenbus_register_frontend(struct xenbus_driver *drv); int xenbus_register_backend(struct xenbus_driver *drv); void xenbus_unregister_driver(struct xenbus_driver *drv); @@ -108,25 +149,6 @@ * sprintf-style type string, and pointer. Returns 0 or errno.*/ int xenbus_gather(struct xenbus_transaction *t, const char *dir, ...); -/* Report a (negative) errno into the store, with explanation. */ -void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,...); - -/* Clear any error. */ -void xenbus_dev_ok(struct xenbus_device *dev); - -/* Register callback to watch this node. */ -struct xenbus_watch -{ - struct list_head list; - - /* Path being watched. */ - char *node; - - /* Callback (executed in a process context with no locks held). */ - void (*callback)(struct xenbus_watch *, - const char **vec, unsigned int len); -}; - /* notifer routines for when the xenstore comes up */ int register_xenstore_notifier(struct notifier_block *nb); void unregister_xenstore_notifier(struct notifier_block *nb); @@ -152,6 +174,89 @@ }) #define XENBUS_EXIST_ERR(err) ((err) == -ENOENT || (err) == -ERANGE) + + +/** + * Register a watch on the given path, using the given xenbus_watch structure + * for storage, and the given callback function as the callback. Return 0 on + * success, or -errno on error. On success, the given path will be saved as + * watch->node, and remains the caller's to free. On error, watch->node will + * be NULL, the device will switch to XenbusStateClosing, and the error will + * be saved in the store. + */ +int xenbus_watch_path(struct xenbus_device *dev, const char *path, + struct xenbus_watch *watch, + void (*callback)(struct xenbus_watch *, + const char **, unsigned int)); + + +/** + * Register a watch on the given path/path2, using the given xenbus_watch + * structure for storage, and the given callback function as the callback. + * Return 0 on success, or -errno on error. On success, the watched path + * (path/path2) will be saved as watch->node, and becomes the caller's to + * kfree(). On error, watch->node will be NULL, so the caller has nothing to + * free, the device will switch to XenbusStateClosing, and the error will be + * saved in the store. + */ +int xenbus_watch_path2(struct xenbus_device *dev, const char *path, + const char *path2, struct xenbus_watch *watch, + void (*callback)(struct xenbus_watch *, + const char **, unsigned int)); + + +/** + * Advertise in the store a change of the given driver to the given new_state. + * Perform the change inside the given transaction xbt. xbt may be NULL, in + * which case this is performed inside its own transaction. Return 0 on + * success, or -errno on error. On error, the device will switch to + * XenbusStateClosing, and the error will be saved in the store. + */ +int xenbus_switch_state(struct xenbus_device *dev, + struct xenbus_transaction *xbt, + XenbusState new_state); + + +/** + * Grant access to the given ring_mfn to the peer of the given device. Return + * 0 on success, or -errno on error. On error, the device will switch to + * XenbusStateClosing, and the error will be saved in the store. + */ +int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn); + + +/** + * Allocate an event channel for the given xenbus_device, assigning the newly + * created local port to *port. Return 0 on success, or -errno on error. On + * error, the device will switch to XenbusStateClosing, and the error will be + * saved in the store. + */ +int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port); + + +/** + * Return the state of the driver rooted at the given store path, or + * XenbusStateClosed if no state can be read. + */ +XenbusState xenbus_read_driver_state(const char *path); + + +/*** + * Report the given negative errno into the store, along with the given + * formatted message. + */ +void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, + ...); + + +/*** + * Equivalent to xenbus_dev_error(dev, err, fmt, args), followed by + * xenbus_switch_state(dev, NULL, XenbusStateClosing) to schedule an orderly + * closedown of this driver and its peer. + */ +void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, + ...); + #endif /* _ASM_XEN_XENBUS_H */ diff -r e2e7f47e6f79 -r bf7c16e761fc tools/python/xen/util/diagnose.py --- a/tools/python/xen/util/diagnose.py Fri Nov 11 17:01:44 2005 +++ b/tools/python/xen/util/diagnose.py Fri Nov 11 20:59:05 2005 @@ -22,6 +22,7 @@ from xen.xend.XendClient import server from xen.xend.XendError import XendError from xen.xend.xenstore.xstransact import xstransact +from xen.xend.server import DevController import xen.xend.XendProtocol @@ -107,34 +108,57 @@ print ("Cannot find backend path for device %s, %s." % (deviceClass, device)) else: - backend_error = xstransact.Read( - backendPath.replace('backend/', 'error/backend/'), - 'error') + frontend_state = xstransact.Read(frontendPath, 'state') + backend_state = xstransact.Read(backendPath, 'state') - if backend_error: - diagnose_device_error(backend_error) + print "Backend is in state %s." % stateString(backend_state) + print "Frontend is in state %s." % stateString(frontend_state) + + check_for_error(True) + check_for_error(False) + + diagnose_hotplugging() -def diagnose_device_error(err): - if re.search("2 reading .*/ring-ref and event-channel", err): - print ("Backend is stuck waiting for frontend for device %s, %s." % - (deviceClass, device)) - diagnose_stuck_frontend() +def check_for_error(backend): + if backend: + path = backendPath.replace('backend/', 'error/backend/') else: - print ("Device %s, %s shows error %s." % - (deviceClass, device, err)) + path = frontendPath.replace('device/', 'error/device/') + + err = xstransact.Read(path, 'error') + + if err: + print ("%s for device %s, %s shows error %s." % + (backend and 'Backend' or 'Frontend', deviceClass, device, + err)) -def diagnose_stuck_frontend(): - if deviceClass == "vbd": +def diagnose_hotplugging(): + if deviceClass == 'vbd': phy = xstransact.Read(backendPath, 'physical-device') if phy: - print ("Device %s, %s hotplugging has completed successfully." % + print ('Device %s, %s hotplugging has completed successfully, ' + 'and is connected to physical device %s.' % + (deviceClass, device, phy)) + else: + print ('Device %s, %s hotplugging failed.' % (deviceClass, device)) + elif deviceClass == 'vif': + handle = xstransact.Read(backendPath, 'handle') + + if handle: + print ('Device %s, %s hotplugging has completed successfully, ' + 'and is using handle %s.' % + (deviceClass, device, handle)) else: - print ("Device %s, %s hotplugging failed." % + print ('Device %s, %s hotplugging failed.' % (deviceClass, device)) + + +def stateString(state): + return state and DevController.xenbusState[int(state)] or '<None>' def main(argv = None): diff -r e2e7f47e6f79 -r bf7c16e761fc tools/python/xen/xend/server/DevController.py --- a/tools/python/xen/xend/server/DevController.py Fri Nov 11 17:01:44 2005 +++ b/tools/python/xen/xend/server/DevController.py Fri Nov 11 20:59:05 2005 @@ -25,9 +25,22 @@ from xen.xend.xenstore.xstransact import xstransact from xen.xend.xenstore.xswatch import xswatch -DEVICE_CREATE_TIMEOUT = 120 +DEVICE_CREATE_TIMEOUT = 5 HOTPLUG_STATUS_NODE = "hotplug-status" HOTPLUG_STATUS_ERROR = "error" + +xenbusState = { + 'Unknown' : 0, + 'Initialising' : 1, + 'InitWait' : 2, + 'Initialised' : 3, + 'Connected' : 4, + 'Closing' : 5, + 'Closed' : 6, + } + +xenbusState.update(dict(zip(xenbusState.values(), xenbusState.keys()))) + class DevController: """Abstract base class for a device controller. Device controllers create @@ -116,10 +129,8 @@ frontpath = self.frontendPath(devid) backpath = xstransact.Read(frontpath, "backend") - xstransact.Remove(frontpath) - if backpath: - xstransact.Remove(backpath) + xstransact.Write(backpath, 'state', str(xenbusState['Closing'])) else: raise VmError("Device %s not connected" % devid) @@ -257,18 +268,18 @@ frontpath = self.frontendPath(devid) backpath = self.backendPath(backdom, devid) - xstransact.Remove(backpath, HOTPLUG_STATUS_NODE) - frontDetails.update({ 'backend' : backpath, - 'backend-id' : "%i" % backdom.getDomid() + 'backend-id' : "%i" % backdom.getDomid(), + 'state' : str(xenbusState['Initialising']) }) backDetails.update({ 'domain' : self.vm.getName(), 'frontend' : frontpath, - 'frontend-id' : "%i" % self.vm.getDomid() + 'frontend-id' : "%i" % self.vm.getDomid(), + 'state' : str(xenbusState['Initialising']) }) log.debug('DevController: writing %s to %s.', str(frontDetails), @@ -276,8 +287,20 @@ log.debug('DevController: writing %s to %s.', str(backDetails), backpath) - xstransact.Write(frontpath, frontDetails) - xstransact.Write(backpath, backDetails) + while True: + t = xstransact() + try: + t.remove2(backpath, HOTPLUG_STATUS_NODE) + + t.write2(frontpath, frontDetails) + t.write2(backpath, backDetails) + + if t.commit(): + return + except: + t.abort() + raise + def waitForBackend(self,devid): ev = Event() diff -r e2e7f47e6f79 -r bf7c16e761fc tools/python/xen/xend/xenstore/xstransact.py --- a/tools/python/xen/xend/xenstore/xstransact.py Fri Nov 11 17:01:44 2005 +++ b/tools/python/xen/xend/xenstore/xstransact.py Fri Nov 11 20:59:05 2005 @@ -10,7 +10,7 @@ class xstransact: - def __init__(self, path): + def __init__(self, path = ""): assert path is not None self.in_transaction = False # Set this temporarily -- if this @@ -41,7 +41,7 @@ return rc def _read(self, key): - path = "%s/%s" % (self.path, key) + path = self.prependPath(key) try: return xshandle().read(self.transaction, path) except RuntimeError, ex: @@ -66,7 +66,7 @@ return ret def _write(self, key, data): - path = "%s/%s" % (self.path, key) + path = self.prependPath(key) try: xshandle().write(self.transaction, path, data) except RuntimeError, ex: @@ -99,7 +99,7 @@ raise TypeError def _remove(self, key): - path = "%s/%s" % (self.path, key) + path = self.prependPath(key) return xshandle().rm(self.transaction, path) def remove(self, *args): @@ -114,7 +114,7 @@ self._remove(key) def _list(self, key): - path = "%s/%s" % (self.path, key) + path = self.prependPath(key) l = xshandle().ls(self.transaction, path) if l: return map(lambda x: key + "/" + x, l) @@ -215,6 +215,30 @@ self._write(key, fmt % val) + def remove2(self, middlePath, *args): + self.callRebased(middlePath, self.remove, *args) + + + def write2(self, middlePath, *args): + self.callRebased(middlePath, self.write, *args) + + + def callRebased(self, middlePath, func, *args): + oldpath = self.path + self.path = self.prependPath(middlePath) + try: + func(*args) + finally: + self.path = oldpath + + + def prependPath(self, key): + if self.path: + return self.path + '/' + key + else: + return key + + def Read(cls, path, *args): """If only one argument is given (path), return the value stored at that path. If two arguments are given, treat the second argument as a diff -r e2e7f47e6f79 -r bf7c16e761fc linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c --- /dev/null Fri Nov 11 17:01:44 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c Fri Nov 11 20:59:05 2005 @@ -0,0 +1,253 @@ +/****************************************************************************** + * Client-facing interface for the Xenbus driver. In other words, the + * interface between the Xenbus and the device-specific code, be it the + * frontend or the backend of that driver. + * + * Copyright (C) 2005 XenSource Ltd + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#if 0 +#define DPRINTK(fmt, args...) \ + printk("xenbus_client (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args) +#else +#define DPRINTK(fmt, args...) ((void)0) +#endif + + +#include <asm-xen/evtchn.h> +#include <asm-xen/gnttab.h> +#include <asm-xen/xenbus.h> + + +int xenbus_watch_path(struct xenbus_device *dev, const char *path, + struct xenbus_watch *watch, + void (*callback)(struct xenbus_watch *, + const char **, unsigned int)) +{ + int err; + + watch->node = path; + watch->callback = callback; + + err = register_xenbus_watch(watch); + + if (err) { + watch->node = NULL; + watch->callback = NULL; + xenbus_dev_fatal(dev, err, "adding watch on %s", path); + } + + return err; +} +EXPORT_SYMBOL(xenbus_watch_path); + + +int xenbus_watch_path2(struct xenbus_device *dev, const char *path, + const char *path2, struct xenbus_watch *watch, + void (*callback)(struct xenbus_watch *, + const char **, unsigned int)) +{ + int err; + char *state = + kmalloc(strlen(path) + 1 + strlen(path2) + 1, GFP_KERNEL); + if (!state) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating path for watch"); + return -ENOMEM; + } + strcpy(state, path); + strcat(state, "/"); + strcat(state, path2); + + err = xenbus_watch_path(dev, state, watch, callback); + + if (err) { + kfree(state); + } + return err; +} +EXPORT_SYMBOL(xenbus_watch_path2); + + +int xenbus_switch_state(struct xenbus_device *dev, + struct xenbus_transaction *xbt, + XenbusState state) +{ + /* We check whether the state is currently set to the given value, and + if not, then the state is set. We don't want to unconditionally + write the given state, because we don't want to fire watches + unnecessarily. + */ + + int current_state; + + int err = xenbus_scanf(xbt, dev->nodename, "state", "%d", + ¤t_state); + if (err == 1 && (XenbusState)current_state == state) + return 0; + + err = xenbus_printf(xbt, dev->nodename, "state", "%d", state); + if (err) { + xenbus_dev_fatal(dev, err, "writing new state"); + return err; + } + return 0; +} +EXPORT_SYMBOL(xenbus_switch_state); + + +/** + * Return the path to the error node for the given device, or NULL on failure. + * If the value returned is non-NULL, then it is the caller's to kfree. + */ +static char *error_path(struct xenbus_device *dev) +{ + char *path_buffer = kmalloc(strlen("error/") + strlen(dev->nodename) + + 1, GFP_KERNEL); + if (path_buffer == NULL) { + return NULL; + } + + strcpy(path_buffer, "error/"); + strcpy(path_buffer + strlen("error/"), dev->nodename); + + return path_buffer; +} + + +void _dev_error(struct xenbus_device *dev, int err, const char *fmt, + va_list ap) +{ + int ret; + unsigned int len; + char *printf_buffer = NULL, *path_buffer = NULL; + +#define PRINTF_BUFFER_SIZE 4096 + printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL); + if (printf_buffer == NULL) + goto fail; + + len = sprintf(printf_buffer, "%i ", -err); + ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap); + + BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1); + dev->has_error = 1; + + path_buffer = error_path(dev); + + if (path_buffer == NULL) { + printk("xenbus: failed to write error node for %s (%s)\n", + dev->nodename, printf_buffer); + goto fail; + } + + if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) { + printk("xenbus: failed to write error node for %s (%s)\n", + dev->nodename, printf_buffer); + goto fail; + } + +fail: + if (printf_buffer) + kfree(printf_buffer); + if (path_buffer) + kfree(path_buffer); +} + + +void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, + ...) +{ + va_list ap; + + va_start(ap, fmt); + _dev_error(dev, err, fmt, ap); + va_end(ap); +} +EXPORT_SYMBOL(xenbus_dev_error); + + +void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, + ...) +{ + va_list ap; + + va_start(ap, fmt); + _dev_error(dev, err, fmt, ap); + va_end(ap); + + xenbus_switch_state(dev, NULL, XenbusStateClosing); +} +EXPORT_SYMBOL(xenbus_dev_fatal); + + +int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn) +{ + int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0); + if (err < 0) + xenbus_dev_fatal(dev, err, "granting access to ring page"); + return err; +} +EXPORT_SYMBOL(xenbus_grant_ring); + + +int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port) +{ + evtchn_op_t op = { + .cmd = EVTCHNOP_alloc_unbound, + .u.alloc_unbound.dom = DOMID_SELF, + .u.alloc_unbound.remote_dom = dev->otherend_id }; + + int err = HYPERVISOR_event_channel_op(&op); + if (err) + xenbus_dev_fatal(dev, err, "allocating event channel"); + else + *port = op.u.alloc_unbound.port; + return err; +} +EXPORT_SYMBOL(xenbus_alloc_evtchn); + + +XenbusState xenbus_read_driver_state(const char *path) +{ + XenbusState result; + + int err = xenbus_gather(NULL, path, "state", "%d", &result, NULL); + if (err) + result = XenbusStateClosed; + + return result; +} +EXPORT_SYMBOL(xenbus_read_driver_state); + + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |