[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [libvirt] [PATCH] Fix ejecting cdroms with latest qemu syntax



Daniel P. Berrange wrote:
> On Mon, Aug 25, 2008 at 01:41:24PM -0400, Cole Robinson wrote:
>> +
>> +    int idx = virDiskNameToIndex(newdisk->dst);
>> +    switch (newdisk->bus) {
>> +        /* Assume that if we are here, device targets don't exceed hypervisor
>> +         * limits, and we are already an appropriate device type */
>> +        case VIR_DOMAIN_DISK_BUS_IDE:
>> +            /* Device name of the form 'ide{0-1}-cd{0-1}' */
>> +            ret = asprintf(&devname, "ide%d-cd%d", ((idx - (idx % 2)) / 2),
>> +                                                   (idx % 2));
>> +            break;
>> +        case VIR_DOMAIN_DISK_BUS_SCSI:
>> +            /* Device name of the form 'scsi{bus#}-cd{0-6}
>> +             * Each bus holds seven devs */
>> +            ret = asprintf(&devname, "scsi%d-cd%d", ((idx - (idx % 7)) / 7),
>> +                                                    (idx % 7));
>> +            break;
>> +        case VIR_DOMAIN_DISK_BUS_FDC:
>> +            /* Device name is 'floppy{0-1}' */
>> +            ret = asprintf(&devname, "floppy%d", idx);
>> +            break;
>> +
>> +        default:
>> +            qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
>> +                             _("Cannot hotplug device with bus '%s'"),
>> +                             virDomainDiskBusTypeToString(newdisk->bus));
>> +            return -1;
>> +    }
> 
> I think this block could be re-factored a little into one or more helper
> methods, because I think we'll need to re-use this for hot-unplug of
> disks. I'd suggest a helper to turn the plain integer index into the
> (bus,device) index pair
> 
>      virDiskNameToBusDeviceIndex(virDomainDiskDefPtr disk, 
>                                  int *busIdx, 
>                                  int *devIdx);
> 
> And then a QEMU specific function
> 
>     char *virQEMUDeviceName(virDomainDiskDefPtr disk);
> 
> and have this call virDiskNameToBusDeviceIndex() and return the 'ide1-2'
> type string.
> 

Okay, second cut attached. I followed your recommendations:

virDiskNameToBusDeviceIndex added to domain_conf.c
qemudDiskDeviceName added to qemu_driver.c

I also hopefully solved the back compatibility issue. Seems
-drive was added to qemu in Dec 07 (qemu commit 3759) and the
device naming scheme was updated at the end of that month (qemu
commit 3846), so checking for -drive should be a sufficient 
indicator that we need to use the new device name format. So
if drive was detected (using the already present qemu_conf
flag), we revert to the old style cdrom/fda naming style.

I tested this on f8 using plain qemu (which lacks the -drive
option) and it appeared to work as expected for cdrom and
floppy devices.

Thanks,
Cole


diff --git a/src/domain_conf.c b/src/domain_conf.c
index dc5eb0c..95277c3 100644
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -3411,5 +3411,38 @@ char *virDomainConfigFile(virConnectPtr conn,
     return ret;
 }
 
+/* Translates a device name of the form (regex) "[fhv]d[a-z]+" into
+ * the corresponding bus,index combination (e.g. sda => (0,0), sdi (1,1),
+ *                                               hdd => (1,1), vdaa => (0,27))
+ * @param disk The disk device
+ * @param busIdx parsed bus number
+ * @param devIdx parsed device number
+ * @return 0 on success, -1 on failure
+ */
+int virDiskNameToBusDeviceIndex(virDomainDiskDefPtr disk,
+                                int *busIdx,
+                                int *devIdx) {
+
+    int idx = virDiskNameToIndex(disk->dst);
+
+    switch (disk->bus) {
+        case VIR_DOMAIN_DISK_BUS_IDE:
+            *busIdx = ((idx - (idx % 2)) / 2);
+            *devIdx = (idx % 2);
+            break;
+        case VIR_DOMAIN_DISK_BUS_SCSI:
+            *busIdx = ((idx - (idx % 7)) / 7);
+            *devIdx = (idx % 7);
+            break;
+        case VIR_DOMAIN_DISK_BUS_FDC:
+        case VIR_DOMAIN_DISK_BUS_VIRTIO:
+        default:
+            *busIdx = 0;
+            *devIdx = idx;
+            break;
+    }
+
+    return 0;
+}
 
 #endif /* ! PROXY */
