]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu_hotplug: try harder to eject media
authorPavel Hrdina <phrdina@redhat.com>
Mon, 29 Jun 2015 14:19:44 +0000 (16:19 +0200)
committerPavel Hrdina <phrdina@redhat.com>
Thu, 9 Jul 2015 16:02:05 +0000 (18:02 +0200)
Some guests lock the tray and QEMU eject command will simply fail to
eject the media.  But the guest OS can handle this attempt to eject the
media and can unlock the tray and open it. In this case, we should try
again to actually eject the media.

If the first attempt fails to detect a tray_open we will fail with
error, from monitor.  If we receive that event, we know, that the guest
properly reacted to the eject request, unlocked the tray and opened it.
In this case, we need to run the command again to actually eject the
media from the device.  The reason to call it again is, that QEMU
doesn't wait for the guest to react and report an error, that the tray
is locked.

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1147471

Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
src/qemu/qemu_hotplug.c
src/qemu/qemu_process.c

index 79338cf61d17cfec48156520ca7c50c6e4c37746..1ea397f4eadf0435f5ef90a8e719c2c3419a7943 100644 (file)
@@ -59,7 +59,7 @@
 
 VIR_LOG_INIT("qemu.qemu_hotplug");
 
-#define CHANGE_MEDIA_RETRIES 10
+#define CHANGE_MEDIA_TIMEOUT 5000
 
 /* Wait up to 5 seconds for device removal to finish. */
 unsigned long long qemuDomainRemoveDeviceWaitTime = 1000ull * 5;
@@ -166,12 +166,13 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver,
                                virStorageSourcePtr newsrc,
                                bool force)
 {
-    int ret = -1;
+    int ret = -1, rc;
     char *driveAlias = NULL;
     qemuDomainObjPrivatePtr priv = vm->privateData;
-    int retries = CHANGE_MEDIA_RETRIES;
     const char *format = NULL;
     char *sourcestr = NULL;
+    bool ejectRetry = false;
+    unsigned long long now;
 
     if (!disk->info.alias) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -193,36 +194,31 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver,
     if (!(driveAlias = qemuDeviceDriveHostAlias(disk, priv->qemuCaps)))
         goto error;
 
-    qemuDomainObjEnterMonitor(driver, vm);
-    ret = qemuMonitorEjectMedia(priv->mon, driveAlias, force);
-    if (qemuDomainObjExitMonitor(driver, vm) < 0) {
-        ret = -1;
-        goto cleanup;
-    }
-
-    if (ret < 0)
-        goto error;
+    do {
+        qemuDomainObjEnterMonitor(driver, vm);
+        rc = qemuMonitorEjectMedia(priv->mon, driveAlias, force);
+        if (qemuDomainObjExitMonitor(driver, vm) < 0)
+            goto cleanup;
 
-    virObjectRef(vm);
-    /* we don't want to report errors from media tray_open polling */
-    while (retries) {
-        if (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)
-            break;
+        if (rc == -2) {
+            /* we've already tried, error out */
+            if (ejectRetry)
+                goto error;
+            VIR_DEBUG("tray is locked, wait for the guest to unlock "
+                      "the tray and try to eject it again");
+            ejectRetry = true;
+        } else if (rc < 0) {
+            goto error;
+        }
 
-        retries--;
-        virObjectUnlock(vm);
-        VIR_DEBUG("Waiting 500ms for tray to open. Retries left %d", retries);
-        usleep(500 * 1000); /* sleep 500ms */
-        virObjectLock(vm);
-    }
-    virObjectUnref(vm);
+        if (virTimeMillisNow(&now) < 0)
+            goto error;
 
-    if (retries <= 0) {
-        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
-                       _("Unable to eject media"));
-        ret = -1;
-        goto error;
-    }
+        while (disk->tray_status != VIR_DOMAIN_DISK_TRAY_OPEN) {
+            if (virDomainObjWaitUntil(vm, now + CHANGE_MEDIA_TIMEOUT) != 0)
+                goto error;
+        }
+    } while (ejectRetry && rc != 0);
 
     if (!virStorageSourceIsEmpty(newsrc)) {
         if (qemuGetDriveSourceString(newsrc, conn, &sourcestr) < 0)
@@ -237,19 +233,17 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver,
             }
         }
         qemuDomainObjEnterMonitor(driver, vm);
-        ret = qemuMonitorChangeMedia(priv->mon,
-                                     driveAlias,
-                                     sourcestr,
-                                     format);
-        if (qemuDomainObjExitMonitor(driver, vm) < 0) {
-            ret = -1;
+        rc = qemuMonitorChangeMedia(priv->mon,
+                                    driveAlias,
+                                    sourcestr,
+                                    format);
+        if (qemuDomainObjExitMonitor(driver, vm) < 0)
             goto cleanup;
-        }
     }
 
-    virDomainAuditDisk(vm, disk->src, newsrc, "update", ret >= 0);
+    virDomainAuditDisk(vm, disk->src, newsrc, "update", rc >= 0);
 
-    if (ret < 0)
+    if (rc < 0)
         goto error;
 
     /* remove the old source from shared device list */
@@ -259,6 +253,7 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver,
     virStorageSourceFree(disk->src);
     disk->src = newsrc;
     newsrc = NULL;
+    ret = 0;
 
  cleanup:
     VIR_FREE(driveAlias);
index a688feb9ba0fe1a8501e537f25983516b95d5c9c..648ba00c804e169644c2a9738b5f67427f98f2cd 100644 (file)
@@ -1152,6 +1152,8 @@ qemuProcessHandleTrayChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
             VIR_WARN("Unable to save status on vm %s after tray moved event",
                      vm->def->name);
         }
+
+        virDomainObjBroadcast(vm);
     }
 
     virObjectUnlock(vm);