On Fri, Jul 12, 2019 at 06:06:00PM +0200, Peter Krempa wrote:
Refresh the state of the jobs and process any events that might have
happened while libvirt was not running.
The job state processing requires some care to figure out if a job
needs to be bumped.
For any invalid job try doing our best to cancel it.
Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
src/qemu/qemu_blockjob.c | 109 +++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_blockjob.h | 5 ++
src/qemu/qemu_process.c | 7 ++-
3 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c
index 8b142f1aba..360fc40e61 100644
--- a/src/qemu/qemu_blockjob.c
+++ b/src/qemu/qemu_blockjob.c
@@ -242,6 +242,115 @@ qemuBlockJobIsRunning(qemuBlockJobDataPtr job)
}
+/* returns 1 for a job we didn't reconnect to */
+static int
+qemuBlockJobRefreshJobsFindInactive(const void *payload,
+ const void *name ATTRIBUTE_UNUSED,
+ const void *data ATTRIBUTE_UNUSED)
+{
+ const qemuBlockJobData *job = payload;
+
+ return !job->reconnected;
+}
+
+
+int
+qemuBlockJobRefreshJobs(virQEMUDriverPtr driver,
+ virDomainObjPtr vm)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ qemuMonitorJobInfoPtr *jobinfo = NULL;
+ size_t njobinfo = 0;
+ qemuBlockJobDataPtr job = NULL;
+ int newstate;
+ size_t i;
+ int ret = -1;
+ int rc;
+
+ qemuDomainObjEnterMonitor(driver, vm);
+
+ rc = qemuMonitorGetJobInfo(priv->mon, &jobinfo, &njobinfo);
+
+ if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
+ goto cleanup;
+
+ for (i = 0; i < njobinfo; i++) {
+ if (!(job = virHashLookup(priv->blockjobs, jobinfo[i]->id))) {
+ VIR_DEBUG("ignoring untracked job '%s'",
jobinfo[i]->id);
+ continue;
+ }
+
+ /* try cancelling invalid jobs - this works only if the job is not
+ * concluded. In such case it will fail. We'll leave such job linger
+ * in qemu and just forget about it in libvirt because there's not much
+ * we coud do besides killing the VM */
+ if (job->invalidData) {
+ qemuDomainObjEnterMonitor(driver, vm);
+
+ rc = qemuMonitorJobCancel(priv->mon, job->name, true);
+ if (rc == -1 && jobinfo[i]->status ==
QEMU_MONITOR_JOB_STATUS_CONCLUDED)
+ VIR_WARN("can't cancel job '%s' with invalid data",
job->name);
+
+ if (qemuDomainObjExitMonitor(driver, vm) < 0)
+ goto cleanup;
+
+ if (rc < 0)
+ qemuBlockJobUnregister(job, vm);
+ job = NULL;
The value is unused after this.
+ continue;
+ }
+
+ if ((newstate = qemuBlockjobConvertMonitorStatus(jobinfo[i]->status)) < 0)
+ continue;
+
+ if (newstate != job->state) {
+ if ((job->state == QEMU_BLOCKJOB_STATE_FAILED ||
+ job->state == QEMU_BLOCKJOB_STATE_COMPLETED)) {
+ /* preserve the old state but allow the job to be bumped to
+ * execute the finishing steps */
+ job->newstate = job->state;
+ } else if (newstate == QEMU_BLOCKJOB_STATE_CONCLUDED) {
+ if (VIR_STRDUP(job->errmsg, jobinfo[i]->error) < 0)
+ goto cleanup;
+
+ if (job->errmsg)
+ job->newstate = QEMU_BLOCKJOB_STATE_FAILED;
+ else
+ job->newstate = QEMU_BLOCKJOB_STATE_COMPLETED;
+ } else if (newstate == QEMU_BLOCKJOB_STATE_READY) {
+ /* Apply _READY state only if it was not applied before */
+ if (job->state == QEMU_BLOCKJOB_STATE_NEW ||
+ job->state == QEMU_BLOCKJOB_STATE_RUNNING)
+ job->newstate = newstate;
+ }
+ /* don't update the job otherwise */
+ }
+
+ job->reconnected = true;
+
+ if (job->newstate != -1)
+ qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE);
+ job = NULL; /* job may have become invalid here */
+ }
+
+ /* remove data for job which qemu didn't report (the algorithm is
+ * inefficient, but the possibility of such jobs is very low */
+ while ((job = virHashSearch(priv->blockjobs, qemuBlockJobRefreshJobsFindInactive,
NULL, NULL))) {
+ qemuBlockJobUnregister(job, vm);
+ job = NULL;
+ }
+
+ ret = 0;
+
+ cleanup:
+ for (i = 0; i < njobinfo; i++)
+ qemuMonitorJobInfoFree(jobinfo[i]);
+ VIR_FREE(jobinfo);
+
+ return ret;
+}
+
+
/**
* qemuBlockJobEmitEvents:
*
Reviewed-by: Ján Tomko <jtomko(a)redhat.com>
Jano