patches.xen/xen-blkback-cdrom
authorAlex Bligh <alex@alex.org.uk>
Thu, 17 May 2012 19:10:03 +0000 (20:10 +0100)
committerAlex Bligh <alex@alex.org.uk>
Thu, 17 May 2012 19:10:03 +0000 (20:10 +0100)
drivers/xen/blkback/Makefile
drivers/xen/blkback/cdrom.c [new file with mode: 0644]
drivers/xen/blkback/common.h
drivers/xen/blkback/vbd.c
drivers/xen/blkback/xenbus.c

index 48e3e22..599afe4 100644 (file)
@@ -1,4 +1,4 @@
 obj-$(CONFIG_XEN_BLKDEV_BACKEND) := blkbk.o
 obj-$(CONFIG_XEN_BLKBACK_PAGEMAP) += blkback-pagemap.o
 
-blkbk-y        := blkback.o xenbus.o interface.o vbd.o
+blkbk-y        := blkback.o xenbus.o interface.o vbd.o cdrom.o
diff --git a/drivers/xen/blkback/cdrom.c b/drivers/xen/blkback/cdrom.c
new file mode 100644 (file)
index 0000000..cb1de77
--- /dev/null
@@ -0,0 +1,154 @@
+/******************************************************************************
+ * blkback/cdrom.c
+ *
+ * Routines for managing cdrom watch and media-present attribute of a
+ * cdrom type virtual block device (VBD).
+ *
+ * Copyright (c) 2003-2005, Keir Fraser & Steve Hand
+ * Copyright (c) 2007       Pat Campbell
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when 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.
+ */
+
+#include "common.h"
+
+#define MEDIA_PRESENT "media-present"
+
+static void cdrom_media_changed(struct xenbus_watch *, const char **, unsigned int);
+
+/**
+ * Writes media-present=1 attribute for the given vbd device if not
+ * already there
+ */
+static int cdrom_xenstore_write_media_present(struct backend_info *be)
+{
+       struct xenbus_device *dev = be->dev;
+       struct xenbus_transaction xbt;
+       int err;
+       int media_present;
+
+       err = xenbus_scanf(XBT_NIL, dev->nodename, MEDIA_PRESENT, "%d",
+                          &media_present);
+       if (0 < err) {
+               DPRINTK("already written err%d", err);
+               return(0);
+       }
+       media_present = !!be->blkif->vbd.bdev;
+
+again:
+       err = xenbus_transaction_start(&xbt);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "starting transaction");
+               return(-1);
+       }
+
+       err = xenbus_printf(xbt, dev->nodename, MEDIA_PRESENT, "%d", media_present );
+       if (err) {
+               xenbus_dev_fatal(dev, err, "writing %s/%s",
+                        dev->nodename, MEDIA_PRESENT);
+               goto abort;
+       }
+       err = xenbus_transaction_end(xbt, 0);
+       if (err == -EAGAIN)
+               goto again;
+       if (err)
+               xenbus_dev_fatal(dev, err, "ending transaction");
+       return 0;
+ abort:
+       xenbus_transaction_end(xbt, 1);
+       return -1;
+}
+
+/**
+ *
+ */
+static int cdrom_is_type(struct backend_info *be)
+{
+       DPRINTK("type:%x", be->blkif->vbd.type );
+       return (be->blkif->vbd.type & VDISK_CDROM)
+              && (be->blkif->vbd.type & GENHD_FL_REMOVABLE);
+}
+
+/**
+ *
+ */
+void cdrom_add_media_watch(struct backend_info *be)
+{
+       struct xenbus_device *dev = be->dev;
+       int err;
+
+       DPRINTK("nodename:%s", dev->nodename);
+       if (cdrom_is_type(be)) {
+               DPRINTK("is a cdrom");
+               if (cdrom_xenstore_write_media_present(be) == 0) {
+                       DPRINTK("xenstore wrote OK");
+                       err = xenbus_watch_path2(dev, dev->nodename, MEDIA_PRESENT,
+                                                &be->cdrom_watch,
+                                                cdrom_media_changed);
+                       if (err)
+                               DPRINTK(MEDIA_PRESENT " watch add failed");
+               }
+       }
+}
+
+/**
+ * Callback received when the MEDIA_PRESENT xenstore node is changed
+ */
+static void cdrom_media_changed(struct xenbus_watch *watch,
+                               const char **vec, unsigned int len)
+{
+       int err, media_present;
+       struct backend_info *be
+               = container_of(watch, struct backend_info, cdrom_watch);
+       struct xenbus_device *dev = be->dev;
+
+       if (!cdrom_is_type(be)) {
+               DPRINTK("callback not for a cdrom" );
+               return;
+       }
+
+       err = xenbus_scanf(XBT_NIL, dev->nodename, MEDIA_PRESENT, "%d",
+                          &media_present);
+       if (err <= 0) {
+               DPRINTK("read of " MEDIA_PRESENT " node error:%d", err);
+               return;
+       }
+
+       if (!media_present)
+               vbd_free(&be->blkif->vbd);
+       else if (!be->blkif->vbd.bdev) {
+               char *p = strrchr(dev->otherend, '/') + 1;
+               long handle = simple_strtoul(p, NULL, 0);
+
+               err = vbd_create(be->blkif, handle, be->major, be->minor,
+                                !strchr(be->mode, 'w'), 1);
+               if (err && err != -ENOMEDIUM) {
+                       be->major = be->minor = 0;
+                       xenbus_dev_fatal(dev, err, "creating vbd structure");
+                       return;
+               }
+               vbd_resize(be->blkif);
+       }
+}
index 5a530e1..2388744 100644 (file)
@@ -105,6 +105,7 @@ struct backend_info
        struct xenbus_device *dev;
        blkif_t *blkif;
        struct xenbus_watch backend_watch;
+       struct xenbus_watch cdrom_watch;
        unsigned major;
        unsigned minor;
        char *mode;
@@ -153,4 +154,7 @@ int blkback_barrier(struct xenbus_transaction xbt,
 int blkback_flush_diskcache(struct xenbus_transaction,
                            struct backend_info *, int state);
 
+/* cdrom media change */
+void cdrom_add_media_watch(struct backend_info *be);
+
 #endif /* __BLKIF__BACKEND__COMMON_H__ */
index 7b84011..7d5a53b 100644 (file)
@@ -37,7 +37,7 @@
 
 unsigned long long vbd_size(struct vbd *vbd)
 {
-       return vbd_sz(vbd);
+       return vbd->bdev ? vbd_sz(vbd) : 0;
 }
 
 unsigned int vbd_info(struct vbd *vbd)
@@ -47,7 +47,7 @@ unsigned int vbd_info(struct vbd *vbd)
 
 unsigned long vbd_secsize(struct vbd *vbd)
 {
-       return bdev_logical_block_size(vbd->bdev);
+       return vbd->bdev ? bdev_logical_block_size(vbd->bdev) : 0;
 }
 
 int vbd_create(blkif_t *blkif, blkif_vdev_t handle, unsigned major,
@@ -56,23 +56,44 @@ int vbd_create(blkif_t *blkif, blkif_vdev_t handle, unsigned major,
        struct vbd *vbd;
        struct block_device *bdev;
        struct request_queue *q;
+       fmode_t mode = FMODE_READ | (readonly ? 0 : FMODE_WRITE | FMODE_EXCL);
 
        vbd = &blkif->vbd;
        vbd->handle   = handle; 
        vbd->readonly = readonly;
-       vbd->type     = 0;
+       vbd->size     = 0;
+       vbd->type     = cdrom ? VDISK_CDROM : 0;
 
        vbd->pdevice  = MKDEV(major, minor);
 
-       bdev = blkdev_get_by_dev(vbd->pdevice,
-                                FMODE_READ | (vbd->readonly ? 0
-                                              : FMODE_WRITE | FMODE_EXCL),
-                                blkif);
+       bdev = blkdev_get_by_dev(vbd->pdevice, mode, blkif);
 
        if (IS_ERR(bdev)) {
-               DPRINTK("vbd_creat: device %08x could not be opened.\n",
+               if (PTR_ERR(bdev) != -ENOMEDIUM) {
+                       DPRINTK("vbd_creat: device %08x could not be opened\n",
+                               vbd->pdevice);
+                       return -ENOENT;
+               }
+
+               DPRINTK("vbd_creat: device %08x has no medium\n",
                        vbd->pdevice);
-               return -ENOENT;
+               if (cdrom)
+                       return -ENOMEDIUM;
+
+               bdev = blkdev_get_by_dev(vbd->pdevice, mode | FMODE_NDELAY,
+                                        blkif);
+               if (IS_ERR(bdev))
+                       return -ENOMEDIUM;
+
+               if (bdev->bd_disk) {
+                       if (bdev->bd_disk->flags & GENHD_FL_CD)
+                               vbd->type |= VDISK_CDROM;
+                       if (bdev->bd_disk->flags & GENHD_FL_REMOVABLE)
+                               vbd->type |= VDISK_REMOVABLE;
+               }
+
+               blkdev_put(bdev, mode);
+               return -ENOMEDIUM;
        }
 
        vbd->bdev = bdev;
@@ -86,7 +107,7 @@ int vbd_create(blkif_t *blkif, blkif_vdev_t handle, unsigned major,
 
        vbd->size = vbd_size(vbd);
 
-       if (vbd->bdev->bd_disk->flags & GENHD_FL_CD || cdrom)
+       if (bdev->bd_disk->flags & GENHD_FL_CD)
                vbd->type |= VDISK_CDROM;
        if (vbd->bdev->bd_disk->flags & GENHD_FL_REMOVABLE)
                vbd->type |= VDISK_REMOVABLE;
@@ -117,6 +138,11 @@ int vbd_translate(struct phys_req *req, blkif_t *blkif, int operation)
        if ((operation != READ) && vbd->readonly)
                goto out;
 
+       if (vbd->bdev == NULL) {
+               rc = -ENOMEDIUM;
+               goto out;
+       }
+
        if (likely(req->nr_sects)) {
                blkif_sector_t end = req->sector_number + req->nr_sects;
 
@@ -156,6 +182,14 @@ again:
                pr_warning("Error %d writing new size", err);
                goto abort;
        }
+
+       err = xenbus_printf(xbt, dev->nodename, "sector-size", "%lu",
+                           vbd_secsize(vbd));
+       if (err) {
+               pr_warning("Error writing new sector size");
+               goto abort;
+       }
+
        /*
         * Write the current state; we will use this to synchronize
         * the front-end. If the current state is "connected" the
index e5607e8..852fd1e 100644 (file)
@@ -58,7 +58,7 @@ static void update_blkif_status(blkif_t *blkif)
        char name[TASK_COMM_LEN];
 
        /* Not ready to connect? */
-       if (!blkif->irq || !blkif->vbd.bdev)
+       if (!blkif->irq)
                return;
 
        /* Already connected? */
@@ -76,12 +76,17 @@ static void update_blkif_status(blkif_t *blkif)
                return;
        }
 
-       err = filemap_write_and_wait(blkif->vbd.bdev->bd_inode->i_mapping);
-       if (err) {
-               xenbus_dev_error(blkif->be->dev, err, "block flush");
-               return;
+       if (blkif->vbd.bdev) {
+               struct address_space *mapping
+                       = blkif->vbd.bdev->bd_inode->i_mapping;
+
+               err = filemap_write_and_wait(mapping);
+               if (err) {
+                       xenbus_dev_error(blkif->be->dev, err, "block flush");
+                       return;
+               }
+               invalidate_inode_pages2(mapping);
        }
-       invalidate_inode_pages2(blkif->vbd.bdev->bd_inode->i_mapping);
 
        blkif->xenblkd = kthread_run(blkif_schedule, blkif, name);
        if (IS_ERR(blkif->xenblkd)) {
@@ -190,6 +195,12 @@ static int blkback_remove(struct xenbus_device *dev)
                be->backend_watch.node = NULL;
        }
 
+       if (be->cdrom_watch.node) {
+               unregister_xenbus_watch(&be->cdrom_watch);
+               kfree(be->cdrom_watch.node);
+               be->cdrom_watch.node = NULL;
+       }
+
        if (be->blkif) {
                blkif_disconnect(be->blkif);
                vbd_free(&be->blkif->vbd);
@@ -399,7 +410,13 @@ static void backend_changed(struct xenbus_watch *watch,
 
                err = vbd_create(be->blkif, handle, major, minor,
                                 (NULL == strchr(be->mode, 'w')), cdrom);
-               if (err) {
+               switch (err) {
+               case -ENOMEDIUM:
+                       if (be->blkif->vbd.type
+                           & (VDISK_CDROM | VDISK_REMOVABLE))
+               case 0:
+                               break;
+               default:
                        be->major = be->minor = 0;
                        xenbus_dev_fatal(dev, err, "creating vbd structure");
                        return;
@@ -415,6 +432,9 @@ static void backend_changed(struct xenbus_watch *watch,
 
                /* We're potentially connected now */
                update_blkif_status(be->blkif);
+
+               /* Add watch for cdrom media status if necessay */
+               cdrom_add_media_watch(be);
        }
 }
 
@@ -455,7 +475,8 @@ static void frontend_changed(struct xenbus_device *dev,
                err = connect_ring(be);
                if (err)
                        break;
-               update_blkif_status(be->blkif);
+               if (be->blkif->vbd.bdev)
+                       update_blkif_status(be->blkif);
                break;
 
        case XenbusStateClosing: