--- /dev/null
+/******************************************************************************
+ * 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);
+ }
+}
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)
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,
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;
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;
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;
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
char name[TASK_COMM_LEN];
/* Not ready to connect? */
- if (!blkif->irq || !blkif->vbd.bdev)
+ if (!blkif->irq)
return;
/* Already connected? */
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)) {
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);
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;
/* We're potentially connected now */
update_blkif_status(be->blkif);
+
+ /* Add watch for cdrom media status if necessay */
+ cdrom_add_media_watch(be);
}
}
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: