Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > media > main-src > by-pkgid > d0a35cd31c1125e2132804d68547073d > files > 4285

kernel-2.6.18-194.26.1.el5.src.rpm

From: Miroslav Rezanina <mrezanin@redhat.com>
Date: Mon, 26 Oct 2009 09:49:33 -0400
Subject: [xen] cd-rom drive does not recognize new media
Message-id: <1437442573.708661256550573176.JavaMail.root@zmail06.collab.prod.int.phx2.redhat.com>
Patchwork-id: 21219
O-Subject: Re: [RHEL5.5 PATCH v4] BZ221676 - CD-ROM drive not recognize new
	media.
Bugzilla: 221676
RH-Acked-by: Christopher Lalancette <clalance@redhat.com>
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Markus Armbruster <armbru@redhat.com>

BZ 221676 (https://bugzilla.redhat.com/show_bug.cgi?id=221676) -- [RHEL5][XEN](S2) CD-ROM drive not recognize new media.

Problem:
When we use CD-ROM drive for installing software to HVM guest,
we set 2nd media into CD-ROM drive after 1st media completed.
CD-ROM drive cannot opened the new package on new media, then we can
not continue installing.

Reason:
XEN blkback driver holds open handles for each used block device.
In case of HVM physical CD-ROM, this handle is not use, as QEMU
opens handle to this device by itself. As handle opened by blkback
driver was not used, it stays opened as long as guest is opened.
This opened handle causes cdrom drive to use old disk size, as
he can change memory allocation due to non-zero use_count counter.
This fact leads to state when only first media disk_size is accessible.

Solution:
As cdrom type handle is not used by PV at all and is not used by QEMU
in case of HVM, we replace original handle with dummy copy that satisfy
blkback driver and allows us to close original one. This allows reallocate
cdrom driver buffer and use whole size of new disk.

Testing:
Use HVM xen guest with physical cdrom device used as guest cdrom device.
Run RHEL installation from cdrom in this physical drive. Succesffully
run disk test for all installation disks.

Upstream status:
This problem does not occurs in upstream. Blkback driver is not invoked
for hvm machine. Upstream source code differs too much to be able to use
their solution (new blktap2 interface, changes in xend, xenstore).

diff --git a/drivers/xen/blkback/common.h b/drivers/xen/blkback/common.h
index 785e482..a18178c 100644
--- a/drivers/xen/blkback/common.h
+++ b/drivers/xen/blkback/common.h
@@ -42,6 +42,7 @@
 #include <xen/blkif.h>
 #include <xen/gnttab.h>
 #include <xen/driver_util.h>
+#include <xen/xenbus.h>
 
 #define DPRINTK(_f, _a...)			\
 	pr_debug("(file=%s, line=%d) " _f,	\
@@ -109,7 +110,7 @@ int blkif_map(blkif_t *blkif, unsigned long shared_page, unsigned int evtchn);
 
 /* Create a vbd. */
 int vbd_create(blkif_t *blkif, blkif_vdev_t vdevice, unsigned major,
-	       unsigned minor, int readonly);
+	       unsigned minor, int readonly, struct xenbus_device *dev);
 void vbd_free(struct vbd *vbd);
 
 unsigned long long vbd_size(struct vbd *vbd);
diff --git a/drivers/xen/blkback/vbd.c b/drivers/xen/blkback/vbd.c
index c1862ce..495919a 100644
--- a/drivers/xen/blkback/vbd.c
+++ b/drivers/xen/blkback/vbd.c
@@ -36,6 +36,97 @@
 #define vbd_sz(_v)   ((_v)->bdev->bd_part ?				\
 	(_v)->bdev->bd_part->nr_sects : (_v)->bdev->bd_disk->capacity)
 
