diff --git a/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c b/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c index 1d5a01a10f..36ac488b39 100644 --- a/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c +++ b/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c @@ -1,5 +1,6 @@ /* Xenbus code for blkif backend Copyright (C) 2005 Rusty Russell + 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 #include #include #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; - 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; + long int pdev; + long int readonly; }; + +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) + +/** + * 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) { - 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; + struct backend_info *be = kmalloc(sizeof(struct backend_info), + GFP_KERNEL); + if (!be) { + xenbus_dev_fatal(dev, -ENOMEM, + "allocating backend structure"); + return -ENOMEM; } - if (be->blkif == NULL || be->blkif->status == CONNECTED) - return; + memset(be, 0, sizeof(*be)); - 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; + 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; } - /* 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. */ + err = xenbus_watch_path2(dev, dev->nodename, "physical-device", + &be->backend_watch, backend_changed); + if (err) + goto fail; -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_switch_state(dev, NULL, XenbusStateInitWait); + if (err) + goto fail; - 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; - } + return 0; - /* 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); +fail: + DPRINTK("failed"); + blkback_remove(dev); + return err; } -/* - Setup supplies physical device. - We provide event channel and device details to front end. - Frontend supplies shared frame and event channel. + +/** + * 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,125 +160,198 @@ static void backend_changed(struct xenbus_watch *watch, 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); + maybe_connect(be); } } -static int blkback_probe(struct xenbus_device *dev, - const struct xenbus_device_id *id) + +/** + * Callback received when the frontend's state changes. + */ +static void frontend_changed(struct xenbus_device *dev, + XenbusState frontend_state) { - struct backend_info *be; - char *frontend; + struct backend_info *be = dev->data; 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)); + DPRINTK(""); - 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; - } + switch (frontend_state) { + case XenbusStateInitialising: + case XenbusStateConnected: + break; - 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", - dev->nodename); - goto free_be; + 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; } - - 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; - return 0; - - free_be: - if (be->backend_watch.node) - unregister_xenbus_watch(&be->backend_watch); - kfree(frontend); - kfree(be); - return err; } + +/* ** 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 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 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; +} + + +/* ** Driver Registration ** */ + + static struct xenbus_device_id blkback_ids[] = { { "vbd" }, { "" } }; + static struct xenbus_driver blkback = { .name = "vbd", .owner = THIS_MODULE, .ids = blkback_ids, .probe = blkback_probe, .remove = blkback_remove, + .otherend_changed = frontend_changed }; + void blkif_xenbus_init(void) { xenbus_register_backend(&blkback); } + /* * Local variables: * c-file-style: "linux" diff --git a/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c b/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c index a865e88f31..5f22d6af66 100644 --- a/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c +++ b/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c @@ -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 , \ @@ -39,6 +41,7 @@ #define ASSERT(_p) #endif + #include #include "block.h" #include @@ -51,6 +54,7 @@ #include #include + #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 @@ static int blkif_queue_request(struct request *req) gnttab_grant_foreign_access_ref( ref, - info->backend_id, + info->xbdev->otherend_id, buffer_mfn, rq_data_dir(req) ); @@ -360,6 +659,15 @@ static void blkif_free(struct blkfront_info *info) 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 @@ static void blkif_recover(struct blkfront_info *info) 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 @@ static void blkif_recover(struct blkfront_info *info) 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 struct xenbus_device_id blkfront_ids[] = { { "" } }; -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 @@ static struct xenbus_driver blkfront = { .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) + +static void xlblk_exit(void) { - 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); + return xenbus_unregister_driver(&blkfront); } +module_exit(xlblk_exit); + +MODULE_LICENSE("BSD"); + /* * Local variables: diff --git a/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h b/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h index c3149f5dee..35f1440388 100644 --- a/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h +++ b/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h @@ -113,15 +113,11 @@ struct blk_shadow { 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 --git a/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c b/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c index 44b7391425..7f4958038b 100644 --- a/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c +++ b/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c @@ -1,5 +1,6 @@ /* Xenbus code for netif backend Copyright (C) 2005 Rusty Russell + 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 #include #include +#include #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; - 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); - 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. +/** + * 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 void backend_changed(struct xenbus_watch *watch, - const char **vec, unsigned int len) +static int netback_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) { 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 }; + 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)); - 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; + 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) { + goto fail; } - 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; - } + return 0; - kobject_hotplug(&dev->dev.kobj, KOBJ_ONLINE); - - /* Pass in NULL node to skip exist test. */ - frontend_changed(&be->watch, NULL, 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 @@ static int netback_hotplug(struct xenbus_device *xdev, char **envp, 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,82 +143,162 @@ static int netback_hotplug(struct xenbus_device *xdev, char **envp, return 0; } -static int netback_probe(struct xenbus_device *dev, - const struct xenbus_device_id *id) + +/** + * 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) { - struct backend_info *be; - char *frontend; + int err; + 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) { + 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; - be = kmalloc(sizeof(*be), GFP_KERNEL); - if (!be) { - xenbus_dev_error(dev, -ENOMEM, "allocating backend structure"); - return -ENOMEM; - } - memset(be, 0, sizeof(*be)); + DPRINTK(""); - 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); + 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->backend_watch.node = NULL; - xenbus_dev_error(dev, err, "adding backend watch on %s", - dev->nodename); - goto free_be; + xenbus_dev_fatal(dev, err, + "reading %s/ring-ref and event-channel", + dev->otherend); + return err; } - be->frontpath = frontend; - be->watch.node = be->frontpath; - be->watch.callback = frontend_changed; - err = register_xenbus_watch(&be->watch); + /* Map the shared frame, irq etc. */ + err = netif_map(be->netif, tx_ring_ref, rx_ring_ref, evtchn); if (err) { - be->watch.node = NULL; - xenbus_dev_error(dev, err, - "adding frontend watch on %s", - be->frontpath); - goto free_be; + xenbus_dev_fatal(dev, err, + "mapping shared-frames %lu/%lu port %u", + tx_ring_ref, rx_ring_ref, evtchn); + return err; } - - dev->data = be; 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", .owner = THIS_MODULE, @@ -269,13 +306,16 @@ static struct xenbus_driver netback = { .probe = netback_probe, .remove = netback_remove, .hotplug = netback_hotplug, + .otherend_changed = frontend_changed, }; + void netif_xenbus_init(void) { xenbus_register_backend(&netback); } + /* * Local variables: * c-file-style: "linux" diff --git a/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c b/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c index 30486a426d..2b6fad55a7 100644 --- a/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c +++ b/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c @@ -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 #include #include +#include #define GRANT_INVALID_REF 0 @@ -87,26 +89,11 @@ #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]; -#ifdef CONFIG_PROC_FS -static int xennet_proc_init(void); -static int xennet_proc_addif(struct net_device *dev); -static void xennet_proc_delif(struct net_device *dev); -#else -#define xennet_proc_init() (0) -#define xennet_proc_addif(d) (0) -#define xennet_proc_delif(d) ((void)0) -#endif - -#define netfront_info net_private -struct net_private +struct netfront_info { struct list_head list; struct net_device *netdev; @@ -154,9 +141,6 @@ struct net_private 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]; @@ -181,16 +165,256 @@ static char *be_state_name[] = { #ifdef DEBUG #define DPRINTK(fmt, args...) \ - printk(KERN_ALERT "xen_net (%s:%d) " fmt, __FUNCTION__, __LINE__, ##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 "xen_net: " fmt, ##args) + printk(KERN_INFO "netfront: " fmt, ##args) #define WPRINTK(fmt, args...) \ - printk(KERN_WARNING "xen_net: " 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); +static int xennet_proc_addif(struct net_device *dev); +static void xennet_proc_delif(struct net_device *dev); +#else +#define xennet_proc_init() (0) +#define xennet_proc_addif(d) (0) +#define xennet_proc_delif(d) ((void)0) +#endif + + +/** + * 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 netfront_info *info; + unsigned int handle; + + 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; + } +} -static void netif_free(struct netfront_info *info); /** Send a packet on a net device to encourage switches to learn the * MAC. We send a fake ARP request. @@ -220,9 +444,10 @@ static int send_fake_arp(struct net_device *dev) 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 @@ static void network_tx_buf_gc(struct net_device *dev) { 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_tx_buf_gc(struct net_device *dev) 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 @@ static void network_alloc_rx_buffers(struct net_device *dev) 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 void network_alloc_rx_buffers(struct net_device *dev) 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 @@ static int network_start_xmit(struct sk_buff *skb, struct net_device *dev) 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 int network_start_xmit(struct sk_buff *skb, struct net_device *dev) 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 irqreturn_t netif_int(int irq, void *dev_id, struct pt_regs *ptregs) 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 @@ static int netif_poll(struct net_device *dev, int *pbudget) 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 netif_poll(struct net_device *dev, int *pbudget) 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 int network_close(struct net_device *dev) 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 @@ static void network_connect(struct net_device *dev) 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 @@ static void network_connect(struct net_device *dev) 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 @@ static void network_connect(struct net_device *dev) 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 @@ static void show_device(struct net_private *np) #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 @@ static int create_netdev(int handle, struct xenbus_device *dev, { int i, err = 0; struct net_device *netdev = NULL; - struct net_private *np = NULL; + struct netfront_info *np = NULL; - if ((netdev = alloc_etherdev(sizeof(struct net_private))) == 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 @@ static int create_netdev(int handle, struct xenbus_device *dev, 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 @@ inetdev_notify(struct notifier_block *this, unsigned long event, void *ptr) 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" }, - { "" } -}; +/* ** Close down ** */ -static void watch_for_status(struct xenbus_watch *watch, - const char **vec, unsigned int len) + +/** + * 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 setup_device(struct xenbus_device *dev, struct netfront_info *info) + +static int netfront_remove(struct xenbus_device *dev) { - int err; - evtchn_op_t op = { - .cmd = EVTCHNOP_alloc_unbound, - .u.alloc_unbound.dom = DOMID_SELF, - .u.alloc_unbound.remote_dom = info->backend_id }; + struct netfront_info *info = dev->data; - info->tx_ring_ref = GRANT_INVALID_REF; - info->rx_ring_ref = GRANT_INVALID_REF; - info->rx = NULL; - info->tx = NULL; - info->irq = 0; + DPRINTK("%s\n", dev->nodename); - 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); + 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 @@ static void netif_free(struct netfront_info *info) info->evtchn = info->irq = 0; } -/* Stop network device and free tx/rx queues and irq. */ -static void shutdown_device(struct net_private *np) + +static void end_access(int ref, void *page) { - /* 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); + if (ref != GRANT_INVALID_REF) + gnttab_end_foreign_access(ref, 0, (unsigned long)page); } -/* 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; - } +/* ** Driver registration ** */ - 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; - } +static struct xenbus_device_id netfront_ids[] = { + { "vif" }, + { "" } +}; -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 struct xenbus_driver netfront = { .name = "vif", @@ -1256,13 +1254,15 @@ static struct xenbus_driver netfront = { .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 @@ static int __init netif_init(void) (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 @@ static int xennet_proc_read( { 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 @@ static int xennet_proc_write( { 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 @@ static void xennet_proc_delif(struct net_device *dev) #endif -module_init(netif_init); -module_exit(netif_exit); /* * Local variables: diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile b/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile index 075cb3ff43..b07cb72139 100644 --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile @@ -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 --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c new file mode 100644 index 0000000000..869ce26906 --- /dev/null +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c @@ -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 +#include +#include + + +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: + */ diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c index dda10a04ad..5d5093d6c3 100644 --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c @@ -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 #include @@ -85,6 +92,7 @@ struct xen_bus_type struct device dev; }; + /* device// => - */ static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename) { @@ -103,6 +111,62 @@ static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename) 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); static struct xen_bus_type xenbus_frontend = { @@ -165,6 +229,8 @@ static int xenbus_hotplug_backend(struct device *dev, char **envp, int i = 0; int length = 0; + DPRINTK(""); + if (dev == NULL) return -ENODEV; @@ -211,20 +277,86 @@ static struct xen_bus_type xenbus_backend = { }, }; + +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; + int err; - if (!drv->probe) - return -ENODEV; + 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; + if (!id) { + err = -ENODEV; + goto fail; + } - return drv->probe(dev, id); + 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 @@ static int xenbus_dev_remove(struct device *_dev) 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 @@ static int xenbus_register_driver_common(struct xenbus_driver *drv, 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) { @@ -305,6 +449,8 @@ static int cleanup_dev(struct device *dev, void *data) 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; get_device(dev); @@ -327,12 +473,15 @@ static void xenbus_cleanup_devices(const char *path, struct bus_type *bus) } 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 @@ static ssize_t xendev_show_devtype(struct device *dev, char *buf) } 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 @@ static int xenbus_probe_node(struct xen_bus_type *bus, 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 @@ static int xenbus_probe_frontend(const char *type, const char *name) if (!nodename) return -ENOMEM; + DPRINTK("%s", nodename); + err = xenbus_probe_node(&xenbus_frontend, type, nodename); kfree(nodename); return err; @@ -439,6 +612,8 @@ static int xenbus_probe_backend_unit(const char *dir, if (!nodename) return -ENOMEM; + DPRINTK("%s\n", nodename); + err = xenbus_probe_node(&xenbus_backend, type, nodename); kfree(nodename); return err; @@ -452,6 +627,8 @@ static int xenbus_probe_backend(const char *type, const char *domid) char **dir; unsigned int i, dir_n = 0; + DPRINTK(""); + nodename = kasprintf("%s/%s/%s", xenbus_backend.root, type, domid); if (!nodename) return -ENOMEM; @@ -574,12 +751,16 @@ static void dev_changed(const char *node, struct xen_bus_type *bus) 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); } @@ -600,6 +781,8 @@ static int suspend_dev(struct device *dev, void *data) struct xenbus_driver *drv; struct xenbus_device *xdev; + DPRINTK(""); + if (dev->driver == NULL) return 0; drv = to_xenbus_driver(dev->driver); @@ -607,33 +790,49 @@ static int suspend_dev(struct device *dev, void *data) 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 @@ void xenbus_resume(void) 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 @@ void xenbus_probe(void *unused) /* Notify others that xenstore is up */ notifier_call_chain(&xenstore_chain, 0, 0); - - return; } @@ -716,10 +914,10 @@ static int __init xenbus_probe_init(void) { 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 @@ static int __init xenbus_probe_init(void) /* 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 --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c index a646a3d301..36bb3d7f85 100644 --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c @@ -517,87 +517,6 @@ int xenbus_printf(struct xenbus_transaction *t, } 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 --git a/linux-2.6-xen-sparse/include/asm-xen/xenbus.h b/linux-2.6-xen-sparse/include/asm-xen/xenbus.h index 8717e4201e..ddc46e198e 100644 --- a/linux-2.6-xen-sparse/include/asm-xen/xenbus.h +++ b/linux-2.6-xen-sparse/include/asm-xen/xenbus.h @@ -35,10 +35,48 @@ #include #include +/* 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 @@ struct xenbus_driver { 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 @@ static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv) 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 @@ int xenbus_printf(struct xenbus_transaction *t, * 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); @@ -153,6 +175,89 @@ void xenbus_resume(void); #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 --git a/tools/python/xen/util/diagnose.py b/tools/python/xen/util/diagnose.py index 201c88120c..51c59fdfff 100644 --- a/tools/python/xen/util/diagnose.py +++ b/tools/python/xen/util/diagnose.py @@ -22,6 +22,7 @@ from xen.xend import sxp 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 @@ def diagnose_devices(): 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." % - (deviceClass, device)) + 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." % + 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.' % + (deviceClass, device)) + + +def stateString(state): + return state and DevController.xenbusState[int(state)] or '' def main(argv = None): diff --git a/tools/python/xen/xend/server/DevController.py b/tools/python/xen/xend/server/DevController.py index f648562c1a..3e4853fbf0 100644 --- a/tools/python/xen/xend/server/DevController.py +++ b/tools/python/xen/xend/server/DevController.py @@ -25,10 +25,23 @@ from xen.xend.XendLogging import log 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 appropriate entries in the store to trigger the creation, reconfiguration, @@ -116,10 +129,8 @@ class DevController: 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 @@ class DevController: 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 @@ class DevController: 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 --git a/tools/python/xen/xend/xenstore/xstransact.py b/tools/python/xen/xend/xenstore/xstransact.py index c1b72a970f..ac06c2b1b8 100644 --- a/tools/python/xen/xend/xenstore/xstransact.py +++ b/tools/python/xen/xend/xenstore/xstransact.py @@ -10,7 +10,7 @@ from xen.xend.xenstore.xsutil import xshandle 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 @@ class xstransact: 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 @@ class xstransact: 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 @@ class xstransact: 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 @@ class xstransact: 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 @@ class xstransact: 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