[libvirt] [PATCH 19/25] qemu: process: Refresh -blockdev based blockjobs on reconnect to qemu

Peter Krempa posted 25 patches 6 years, 7 months ago
[libvirt] [PATCH 19/25] qemu: process: Refresh -blockdev based blockjobs on reconnect to qemu
Posted by Peter Krempa 6 years, 7 months ago
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@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;
+            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:
  *
diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h
index a558b0a5a2..2d8ecdd4c3 100644
--- a/src/qemu/qemu_blockjob.h
+++ b/src/qemu/qemu_blockjob.h
@@ -84,6 +84,7 @@ struct _qemuBlockJobData {
     int newstate; /* qemuBlockjobState, subset of events emitted by qemu */

     bool invalidData; /* the job data (except name) is not valid */
+    bool reconnected; /* internal field for tracking whether job is live after reconnect to qemu */
 };

 int
@@ -122,6 +123,10 @@ void
 qemuBlockJobStartupFinalize(virDomainObjPtr vm,
                             qemuBlockJobDataPtr job);

+int
+qemuBlockJobRefreshJobs(virQEMUDriverPtr driver,
+                        virDomainObjPtr vm);
+
 int qemuBlockJobUpdate(virDomainObjPtr vm,
                        qemuBlockJobDataPtr job,
                        int asyncJob);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 416f4f5c9a..3afdaafb23 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -7953,7 +7953,12 @@ static int
 qemuProcessRefreshBlockjobs(virQEMUDriverPtr driver,
                             virDomainObjPtr vm)
 {
-    return qemuProcessRefreshLegacyBlockjobs(driver, vm);
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV))
+        return qemuBlockJobRefreshJobs(driver, vm);
+    else
+        return qemuProcessRefreshLegacyBlockjobs(driver, vm);
 }


-- 
2.21.0

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 19/25] qemu: process: Refresh -blockdev based blockjobs on reconnect to qemu
Posted by Ján Tomko 6 years, 6 months ago
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@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@redhat.com>

Jano
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list