diff --git a/src/domain_conf.h b/src/domain_conf.h
index cfa2a90..de8a043 100644
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -555,6 +555,10 @@ char *virDomainConfigFile(virConnectPtr conn,
                           const char *dir,
                           const char *name);
 
+int virDiskNameToBusDeviceIndex(virDomainDiskDefPtr disk,
+                                int *busIdx,
+                                int *devIdx);
+
 virDomainNetDefPtr virDomainNetDefParseXML(virConnectPtr conn,
                         xmlNodePtr node);
 
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index ad14d02..d842217 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -2943,36 +2943,134 @@ static int qemudDomainUndefine(virDomainPtr dom) {
     return 0;
 }
 
-static int qemudDomainChangeCDROM(virDomainPtr dom,
-                                  virDomainObjPtr vm,
-                                  virDomainDiskDefPtr olddisk,
-                                  virDomainDiskDefPtr newdisk) {
+/* Return the disks name for use in monitor commands */
+static char *qemudDiskDeviceName(virDomainPtr dom,
+                                 virDomainDiskDefPtr disk) {
+
+    int busid, devid;
+    int ret;
+    char *devname;
+
+    virDiskNameToBusDeviceIndex(disk, &busid, &devid);
+
+    switch (disk->bus) {
+        case VIR_DOMAIN_DISK_BUS_IDE:
+            ret = asprintf(&devname, "ide%d-cd%d", busid, devid);
+            break;
+        case VIR_DOMAIN_DISK_BUS_SCSI:
+            ret = asprintf(&devname, "scsi%d-cd%d", busid, devid);
+            break;
+        case VIR_DOMAIN_DISK_BUS_FDC:
+            ret = asprintf(&devname, "floppy%d", devid);
+            break;
+        case VIR_DOMAIN_DISK_BUS_VIRTIO:
+            ret = asprintf(&devname, "virtio%d", devid);
+            break;
+        default:
+            qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+                             _("Unknown disk name mapping for bus '%s'"),
+                             virDomainDiskBusTypeToString(disk->bus));
+            return NULL;
+    }
+
+    if (ret == -1) {
+        qemudReportError(dom->conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    return devname;
+}
+
+static int qemudDomainChangeEjectableMedia(virDomainPtr dom,
+                                           virDomainDeviceDefPtr dev)
+{
     struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(driver->domains, dom->uuid);
+    virDomainDiskDefPtr origdisk, newdisk;
     char *cmd, *reply, *safe_path;
+    char *devname = NULL;
+    int qemuCmdFlags;
+
+    if (!vm) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return -1;
+    }
+
+    newdisk = dev->data.disk;
+    origdisk = vm->def->disks;
+    while (origdisk) {
+        if (origdisk->bus == newdisk->bus &&
+            STREQ(origdisk->dst, newdisk->dst))
+            break;
+        origdisk = origdisk->next;
+    }
+
+    if (!origdisk) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("No device with bus '%s' and target '%s'"),
+                         virDomainDiskBusTypeToString(newdisk->bus),
+                         newdisk->dst);
+        return -1;
+    }
+
+    if (qemudExtractVersionInfo(vm->def->emulator,
+                                NULL,
+                                &qemuCmdFlags) < 0) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("Cannot determine QEMU argv syntax %s"),
+                         vm->def->emulator);
+        return -1;
+    }
+
+    if (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE) {
+        devname = qemudDiskDeviceName(dom, newdisk);
+    } else {
+        /* Back compat for no -drive option */
+        if (newdisk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)
+            devname = strdup(newdisk->dst);
+        else if (newdisk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
+                 STREQ(newdisk->dst, "hdc"))
+            devname = strdup("cdrom");
+        else {
+            qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                             _("Emulator version does not support removable "
+                               "media for device '%s' and target '%s'"),
+                               virDomainDiskDeviceTypeToString(newdisk->device),
+                               newdisk->dst);
+            return -1;
+        }
+    }
+
+    if (!devname) {
+        qemudReportError(dom->conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
+    }
 
     if (newdisk->src) {
         safe_path = qemudEscapeMonitorArg(newdisk->src);
         if (!safe_path) {
             qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                              "%s", _("out of memory"));
+            VIR_FREE(devname);
             return -1;
         }
-        if (asprintf (&cmd, "change %s \"%s\"",
-                      /* XXX qemu may support multiple CDROM in future */
-                      /* olddisk->dst */ "cdrom",
-                      safe_path) == -1) {
+        if (asprintf (&cmd, "change %s \"%s\"", devname, safe_path) == -1) {
             qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                              "%s", _("out of memory"));
             VIR_FREE(safe_path);
+            VIR_FREE(devname);
             return -1;
         }
         VIR_FREE(safe_path);
 
-    } else if (asprintf(&cmd, "eject cdrom") == -1) {
+    } else if (asprintf(&cmd, "eject %s", devname) == -1) {
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                          "%s", _("out of memory"));
+        VIR_FREE(devname);
         return -1;
     }
