]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
qemu: blockjob: Add modern block job event handler
authorPeter Krempa <pkrempa@redhat.com>
Fri, 7 Dec 2018 09:21:22 +0000 (10:21 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Thu, 18 Jul 2019 15:59:34 +0000 (17:59 +0200)
Add the infrastructure to handle block job events in the -blockdev era.

Some complexity is required as qemu does not bother to notify whether
the job was concluded successfully or failed. Thus it's necessary to
re-query the monitor.

To minimize the possibility of stuck jobs save the state into the XML
prior to handling everything so that the reconnect code can potentially
continue with the cleanup.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
src/qemu/qemu_blockjob.c

index d9e27e8cf6c18464fb7b7e6fa3161a353883c8f4..16839192bb8c4a3837ef8b295be5efad16cc1ca5 100644 (file)
@@ -426,6 +426,168 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver,
 }
 
 
+static void
+qemuBlockJobEventProcessConcludedTransition(qemuBlockJobDataPtr job,
+                                            virQEMUDriverPtr driver,
+                                            virDomainObjPtr vm,
+                                            qemuDomainAsyncJob asyncJob ATTRIBUTE_UNUSED)
+{
+    switch ((qemuBlockjobState) job->newstate) {
+    case QEMU_BLOCKJOB_STATE_COMPLETED:
+        switch ((qemuBlockJobType) job->type) {
+        case QEMU_BLOCKJOB_TYPE_PULL:
+        case QEMU_BLOCKJOB_TYPE_COMMIT:
+        case QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT:
+        case QEMU_BLOCKJOB_TYPE_COPY:
+        case QEMU_BLOCKJOB_TYPE_NONE:
+        case QEMU_BLOCKJOB_TYPE_INTERNAL:
+        case QEMU_BLOCKJOB_TYPE_LAST:
+        default:
+            break;
+        }
+        break;
+
+    case QEMU_BLOCKJOB_STATE_FAILED:
+    case QEMU_BLOCKJOB_STATE_CANCELLED:
+        switch ((qemuBlockJobType) job->type) {
+        case QEMU_BLOCKJOB_TYPE_PULL:
+        case QEMU_BLOCKJOB_TYPE_COMMIT:
+        case QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT:
+        case QEMU_BLOCKJOB_TYPE_COPY:
+        case QEMU_BLOCKJOB_TYPE_NONE:
+        case QEMU_BLOCKJOB_TYPE_INTERNAL:
+        case QEMU_BLOCKJOB_TYPE_LAST:
+        default:
+            break;
+        }
+        break;
+
+    /* states below are impossible in this handler */
+    case QEMU_BLOCKJOB_STATE_READY:
+    case QEMU_BLOCKJOB_STATE_NEW:
+    case QEMU_BLOCKJOB_STATE_RUNNING:
+    case QEMU_BLOCKJOB_STATE_CONCLUDED:
+    case QEMU_BLOCKJOB_STATE_LAST:
+    default:
+        break;
+    }
+
+    qemuBlockJobEmitEvents(driver, vm, job->disk, job->type, job->newstate);
+    job->state = job->newstate;
+    job->newstate = -1;
+}
+
+
+static void
+qemuBlockJobEventProcessConcluded(qemuBlockJobDataPtr job,
+                                  virQEMUDriverPtr driver,
+                                  virDomainObjPtr vm,
+                                  qemuDomainAsyncJob asyncJob)
+{
+    qemuMonitorJobInfoPtr *jobinfo = NULL;
+    size_t njobinfo = 0;
+    size_t i;
+    int rc = 0;
+    bool dismissed = false;
+    bool refreshed = false;
+
+    if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
+        goto cleanup;
+
+    /* we need to fetch the error state as the event does not propagate it */
+    if (job->newstate == QEMU_BLOCKJOB_STATE_CONCLUDED &&
+        (rc = qemuMonitorGetJobInfo(qemuDomainGetMonitor(vm), &jobinfo, &njobinfo)) == 0) {
+
+        for (i = 0; i < njobinfo; i++) {
+            if (STRNEQ_NULLABLE(job->name, jobinfo[i]->id))
+                continue;
+
+            if (VIR_STRDUP(job->errmsg, jobinfo[i]->error) < 0)
+                rc = -1;
+
+            if (job->errmsg)
+                job->newstate = QEMU_BLOCKJOB_STATE_FAILED;
+            else
+                job->newstate = QEMU_BLOCKJOB_STATE_COMPLETED;
+
+            refreshed = true;
+
+            break;
+        }
+
+        if (i == njobinfo) {
+            VIR_WARN("failed to refresh job '%s'", job->name);
+            rc = -1;
+        }
+    }
+
+    /* dismiss job in qemu */
+    if (rc >= 0) {
+        if ((rc = qemuMonitorJobDismiss(qemuDomainGetMonitor(vm), job->name)) >= 0)
+            dismissed = true;
+    }
+
+    if (job->invalidData) {
+        VIR_WARN("terminating job '%s' with invalid data", job->name);
+        goto cleanup;
+    }
+
+    if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
+        goto cleanup;
+
+    if (refreshed)
+        qemuDomainSaveStatus(vm);
+
+    VIR_DEBUG("handling job '%s' state '%d' newstate '%d'", job->name, job->state, job->newstate);
+
+    qemuBlockJobEventProcessConcludedTransition(job, driver, vm, asyncJob);
+
+ cleanup:
+    if (dismissed) {
+        qemuBlockJobUnregister(job, vm);
+        qemuDomainSaveConfig(vm);
+    }
+
+    for (i = 0; i < njobinfo; i++)
+        qemuMonitorJobInfoFree(jobinfo[i]);
+    VIR_FREE(jobinfo);
+}
+
+
+static void
+qemuBlockJobEventProcess(virQEMUDriverPtr driver,
+                         virDomainObjPtr vm,
+                         qemuBlockJobDataPtr job,
+                         qemuDomainAsyncJob asyncJob)
+
+{
+    switch ((qemuBlockjobState) job->newstate) {
+    case QEMU_BLOCKJOB_STATE_COMPLETED:
+    case QEMU_BLOCKJOB_STATE_FAILED:
+    case QEMU_BLOCKJOB_STATE_CANCELLED:
+    case QEMU_BLOCKJOB_STATE_CONCLUDED:
+        qemuBlockJobEventProcessConcluded(job, driver, vm, asyncJob);
+        break;
+
+    case QEMU_BLOCKJOB_STATE_READY:
+        if (job->disk && job->disk->mirror) {
+            job->disk->mirrorState = VIR_DOMAIN_BLOCK_JOB_READY;
+            qemuBlockJobEmitEvents(driver, vm, job->disk, job->type, job->newstate);
+        }
+        job->state = job->newstate;
+        job->newstate = -1;
+        qemuDomainSaveStatus(vm);
+        break;
+
+    case QEMU_BLOCKJOB_STATE_NEW:
+    case QEMU_BLOCKJOB_STATE_RUNNING:
+    case QEMU_BLOCKJOB_STATE_LAST:
+    default:
+        job->newstate = -1;
+    }
+}
+
+
 /**
  * qemuBlockJobUpdate:
  * @vm: domain
@@ -447,7 +609,10 @@ qemuBlockJobUpdate(virDomainObjPtr vm,
     if (job->newstate == -1)
         return -1;
 
-    qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob);
+    if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV))
+        qemuBlockJobEventProcess(priv->driver, vm, job, asyncJob);
+    else
+        qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob);
 
     return job->state;
 }