+/*
+ * Structure holding dummy device information. This structure is shared by
+ * all dummy devices as it is used only to fullfil driver requirements and
+ * is not significant for guest.
+ */
+static struct block_device dummy_dev;
+
+/*
+ * Function copies device structure taken as argument to dummy structure
+ * that is used in case we want to close original device. Function sets
+ * all pointer members to 0, except block_device.bd_disk attribute that
+ * is used by blkback driver so has to be accessible. 
+ */
+struct block_device* vbd_dev_dummy_copy(const struct block_device* bdev)
+{
+	static struct gendisk dummy_disk;
+
+	memcpy(&dummy_dev,bdev,sizeof(struct block_device));
+	memcpy(&dummy_disk,bdev->bd_disk,sizeof(struct gendisk));
+
+	dummy_dev.bd_inode = NULL;
+	dummy_dev.bd_holder = NULL;
+	dummy_dev.bd_contains = NULL;
+	dummy_dev.bd_part = NULL;
+	dummy_dev.bd_inode_backing_dev_info = NULL;
+	dummy_dev.bd_inodes.next = &dummy_dev.bd_inodes;
+	dummy_dev.bd_inodes.prev = &dummy_dev.bd_inodes;
+	dummy_dev.bd_list.next = &dummy_dev.bd_list;
+	dummy_dev.bd_list.prev = &dummy_dev.bd_list;
+#ifdef CONFIG_SYSFS
+	dummy_dev.bd_holder_list.next = &dummy_dev.bd_holder_list;
+	dummy_dev.bd_holder_list.prev = &dummy_dev.bd_holder_list;
+#endif
+	dummy_dev.bd_disk = &dummy_disk;
+	dummy_disk.part = NULL;
+	dummy_disk.fops = NULL;
+	dummy_disk.queue = NULL;
+	dummy_disk.private_data = NULL;
+	dummy_disk.driverfs_dev = NULL;
+	dummy_disk.holder_dir = NULL;
+	dummy_disk.slave_dir = NULL;
+	dummy_disk.random = NULL;
+#ifdef  CONFIG_SMP
+	dummy_disk.dkstats = NULL;
+#endif
+
+	return &dummy_dev;
+}
+
+/* 
+ * Function checks xenstore if device taken as argument is used in
+ * hvm. In that case returns 1, otherwise returns 0.
+ *
+ * For check is used /vm/<uuid>/image record. This contains string 
+ * that describes guest.
+ */
+int vbd_is_hvm(struct xenbus_device *dev)
+{
+	int id,err;
+	char buf[1024];
+	char *uuid, *image;
+	int ret = 0;
+
+	err = xenbus_scanf(XBT_NIL, dev->nodename, "frontend-id", "%d", &id);
+	if (err != 1) {
+		xenbus_dev_fatal(dev, err, "Can't read frontend-id");
+		return ret;
+	}
+
+	sprintf(buf, "/local/domain/%d", id);
+	uuid = xenbus_read(XBT_NIL, buf, "vm", NULL);
+	if (!uuid) {
+		xenbus_dev_fatal(dev, err, "Can't read domain uuid");
+		return ret;
+	}
+
+	sprintf(buf, "%s", uuid);
+	image = xenbus_read(XBT_NIL, buf, "image", NULL);
+
+	if (image && !IS_ERR(image)) {
+		if (strstr(image, "(hvm"))
+			ret = 1;
+		kfree(image); 
+	} 
+
+	kfree(uuid);
+	
+	return ret;
+}
+
+
 unsigned long long vbd_size(struct vbd *vbd)
 {
 	return vbd_sz(vbd);
@@ -52,7 +143,7 @@ unsigned long vbd_secsize(struct vbd *vbd)
 }
 
 int vbd_create(blkif_t *blkif, blkif_vdev_t handle, unsigned major,
-	       unsigned minor, int readonly)
+	       unsigned minor, int readonly, struct xenbus_device *dev)
 {
 	struct vbd *vbd;
 	struct block_device *bdev;
@@ -82,8 +173,28 @@ int vbd_create(blkif_t *blkif, blkif_vdev_t handle, unsigned major,
 		return -ENOENT;
 	}
 
-	if (vbd->bdev->bd_disk->flags & GENHD_FL_CD)
+	if (vbd->bdev->bd_disk->flags & GENHD_FL_CD) {
 		vbd->type |= VDISK_CDROM;
+/*
+ * When a blkback device is plugged in, blkback driver uses open_by_devnum 
+ * to obtain reference to a block_device structure describing device. Calling 
+ * open_by_devnum increase use counter. Cdrom driver do not reallocate read
+ * buffer after medium change until use count is set to 0. In case of HVM guest
+ * qemu opens/close its own handle from user space instead of using handle hold
+ * by blkback driver. This behavior leads to situation when use counter's
+ * minimal value is 1 and so read buffer is not reallocate and disk size of 
+ * previous medium is used. When new medium is bigger than old one, only part
+ * equal to old medium size is accessible.
+ * As blkback handle is not needed outside blkback driver and blkback driver
+ * use this handle only for updating status after plugging in, fake structure
+ * can be used to fullfil requirements of blkback driver. Original structure
+ * is returned to system so use counter is lowered by one and can be 0.
+ */
+		if (vbd_is_hvm(dev)) {
+			vbd->bdev = vbd_dev_dummy_copy(bdev);
+			blkdev_put(bdev);
+		}
+	}
 	if (vbd->bdev->bd_disk->flags & GENHD_FL_REMOVABLE)
 		vbd->type |= VDISK_REMOVABLE;
 
@@ -94,7 +205,7 @@ int vbd_create(blkif_t *blkif, blkif_vdev_t handle, unsigned major,
 
 void vbd_free(struct vbd *vbd)
 {
-	if (vbd->bdev)
+	if (vbd->bdev && vbd->bdev != &dummy_dev)
 		blkdev_put(vbd->bdev);
 	vbd->bdev = NULL;
 }
diff --git a/drivers/xen/blkback/xenbus.c b/drivers/xen/blkback/xenbus.c
index 18d5caa..49aa93f 100644
--- a/drivers/xen/blkback/xenbus.c
+++ b/drivers/xen/blkback/xenbus.c
@@ -299,7 +299,7 @@ static void backend_changed(struct xenbus_watch *watch,
 		be->minor = minor;
 
 		err = vbd_create(be->blkif, handle, major, minor,
-				 (NULL == strchr(be->mode, 'w')));
+				 (NULL == strchr(be->mode, 'w')),dev);
 		if (err) {
 			be->major = be->minor = 0;
 			xenbus_dev_fatal(dev, err, "creating vbd structure");