+    VIR_FREE(devname);
 
     if (qemudMonitorCommand(driver, vm, cmd, &reply) < 0) {
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
@@ -2984,7 +3082,7 @@ static int qemudDomainChangeCDROM(virDomainPtr dom,
     /* If the command failed qemu prints:
      * device not found, device is locked ...
      * No message is printed on success it seems */
-    DEBUG ("cdrom change reply: %s", reply);
+    DEBUG ("ejectable media change reply: %s", reply);
     if (strstr(reply, "\ndevice ")) {
         qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                           "%s", _("changing cdrom media failed"));
@@ -2992,49 +3090,16 @@ static int qemudDomainChangeCDROM(virDomainPtr dom,
         VIR_FREE(cmd);
         return -1;
     }
-
     VIR_FREE(reply);
     VIR_FREE(cmd);
 
-    VIR_FREE(olddisk->src);
-    olddisk->src = newdisk->src;
+    VIR_FREE(origdisk->src);
+    origdisk->src = newdisk->src;
     newdisk->src = NULL;
-    olddisk->type = newdisk->type;
+    origdisk->type = newdisk->type;
     return 0;
 }
 
-static int qemudDomainAttachCdromDevice(virDomainPtr dom,
-                                        virDomainDeviceDefPtr dev)
-{
-    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
-    virDomainObjPtr vm = virDomainFindByUUID(driver->domains, dom->uuid);
-    virDomainDiskDefPtr disk;
-
-    if (!vm) {
-        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
-                         "%s", _("no domain with matching uuid"));
-        return -1;
-    }
-
-    disk = vm->def->disks;
-    while (disk) {
-        if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
-            STREQ(disk->dst, dev->data.disk->dst))
-            break;
-        disk = disk->next;
-    }
-
-    if (!disk) {
-        qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
-                         "%s", _("CDROM not attached, cannot change media"));
-        return -1;
-    }
-
-    if (qemudDomainChangeCDROM(dom, vm, disk, dev->data.disk) < 0) {
-        return -1;
-    }
-    return 0;
-}
 
 static int qemudDomainAttachUsbMassstorageDevice(virDomainPtr dom, virDomainDeviceDefPtr dev)
 {
@@ -3186,10 +3251,11 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
     }
 
     if (dev->type == VIR_DOMAIN_DEVICE_DISK &&
-        dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
-                ret = qemudDomainAttachCdromDevice(dom, dev);
+        (dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM ||
+         dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)) {
+                ret = qemudDomainChangeEjectableMedia(dom, dev);
     } else if (dev->type == VIR_DOMAIN_DEVICE_DISK &&
-        dev->data.disk->device == VIR_DOMAIN_DEVICE_DISK &&
+        dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
         dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
                 ret = qemudDomainAttachUsbMassstorageDevice(dom, dev);
     } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]