Path: blob/master/drivers/block/xen-blkback/xenbus.c
15115 views
/* Xenbus code for blkif backend1Copyright (C) 2005 Rusty Russell <[email protected]>2Copyright (C) 2005 XenSource Ltd34This program is free software; you can redistribute it and/or modify5it under the terms of the GNU General Public License as published by6the Free Software Foundation; either version 2 of the License, or7(at your option) any later version.89This program is distributed in the hope that it will be useful,10but WITHOUT ANY WARRANTY; without even the implied warranty of11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12GNU General Public License for more details.1314*/1516#include <stdarg.h>17#include <linux/module.h>18#include <linux/kthread.h>19#include <xen/events.h>20#include <xen/grant_table.h>21#include "common.h"2223struct backend_info {24struct xenbus_device *dev;25struct xen_blkif *blkif;26struct xenbus_watch backend_watch;27unsigned major;28unsigned minor;29char *mode;30};3132static struct kmem_cache *xen_blkif_cachep;33static void connect(struct backend_info *);34static int connect_ring(struct backend_info *);35static void backend_changed(struct xenbus_watch *, const char **,36unsigned int);3738struct xenbus_device *xen_blkbk_xenbus(struct backend_info *be)39{40return be->dev;41}4243static int blkback_name(struct xen_blkif *blkif, char *buf)44{45char *devpath, *devname;46struct xenbus_device *dev = blkif->be->dev;4748devpath = xenbus_read(XBT_NIL, dev->nodename, "dev", NULL);49if (IS_ERR(devpath))50return PTR_ERR(devpath);5152devname = strstr(devpath, "/dev/");53if (devname != NULL)54devname += strlen("/dev/");55else56devname = devpath;5758snprintf(buf, TASK_COMM_LEN, "blkback.%d.%s", blkif->domid, devname);59kfree(devpath);6061return 0;62}6364static void xen_update_blkif_status(struct xen_blkif *blkif)65{66int err;67char name[TASK_COMM_LEN];6869/* Not ready to connect? */70if (!blkif->irq || !blkif->vbd.bdev)71return;7273/* Already connected? */74if (blkif->be->dev->state == XenbusStateConnected)75return;7677/* Attempt to connect: exit if we fail to. */78connect(blkif->be);79if (blkif->be->dev->state != XenbusStateConnected)80return;8182err = blkback_name(blkif, name);83if (err) {84xenbus_dev_error(blkif->be->dev, err, "get blkback dev name");85return;86}8788err = filemap_write_and_wait(blkif->vbd.bdev->bd_inode->i_mapping);89if (err) {90xenbus_dev_error(blkif->be->dev, err, "block flush");91return;92}93invalidate_inode_pages2(blkif->vbd.bdev->bd_inode->i_mapping);9495blkif->xenblkd = kthread_run(xen_blkif_schedule, blkif, name);96if (IS_ERR(blkif->xenblkd)) {97err = PTR_ERR(blkif->xenblkd);98blkif->xenblkd = NULL;99xenbus_dev_error(blkif->be->dev, err, "start xenblkd");100}101}102103static struct xen_blkif *xen_blkif_alloc(domid_t domid)104{105struct xen_blkif *blkif;106107blkif = kmem_cache_alloc(xen_blkif_cachep, GFP_KERNEL);108if (!blkif)109return ERR_PTR(-ENOMEM);110111memset(blkif, 0, sizeof(*blkif));112blkif->domid = domid;113spin_lock_init(&blkif->blk_ring_lock);114atomic_set(&blkif->refcnt, 1);115init_waitqueue_head(&blkif->wq);116blkif->st_print = jiffies;117init_waitqueue_head(&blkif->waiting_to_free);118119return blkif;120}121122static int map_frontend_page(struct xen_blkif *blkif, unsigned long shared_page)123{124struct gnttab_map_grant_ref op;125126gnttab_set_map_op(&op, (unsigned long)blkif->blk_ring_area->addr,127GNTMAP_host_map, shared_page, blkif->domid);128129if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))130BUG();131132if (op.status) {133DPRINTK("Grant table operation failure !\n");134return op.status;135}136137blkif->shmem_ref = shared_page;138blkif->shmem_handle = op.handle;139140return 0;141}142143static void unmap_frontend_page(struct xen_blkif *blkif)144{145struct gnttab_unmap_grant_ref op;146147gnttab_set_unmap_op(&op, (unsigned long)blkif->blk_ring_area->addr,148GNTMAP_host_map, blkif->shmem_handle);149150if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))151BUG();152}153154static int xen_blkif_map(struct xen_blkif *blkif, unsigned long shared_page,155unsigned int evtchn)156{157int err;158159/* Already connected through? */160if (blkif->irq)161return 0;162163blkif->blk_ring_area = alloc_vm_area(PAGE_SIZE);164if (!blkif->blk_ring_area)165return -ENOMEM;166167err = map_frontend_page(blkif, shared_page);168if (err) {169free_vm_area(blkif->blk_ring_area);170return err;171}172173switch (blkif->blk_protocol) {174case BLKIF_PROTOCOL_NATIVE:175{176struct blkif_sring *sring;177sring = (struct blkif_sring *)blkif->blk_ring_area->addr;178BACK_RING_INIT(&blkif->blk_rings.native, sring, PAGE_SIZE);179break;180}181case BLKIF_PROTOCOL_X86_32:182{183struct blkif_x86_32_sring *sring_x86_32;184sring_x86_32 = (struct blkif_x86_32_sring *)blkif->blk_ring_area->addr;185BACK_RING_INIT(&blkif->blk_rings.x86_32, sring_x86_32, PAGE_SIZE);186break;187}188case BLKIF_PROTOCOL_X86_64:189{190struct blkif_x86_64_sring *sring_x86_64;191sring_x86_64 = (struct blkif_x86_64_sring *)blkif->blk_ring_area->addr;192BACK_RING_INIT(&blkif->blk_rings.x86_64, sring_x86_64, PAGE_SIZE);193break;194}195default:196BUG();197}198199err = bind_interdomain_evtchn_to_irqhandler(blkif->domid, evtchn,200xen_blkif_be_int, 0,201"blkif-backend", blkif);202if (err < 0) {203unmap_frontend_page(blkif);204free_vm_area(blkif->blk_ring_area);205blkif->blk_rings.common.sring = NULL;206return err;207}208blkif->irq = err;209210return 0;211}212213static void xen_blkif_disconnect(struct xen_blkif *blkif)214{215if (blkif->xenblkd) {216kthread_stop(blkif->xenblkd);217blkif->xenblkd = NULL;218}219220atomic_dec(&blkif->refcnt);221wait_event(blkif->waiting_to_free, atomic_read(&blkif->refcnt) == 0);222atomic_inc(&blkif->refcnt);223224if (blkif->irq) {225unbind_from_irqhandler(blkif->irq, blkif);226blkif->irq = 0;227}228229if (blkif->blk_rings.common.sring) {230unmap_frontend_page(blkif);231free_vm_area(blkif->blk_ring_area);232blkif->blk_rings.common.sring = NULL;233}234}235236void xen_blkif_free(struct xen_blkif *blkif)237{238if (!atomic_dec_and_test(&blkif->refcnt))239BUG();240kmem_cache_free(xen_blkif_cachep, blkif);241}242243int __init xen_blkif_interface_init(void)244{245xen_blkif_cachep = kmem_cache_create("blkif_cache",246sizeof(struct xen_blkif),2470, 0, NULL);248if (!xen_blkif_cachep)249return -ENOMEM;250251return 0;252}253254/*255* sysfs interface for VBD I/O requests256*/257258#define VBD_SHOW(name, format, args...) \259static ssize_t show_##name(struct device *_dev, \260struct device_attribute *attr, \261char *buf) \262{ \263struct xenbus_device *dev = to_xenbus_device(_dev); \264struct backend_info *be = dev_get_drvdata(&dev->dev); \265\266return sprintf(buf, format, ##args); \267} \268static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)269270VBD_SHOW(oo_req, "%d\n", be->blkif->st_oo_req);271VBD_SHOW(rd_req, "%d\n", be->blkif->st_rd_req);272VBD_SHOW(wr_req, "%d\n", be->blkif->st_wr_req);273VBD_SHOW(f_req, "%d\n", be->blkif->st_f_req);274VBD_SHOW(rd_sect, "%d\n", be->blkif->st_rd_sect);275VBD_SHOW(wr_sect, "%d\n", be->blkif->st_wr_sect);276277static struct attribute *xen_vbdstat_attrs[] = {278&dev_attr_oo_req.attr,279&dev_attr_rd_req.attr,280&dev_attr_wr_req.attr,281&dev_attr_f_req.attr,282&dev_attr_rd_sect.attr,283&dev_attr_wr_sect.attr,284NULL285};286287static struct attribute_group xen_vbdstat_group = {288.name = "statistics",289.attrs = xen_vbdstat_attrs,290};291292VBD_SHOW(physical_device, "%x:%x\n", be->major, be->minor);293VBD_SHOW(mode, "%s\n", be->mode);294295int xenvbd_sysfs_addif(struct xenbus_device *dev)296{297int error;298299error = device_create_file(&dev->dev, &dev_attr_physical_device);300if (error)301goto fail1;302303error = device_create_file(&dev->dev, &dev_attr_mode);304if (error)305goto fail2;306307error = sysfs_create_group(&dev->dev.kobj, &xen_vbdstat_group);308if (error)309goto fail3;310311return 0;312313fail3: sysfs_remove_group(&dev->dev.kobj, &xen_vbdstat_group);314fail2: device_remove_file(&dev->dev, &dev_attr_mode);315fail1: device_remove_file(&dev->dev, &dev_attr_physical_device);316return error;317}318319void xenvbd_sysfs_delif(struct xenbus_device *dev)320{321sysfs_remove_group(&dev->dev.kobj, &xen_vbdstat_group);322device_remove_file(&dev->dev, &dev_attr_mode);323device_remove_file(&dev->dev, &dev_attr_physical_device);324}325326327static void xen_vbd_free(struct xen_vbd *vbd)328{329if (vbd->bdev)330blkdev_put(vbd->bdev, vbd->readonly ? FMODE_READ : FMODE_WRITE);331vbd->bdev = NULL;332}333334static int xen_vbd_create(struct xen_blkif *blkif, blkif_vdev_t handle,335unsigned major, unsigned minor, int readonly,336int cdrom)337{338struct xen_vbd *vbd;339struct block_device *bdev;340struct request_queue *q;341342vbd = &blkif->vbd;343vbd->handle = handle;344vbd->readonly = readonly;345vbd->type = 0;346347vbd->pdevice = MKDEV(major, minor);348349bdev = blkdev_get_by_dev(vbd->pdevice, vbd->readonly ?350FMODE_READ : FMODE_WRITE, NULL);351352if (IS_ERR(bdev)) {353DPRINTK("xen_vbd_create: device %08x could not be opened.\n",354vbd->pdevice);355return -ENOENT;356}357358vbd->bdev = bdev;359if (vbd->bdev->bd_disk == NULL) {360DPRINTK("xen_vbd_create: device %08x doesn't exist.\n",361vbd->pdevice);362xen_vbd_free(vbd);363return -ENOENT;364}365vbd->size = vbd_sz(vbd);366367if (vbd->bdev->bd_disk->flags & GENHD_FL_CD || cdrom)368vbd->type |= VDISK_CDROM;369if (vbd->bdev->bd_disk->flags & GENHD_FL_REMOVABLE)370vbd->type |= VDISK_REMOVABLE;371372q = bdev_get_queue(bdev);373if (q && q->flush_flags)374vbd->flush_support = true;375376DPRINTK("Successful creation of handle=%04x (dom=%u)\n",377handle, blkif->domid);378return 0;379}380static int xen_blkbk_remove(struct xenbus_device *dev)381{382struct backend_info *be = dev_get_drvdata(&dev->dev);383384DPRINTK("");385386if (be->major || be->minor)387xenvbd_sysfs_delif(dev);388389if (be->backend_watch.node) {390unregister_xenbus_watch(&be->backend_watch);391kfree(be->backend_watch.node);392be->backend_watch.node = NULL;393}394395if (be->blkif) {396xen_blkif_disconnect(be->blkif);397xen_vbd_free(&be->blkif->vbd);398xen_blkif_free(be->blkif);399be->blkif = NULL;400}401402kfree(be);403dev_set_drvdata(&dev->dev, NULL);404return 0;405}406407int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt,408struct backend_info *be, int state)409{410struct xenbus_device *dev = be->dev;411int err;412413err = xenbus_printf(xbt, dev->nodename, "feature-flush-cache",414"%d", state);415if (err)416xenbus_dev_fatal(dev, err, "writing feature-flush-cache");417418return err;419}420421/*422* Entry point to this code when a new device is created. Allocate the basic423* structures, and watch the store waiting for the hotplug scripts to tell us424* the device's physical major and minor numbers. Switch to InitWait.425*/426static int xen_blkbk_probe(struct xenbus_device *dev,427const struct xenbus_device_id *id)428{429int err;430struct backend_info *be = kzalloc(sizeof(struct backend_info),431GFP_KERNEL);432if (!be) {433xenbus_dev_fatal(dev, -ENOMEM,434"allocating backend structure");435return -ENOMEM;436}437be->dev = dev;438dev_set_drvdata(&dev->dev, be);439440be->blkif = xen_blkif_alloc(dev->otherend_id);441if (IS_ERR(be->blkif)) {442err = PTR_ERR(be->blkif);443be->blkif = NULL;444xenbus_dev_fatal(dev, err, "creating block interface");445goto fail;446}447448/* setup back pointer */449be->blkif->be = be;450451err = xenbus_watch_pathfmt(dev, &be->backend_watch, backend_changed,452"%s/%s", dev->nodename, "physical-device");453if (err)454goto fail;455456err = xenbus_switch_state(dev, XenbusStateInitWait);457if (err)458goto fail;459460return 0;461462fail:463DPRINTK("failed");464xen_blkbk_remove(dev);465return err;466}467468469/*470* Callback received when the hotplug scripts have placed the physical-device471* node. Read it and the mode node, and create a vbd. If the frontend is472* ready, connect.473*/474static void backend_changed(struct xenbus_watch *watch,475const char **vec, unsigned int len)476{477int err;478unsigned major;479unsigned minor;480struct backend_info *be481= container_of(watch, struct backend_info, backend_watch);482struct xenbus_device *dev = be->dev;483int cdrom = 0;484char *device_type;485486DPRINTK("");487488err = xenbus_scanf(XBT_NIL, dev->nodename, "physical-device", "%x:%x",489&major, &minor);490if (XENBUS_EXIST_ERR(err)) {491/*492* Since this watch will fire once immediately after it is493* registered, we expect this. Ignore it, and wait for the494* hotplug scripts.495*/496return;497}498if (err != 2) {499xenbus_dev_fatal(dev, err, "reading physical-device");500return;501}502503if ((be->major || be->minor) &&504((be->major != major) || (be->minor != minor))) {505pr_warn(DRV_PFX "changing physical device (from %x:%x to %x:%x) not supported.\n",506be->major, be->minor, major, minor);507return;508}509510be->mode = xenbus_read(XBT_NIL, dev->nodename, "mode", NULL);511if (IS_ERR(be->mode)) {512err = PTR_ERR(be->mode);513be->mode = NULL;514xenbus_dev_fatal(dev, err, "reading mode");515return;516}517518device_type = xenbus_read(XBT_NIL, dev->otherend, "device-type", NULL);519if (!IS_ERR(device_type)) {520cdrom = strcmp(device_type, "cdrom") == 0;521kfree(device_type);522}523524if (be->major == 0 && be->minor == 0) {525/* Front end dir is a number, which is used as the handle. */526527char *p = strrchr(dev->otherend, '/') + 1;528long handle;529err = strict_strtoul(p, 0, &handle);530if (err)531return;532533be->major = major;534be->minor = minor;535536err = xen_vbd_create(be->blkif, handle, major, minor,537(NULL == strchr(be->mode, 'w')), cdrom);538if (err) {539be->major = 0;540be->minor = 0;541xenbus_dev_fatal(dev, err, "creating vbd structure");542return;543}544545err = xenvbd_sysfs_addif(dev);546if (err) {547xen_vbd_free(&be->blkif->vbd);548be->major = 0;549be->minor = 0;550xenbus_dev_fatal(dev, err, "creating sysfs entries");551return;552}553554/* We're potentially connected now */555xen_update_blkif_status(be->blkif);556}557}558559560/*561* Callback received when the frontend's state changes.562*/563static void frontend_changed(struct xenbus_device *dev,564enum xenbus_state frontend_state)565{566struct backend_info *be = dev_get_drvdata(&dev->dev);567int err;568569DPRINTK("%s", xenbus_strstate(frontend_state));570571switch (frontend_state) {572case XenbusStateInitialising:573if (dev->state == XenbusStateClosed) {574pr_info(DRV_PFX "%s: prepare for reconnect\n",575dev->nodename);576xenbus_switch_state(dev, XenbusStateInitWait);577}578break;579580case XenbusStateInitialised:581case XenbusStateConnected:582/*583* Ensure we connect even when two watches fire in584* close successsion and we miss the intermediate value585* of frontend_state.586*/587if (dev->state == XenbusStateConnected)588break;589590/*591* Enforce precondition before potential leak point.592* blkif_disconnect() is idempotent.593*/594xen_blkif_disconnect(be->blkif);595596err = connect_ring(be);597if (err)598break;599xen_update_blkif_status(be->blkif);600break;601602case XenbusStateClosing:603xen_blkif_disconnect(be->blkif);604xenbus_switch_state(dev, XenbusStateClosing);605break;606607case XenbusStateClosed:608xenbus_switch_state(dev, XenbusStateClosed);609if (xenbus_dev_is_online(dev))610break;611/* fall through if not online */612case XenbusStateUnknown:613/* implies blkif_disconnect() via blkback_remove() */614device_unregister(&dev->dev);615break;616617default:618xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",619frontend_state);620break;621}622}623624625/* ** Connection ** */626627628/*629* Write the physical details regarding the block device to the store, and630* switch to Connected state.631*/632static void connect(struct backend_info *be)633{634struct xenbus_transaction xbt;635int err;636struct xenbus_device *dev = be->dev;637638DPRINTK("%s", dev->otherend);639640/* Supply the information about the device the frontend needs */641again:642err = xenbus_transaction_start(&xbt);643if (err) {644xenbus_dev_fatal(dev, err, "starting transaction");645return;646}647648err = xen_blkbk_flush_diskcache(xbt, be, be->blkif->vbd.flush_support);649if (err)650goto abort;651652err = xenbus_printf(xbt, dev->nodename, "sectors", "%llu",653(unsigned long long)vbd_sz(&be->blkif->vbd));654if (err) {655xenbus_dev_fatal(dev, err, "writing %s/sectors",656dev->nodename);657goto abort;658}659660/* FIXME: use a typename instead */661err = xenbus_printf(xbt, dev->nodename, "info", "%u",662be->blkif->vbd.type |663(be->blkif->vbd.readonly ? VDISK_READONLY : 0));664if (err) {665xenbus_dev_fatal(dev, err, "writing %s/info",666dev->nodename);667goto abort;668}669err = xenbus_printf(xbt, dev->nodename, "sector-size", "%lu",670(unsigned long)671bdev_logical_block_size(be->blkif->vbd.bdev));672if (err) {673xenbus_dev_fatal(dev, err, "writing %s/sector-size",674dev->nodename);675goto abort;676}677678err = xenbus_transaction_end(xbt, 0);679if (err == -EAGAIN)680goto again;681if (err)682xenbus_dev_fatal(dev, err, "ending transaction");683684err = xenbus_switch_state(dev, XenbusStateConnected);685if (err)686xenbus_dev_fatal(dev, err, "switching to Connected state",687dev->nodename);688689return;690abort:691xenbus_transaction_end(xbt, 1);692}693694695static int connect_ring(struct backend_info *be)696{697struct xenbus_device *dev = be->dev;698unsigned long ring_ref;699unsigned int evtchn;700char protocol[64] = "";701int err;702703DPRINTK("%s", dev->otherend);704705err = xenbus_gather(XBT_NIL, dev->otherend, "ring-ref", "%lu",706&ring_ref, "event-channel", "%u", &evtchn, NULL);707if (err) {708xenbus_dev_fatal(dev, err,709"reading %s/ring-ref and event-channel",710dev->otherend);711return err;712}713714be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;715err = xenbus_gather(XBT_NIL, dev->otherend, "protocol",716"%63s", protocol, NULL);717if (err)718strcpy(protocol, "unspecified, assuming native");719else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE))720be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;721else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32))722be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32;723else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64))724be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64;725else {726xenbus_dev_fatal(dev, err, "unknown fe protocol %s", protocol);727return -1;728}729pr_info(DRV_PFX "ring-ref %ld, event-channel %d, protocol %d (%s)\n",730ring_ref, evtchn, be->blkif->blk_protocol, protocol);731732/* Map the shared frame, irq etc. */733err = xen_blkif_map(be->blkif, ring_ref, evtchn);734if (err) {735xenbus_dev_fatal(dev, err, "mapping ring-ref %lu port %u",736ring_ref, evtchn);737return err;738}739740return 0;741}742743744/* ** Driver Registration ** */745746747static const struct xenbus_device_id xen_blkbk_ids[] = {748{ "vbd" },749{ "" }750};751752753static struct xenbus_driver xen_blkbk = {754.name = "vbd",755.owner = THIS_MODULE,756.ids = xen_blkbk_ids,757.probe = xen_blkbk_probe,758.remove = xen_blkbk_remove,759.otherend_changed = frontend_changed760};761762763int xen_blkif_xenbus_init(void)764{765return xenbus_register_backend(&xen_blkbk);766}767768769