]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: hotplug: Add support for signalling device unplug failure
authorPeter Krempa <pkrempa@redhat.com>
Mon, 4 Apr 2016 15:17:43 +0000 (17:17 +0200)
committerPeter Krempa <pkrempa@redhat.com>
Wed, 13 Apr 2016 11:26:29 +0000 (13:26 +0200)
Similarly to the DEVICE_DELETED event we will be able to tell when
unplug of certain device types will be rejected by the guest OS. Wire up
the device deletion signalling code to allow handling this.

src/qemu/qemu_domain.h
src/qemu/qemu_hotplug.c
src/qemu/qemu_hotplug.h
src/qemu/qemu_process.c

index 77b5eb5d2f224ffc1a8efaca202876eaf7870016..d8d57d32df5d90585ce76372a3ec3f3a71801597 100644 (file)
@@ -149,6 +149,22 @@ typedef void (*qemuDomainCleanupCallback)(virQEMUDriverPtr driver,
                                           virDomainObjPtr vm);
 
 # define QEMU_DOMAIN_MASTER_KEY_LEN 32  /* 32 bytes for 256 bit random key */
+
+
+/* helper data types for async device unplug */
+typedef enum {
+    QEMU_DOMAIN_UNPLUGGING_DEVICE_STATUS_NONE = 0,
+    QEMU_DOMAIN_UNPLUGGING_DEVICE_STATUS_OK,
+    QEMU_DOMAIN_UNPLUGGING_DEVICE_STATUS_GUEST_REJECTED,
+} qemuDomainUnpluggingDeviceStatus;
+
+typedef struct _qemuDomainUnpluggingDevice qemuDomainUnpluggingDevice;
+typedef qemuDomainUnpluggingDevice *qemuDomainUnpluggingDevicePtr;
+struct _qemuDomainUnpluggingDevice {
+    const char *alias;
+    qemuDomainUnpluggingDeviceStatus status;
+};
+
 typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate;
 typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr;
 struct _qemuDomainObjPrivate {
@@ -199,7 +215,8 @@ struct _qemuDomainObjPrivate {
 
     virPerfPtr perf;
 
-    const char *unpluggingDevice; /* alias of the device that is being unplugged */
+    qemuDomainUnpluggingDevice unplug;
+
     char **qemuDevices; /* NULL-terminated list of devices aliases known to QEMU */
 
     bool hookRun;  /* true if there was a hook run over this domain */
index c62b8bc8ca8ee9b613bb0f623593b4feb8008f26..b72b08018a8f1122a886e50a20f3d1beb2b92394 100644 (file)
@@ -3334,20 +3334,24 @@ qemuDomainMarkDeviceForRemoval(virDomainObjPtr vm,
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
 
-    if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_DEL_EVENT))
-        priv->unpluggingDevice = info->alias;
-    else
-        priv->unpluggingDevice = NULL;
+    memset(&priv->unplug, 0, sizeof(priv->unplug));
+
+    if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_DEL_EVENT))
+        return;
+
+    priv->unplug.alias = info->alias;
 }
 
 static void
 qemuDomainResetDeviceRemoval(virDomainObjPtr vm)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
-    priv->unpluggingDevice = NULL;
+    priv->unplug.alias = NULL;
 }
 
 /* Returns:
+ *  -1 Unplug of the device failed
+ *
  *   0 DEVICE_DELETED event is supported and removal of the device did not
  *     finish in qemuDomainRemoveDeviceWaitTime
  *
@@ -3370,17 +3374,23 @@ qemuDomainWaitForDeviceRemoval(virDomainObjPtr vm)
         return 1;
     until += qemuDomainRemoveDeviceWaitTime;
 
-    while (priv->unpluggingDevice) {
+    while (priv->unplug.alias) {
         if ((rc = virDomainObjWaitUntil(vm, until)) == 1)
             return 0;
 
         if (rc < 0) {
             VIR_WARN("Failed to wait on unplug condition for domain '%s' "
-                     "device '%s'", vm->def->name, priv->unpluggingDevice);
+                     "device '%s'", vm->def->name, priv->unplug.alias);
             return 1;
         }
     }
 
+    if (priv->unplug.status == QEMU_DOMAIN_UNPLUGGING_DEVICE_STATUS_GUEST_REJECTED) {
+        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                       _("unplug of device was rejected by the guest"));
+        return -1;
+    }
+
     return 1;
 }
 
@@ -3392,12 +3402,14 @@ qemuDomainWaitForDeviceRemoval(virDomainObjPtr vm)
  */
 bool
 qemuDomainSignalDeviceRemoval(virDomainObjPtr vm,
-                              const char *devAlias)
+                              const char *devAlias,
+                              qemuDomainUnpluggingDeviceStatus status)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
 
-    if (STREQ_NULLABLE(priv->unpluggingDevice, devAlias)) {
+    if (STREQ_NULLABLE(priv->unplug.alias, devAlias)) {
         qemuDomainResetDeviceRemoval(vm);
+        priv->unplug.status = status;
         virDomainObjBroadcast(vm);
         return true;
     }
index 4140da353969267b8062f2a9f1af93e1b95a444d..87d146e7202dc1185a807c39486f50f2ca1f7e52 100644 (file)
@@ -122,6 +122,7 @@ int qemuDomainRemoveDevice(virQEMUDriverPtr driver,
                            virDomainDeviceDefPtr dev);
 
 bool qemuDomainSignalDeviceRemoval(virDomainObjPtr vm,
-                                   const char *devAlias);
+                                   const char *devAlias,
+                                   qemuDomainUnpluggingDeviceStatus status);
 
 #endif /* __QEMU_HOTPLUG_H__ */
index f4aa409b89698af2c983fd83c64e965923d81c43..c8570c0fe412def60c9135988e78d7fce1b7d40b 100644 (file)
@@ -1359,7 +1359,8 @@ qemuProcessHandleDeviceDeleted(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
     VIR_DEBUG("Device %s removed from domain %p %s",
               devAlias, vm, vm->def->name);
 
-    if (qemuDomainSignalDeviceRemoval(vm, devAlias))
+    if (qemuDomainSignalDeviceRemoval(vm, devAlias,
+                                      QEMU_DOMAIN_UNPLUGGING_DEVICE_STATUS_OK))
         goto cleanup;
 
     if (VIR_ALLOC(processEvent) < 0)