[GSoC][PATCH] qemu_domain: Added `qemuDomainJobInfo` to domainJob's `privateData`

Prathamesh Chavan posted 1 patch 3 weeks ago
Test syntax-check failed
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/libvirt tags/patchew/20200723083126.136554-1-pc44800@gmail.com
src/qemu/qemu_backup.c           |  22 +-
src/qemu/qemu_domain.c           | 496 +++++++++++++++++++++++++++++++
src/qemu/qemu_domain.h           |  67 +++++
src/qemu/qemu_domainjob.c        | 483 +-----------------------------
src/qemu/qemu_domainjob.h        |  58 ----
src/qemu/qemu_driver.c           |  49 +--
src/qemu/qemu_migration.c        |  62 ++--
src/qemu/qemu_migration_cookie.c |   8 +-
src/qemu/qemu_process.c          |  32 +-
9 files changed, 665 insertions(+), 612 deletions(-)

[GSoC][PATCH] qemu_domain: Added `qemuDomainJobInfo` to domainJob's `privateData`

Posted by Prathamesh Chavan 3 weeks ago
As `qemuDomainJobInfo` had attributes specific to qemu hypervisor's
jobs, we added it to `privateData` structure.

Supporting functions of `qemuDomainJobInfo` were also moved from
`qemu_domainjob` to `qemu_domain`.

Functions `qemuDomainCurrentJobInfoInit` and
`qemuDomainJobInfoSetOperation` were introduced in this
process.

Signed-off-by: Prathamesh Chavan <pc44800@gmail.com>
---
 src/qemu/qemu_backup.c           |  22 +-
 src/qemu/qemu_domain.c           | 496 +++++++++++++++++++++++++++++++
 src/qemu/qemu_domain.h           |  67 +++++
 src/qemu/qemu_domainjob.c        | 483 +-----------------------------
 src/qemu/qemu_domainjob.h        |  58 ----
 src/qemu/qemu_driver.c           |  49 +--
 src/qemu/qemu_migration.c        |  62 ++--
 src/qemu/qemu_migration_cookie.c |   8 +-
 src/qemu/qemu_process.c          |  32 +-
 9 files changed, 665 insertions(+), 612 deletions(-)

diff --git a/src/qemu/qemu_backup.c b/src/qemu/qemu_backup.c
index a402730d38..1822c6f267 100644
--- a/src/qemu/qemu_backup.c
+++ b/src/qemu/qemu_backup.c
@@ -529,20 +529,21 @@ qemuBackupJobTerminate(virDomainObjPtr vm,
 
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     size_t i;
 
-    qemuDomainJobInfoUpdateTime(priv->job.current);
+    qemuDomainJobInfoUpdateTime(jobPriv->current);
 
-    g_clear_pointer(&priv->job.completed, qemuDomainJobInfoFree);
-    priv->job.completed = qemuDomainJobInfoCopy(priv->job.current);
+    g_clear_pointer(&jobPriv->completed, qemuDomainJobInfoFree);
+    jobPriv->completed = qemuDomainJobInfoCopy(jobPriv->current);
 
-    priv->job.completed->stats.backup.total = priv->backup->push_total;
-    priv->job.completed->stats.backup.transferred = priv->backup->push_transferred;
-    priv->job.completed->stats.backup.tmp_used = priv->backup->pull_tmp_used;
-    priv->job.completed->stats.backup.tmp_total = priv->backup->pull_tmp_total;
+    jobPriv->completed->stats.backup.total = priv->backup->push_total;
+    jobPriv->completed->stats.backup.transferred = priv->backup->push_transferred;
+    jobPriv->completed->stats.backup.tmp_used = priv->backup->pull_tmp_used;
+    jobPriv->completed->stats.backup.tmp_total = priv->backup->pull_tmp_total;
 
-    priv->job.completed->status = jobstatus;
-    priv->job.completed->errmsg = g_strdup(priv->backup->errmsg);
+    jobPriv->completed->status = jobstatus;
+    jobPriv->completed->errmsg = g_strdup(priv->backup->errmsg);
 
     qemuDomainEventEmitJobCompleted(priv->driver, vm);
 
@@ -694,6 +695,7 @@ qemuBackupBegin(virDomainObjPtr vm,
                 unsigned int flags)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(priv->driver);
     g_autoptr(virDomainBackupDef) def = NULL;
     g_autofree char *suffix = NULL;
@@ -745,7 +747,7 @@ qemuBackupBegin(virDomainObjPtr vm,
     qemuDomainObjSetAsyncJobMask(vm, (QEMU_JOB_DEFAULT_MASK |
                                       JOB_MASK(QEMU_JOB_SUSPEND) |
                                       JOB_MASK(QEMU_JOB_MODIFY)));
-    priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP;
+    jobPriv->current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP;
 
     if (!virDomainObjIsActive(vm)) {
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 5b22eb2eaa..e4b5e2a94a 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -89,6 +89,457 @@ VIR_ENUM_IMPL(qemuDomainNamespace,
 );
 
 
+static virDomainJobType
+qemuDomainJobStatusToType(qemuDomainJobStatus status)
+{
+    switch (status) {
+    case QEMU_DOMAIN_JOB_STATUS_NONE:
+        break;
+
+    case QEMU_DOMAIN_JOB_STATUS_ACTIVE:
+    case QEMU_DOMAIN_JOB_STATUS_MIGRATING:
+    case QEMU_DOMAIN_JOB_STATUS_QEMU_COMPLETED:
+    case QEMU_DOMAIN_JOB_STATUS_POSTCOPY:
+    case QEMU_DOMAIN_JOB_STATUS_PAUSED:
+        return VIR_DOMAIN_JOB_UNBOUNDED;
+
+    case QEMU_DOMAIN_JOB_STATUS_COMPLETED:
+        return VIR_DOMAIN_JOB_COMPLETED;
+
+    case QEMU_DOMAIN_JOB_STATUS_FAILED:
+        return VIR_DOMAIN_JOB_FAILED;
+
+    case QEMU_DOMAIN_JOB_STATUS_CANCELED:
+        return VIR_DOMAIN_JOB_CANCELLED;
+    }
+
+    return VIR_DOMAIN_JOB_NONE;
+}
+
+int
+qemuDomainJobInfoUpdateTime(qemuDomainJobInfoPtr jobInfo)
+{
+    unsigned long long now;
+
+    if (!jobInfo->started)
+        return 0;
+
+    if (virTimeMillisNow(&now) < 0)
+        return -1;
+
+    if (now < jobInfo->started) {
+        VIR_WARN("Async job starts in the future");
+        jobInfo->started = 0;
+        return 0;
+    }
+
+    jobInfo->timeElapsed = now - jobInfo->started;
+    return 0;
+}
+
+int
+qemuDomainJobInfoUpdateDowntime(qemuDomainJobInfoPtr jobInfo)
+{
+    unsigned long long now;
+
+    if (!jobInfo->stopped)
+        return 0;
+
+    if (virTimeMillisNow(&now) < 0)
+        return -1;
+
+    if (now < jobInfo->stopped) {
+        VIR_WARN("Guest's CPUs stopped in the future");
+        jobInfo->stopped = 0;
+        return 0;
+    }
+
+    jobInfo->stats.mig.downtime = now - jobInfo->stopped;
+    jobInfo->stats.mig.downtime_set = true;
+    return 0;
+}
+
+
+int
+qemuDomainJobInfoToInfo(qemuDomainJobInfoPtr jobInfo,
+                        virDomainJobInfoPtr info)
+{
+    info->type = qemuDomainJobStatusToType(jobInfo->status);
+    info->timeElapsed = jobInfo->timeElapsed;
+
+    switch (jobInfo->statsType) {
+    case QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION:
+        info->memTotal = jobInfo->stats.mig.ram_total;
+        info->memRemaining = jobInfo->stats.mig.ram_remaining;
+        info->memProcessed = jobInfo->stats.mig.ram_transferred;
+        info->fileTotal = jobInfo->stats.mig.disk_total +
+                          jobInfo->mirrorStats.total;
+        info->fileRemaining = jobInfo->stats.mig.disk_remaining +
+                              (jobInfo->mirrorStats.total -
+                               jobInfo->mirrorStats.transferred);
+        info->fileProcessed = jobInfo->stats.mig.disk_transferred +
+                              jobInfo->mirrorStats.transferred;
+        break;
+
+    case QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP:
+        info->memTotal = jobInfo->stats.mig.ram_total;
+        info->memRemaining = jobInfo->stats.mig.ram_remaining;
+        info->memProcessed = jobInfo->stats.mig.ram_transferred;
+        break;
+
+    case QEMU_DOMAIN_JOB_STATS_TYPE_MEMDUMP:
+        info->memTotal = jobInfo->stats.dump.total;
+        info->memProcessed = jobInfo->stats.dump.completed;
+        info->memRemaining = info->memTotal - info->memProcessed;
+        break;
+
+    case QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP:
+        info->fileTotal = jobInfo->stats.backup.total;
+        info->fileProcessed = jobInfo->stats.backup.transferred;
+        info->fileRemaining = info->fileTotal - info->fileProcessed;
+        break;
+
+    case QEMU_DOMAIN_JOB_STATS_TYPE_NONE:
+        break;
+    }
+
+    info->dataTotal = info->memTotal + info->fileTotal;
+    info->dataRemaining = info->memRemaining + info->fileRemaining;
+    info->dataProcessed = info->memProcessed + info->fileProcessed;
+
+    return 0;
+}
+
+
+static int
+qemuDomainMigrationJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
+                                   int *type,
+                                   virTypedParameterPtr *params,
+                                   int *nparams)
+{
+    qemuMonitorMigrationStats *stats = &jobInfo->stats.mig;
+    qemuDomainMirrorStatsPtr mirrorStats = &jobInfo->mirrorStats;
+    virTypedParameterPtr par = NULL;
+    int maxpar = 0;
+    int npar = 0;
+    unsigned long long mirrorRemaining = mirrorStats->total -
+                                         mirrorStats->transferred;
+
+    if (virTypedParamsAddInt(&par, &npar, &maxpar,
+                             VIR_DOMAIN_JOB_OPERATION,
+                             jobInfo->operation) < 0)
+        goto error;
+
+    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_TIME_ELAPSED,
+                                jobInfo->timeElapsed) < 0)
+        goto error;
+
+    if (jobInfo->timeDeltaSet &&
+        jobInfo->timeElapsed > jobInfo->timeDelta &&
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_TIME_ELAPSED_NET,
+                                jobInfo->timeElapsed - jobInfo->timeDelta) < 0)
+        goto error;
+
+    if (stats->downtime_set &&
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DOWNTIME,
+                                stats->downtime) < 0)
+        goto error;
+
+    if (stats->downtime_set &&
+        jobInfo->timeDeltaSet &&
+        stats->downtime > jobInfo->timeDelta &&
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DOWNTIME_NET,
+                                stats->downtime - jobInfo->timeDelta) < 0)
+        goto error;
+
+    if (stats->setup_time_set &&
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_SETUP_TIME,
+                                stats->setup_time) < 0)
+        goto error;
+
+    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DATA_TOTAL,
+                                stats->ram_total +
+                                stats->disk_total +
+                                mirrorStats->total) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DATA_PROCESSED,
+                                stats->ram_transferred +
+                                stats->disk_transferred +
+                                mirrorStats->transferred) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DATA_REMAINING,
+                                stats->ram_remaining +
+                                stats->disk_remaining +
+                                mirrorRemaining) < 0)
+        goto error;
+
+    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_TOTAL,
+                                stats->ram_total) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_PROCESSED,
+                                stats->ram_transferred) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_REMAINING,
+                                stats->ram_remaining) < 0)
+        goto error;
+
+    if (stats->ram_bps &&
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_BPS,
+                                stats->ram_bps) < 0)
+        goto error;
+
+    if (stats->ram_duplicate_set) {
+        if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                    VIR_DOMAIN_JOB_MEMORY_CONSTANT,
+                                    stats->ram_duplicate) < 0 ||
+            virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                    VIR_DOMAIN_JOB_MEMORY_NORMAL,
+                                    stats->ram_normal) < 0 ||
+            virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                    VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES,
+                                    stats->ram_normal_bytes) < 0)
+            goto error;
+    }
+
+    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE,
+                                stats->ram_dirty_rate) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_ITERATION,
+                                stats->ram_iteration) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_POSTCOPY_REQS,
+                                stats->ram_postcopy_reqs) < 0)
+        goto error;
+
+    if (stats->ram_page_size > 0 &&
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_PAGE_SIZE,
+                                stats->ram_page_size) < 0)
+        goto error;
+
+    /* The remaining stats are disk, mirror, or migration specific
+     * so if this is a SAVEDUMP, we can just skip them */
+    if (jobInfo->statsType == QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP)
+        goto done;
+
+    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DISK_TOTAL,
+                                stats->disk_total +
+                                mirrorStats->total) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DISK_PROCESSED,
+                                stats->disk_transferred +
+                                mirrorStats->transferred) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DISK_REMAINING,
+                                stats->disk_remaining +
+                                mirrorRemaining) < 0)
+        goto error;
+
+    if (stats->disk_bps &&
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_DISK_BPS,
+                                stats->disk_bps) < 0)
+        goto error;
+
+    if (stats->xbzrle_set) {
+        if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                    VIR_DOMAIN_JOB_COMPRESSION_CACHE,
+                                    stats->xbzrle_cache_size) < 0 ||
+            virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                    VIR_DOMAIN_JOB_COMPRESSION_BYTES,
+                                    stats->xbzrle_bytes) < 0 ||
+            virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                    VIR_DOMAIN_JOB_COMPRESSION_PAGES,
+                                    stats->xbzrle_pages) < 0 ||
+            virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                    VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES,
+                                    stats->xbzrle_cache_miss) < 0 ||
+            virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                    VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW,
+                                    stats->xbzrle_overflow) < 0)
+            goto error;
+    }
+
+    if (stats->cpu_throttle_percentage &&
+        virTypedParamsAddInt(&par, &npar, &maxpar,
+                             VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE,
+                             stats->cpu_throttle_percentage) < 0)
+        goto error;
+
+ done:
+    *type = qemuDomainJobStatusToType(jobInfo->status);
+    *params = par;
+    *nparams = npar;
+    return 0;
+
+ error:
+    virTypedParamsFree(par, npar);
+    return -1;
+}
+
+
+static int
+qemuDomainDumpJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
+                              int *type,
+                              virTypedParameterPtr *params,
+                              int *nparams)
+{
+    qemuMonitorDumpStats *stats = &jobInfo->stats.dump;
+    virTypedParameterPtr par = NULL;
+    int maxpar = 0;
+    int npar = 0;
+
+    if (virTypedParamsAddInt(&par, &npar, &maxpar,
+                             VIR_DOMAIN_JOB_OPERATION,
+                             jobInfo->operation) < 0)
+        goto error;
+
+    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_TIME_ELAPSED,
+                                jobInfo->timeElapsed) < 0)
+        goto error;
+
+    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_TOTAL,
+                                stats->total) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_PROCESSED,
+                                stats->completed) < 0 ||
+        virTypedParamsAddULLong(&par, &npar, &maxpar,
+                                VIR_DOMAIN_JOB_MEMORY_REMAINING,
+                                stats->total - stats->completed) < 0)
+        goto error;
+
+    *type = qemuDomainJobStatusToType(jobInfo->status);
+    *params = par;
+    *nparams = npar;
+    return 0;
+
+ error:
+    virTypedParamsFree(par, npar);
+    return -1;
+}
+
+
+static int
+qemuDomainBackupJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
+                                int *type,
+                                virTypedParameterPtr *params,
+                                int *nparams)
+{
+    qemuDomainBackupStats *stats = &jobInfo->stats.backup;
+    g_autoptr(virTypedParamList) par = g_new0(virTypedParamList, 1);
+
+    if (virTypedParamListAddInt(par, jobInfo->operation,
+                                VIR_DOMAIN_JOB_OPERATION) < 0)
+        return -1;
+
+    if (virTypedParamListAddULLong(par, jobInfo->timeElapsed,
+                                   VIR_DOMAIN_JOB_TIME_ELAPSED) < 0)
+        return -1;
+
+    if (stats->transferred > 0 || stats->total > 0) {
+        if (virTypedParamListAddULLong(par, stats->total,
+                                       VIR_DOMAIN_JOB_DISK_TOTAL) < 0)
+            return -1;
+
+        if (virTypedParamListAddULLong(par, stats->transferred,
+                                       VIR_DOMAIN_JOB_DISK_PROCESSED) < 0)
+            return -1;
+
+        if (virTypedParamListAddULLong(par, stats->total - stats->transferred,
+                                       VIR_DOMAIN_JOB_DISK_REMAINING) < 0)
+            return -1;
+    }
+
+    if (stats->tmp_used > 0 || stats->tmp_total > 0) {
+        if (virTypedParamListAddULLong(par, stats->tmp_used,
+                                       VIR_DOMAIN_JOB_DISK_TEMP_USED) < 0)
+            return -1;
+
+        if (virTypedParamListAddULLong(par, stats->tmp_total,
+                                       VIR_DOMAIN_JOB_DISK_TEMP_TOTAL) < 0)
+            return -1;
+    }
+
+    if (jobInfo->status != QEMU_DOMAIN_JOB_STATUS_ACTIVE &&
+        virTypedParamListAddBoolean(par,
+                                    jobInfo->status == QEMU_DOMAIN_JOB_STATUS_COMPLETED,
+                                    VIR_DOMAIN_JOB_SUCCESS) < 0)
+        return -1;
+
+    if (jobInfo->errmsg &&
+        virTypedParamListAddString(par, jobInfo->errmsg, VIR_DOMAIN_JOB_ERRMSG) < 0)
+        return -1;
+
+    *nparams = virTypedParamListStealParams(par, params);
+    *type = qemuDomainJobStatusToType(jobInfo->status);
+    return 0;
+}
+
+
+int
+qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
+                          int *type,
+                          virTypedParameterPtr *params,
+                          int *nparams)
+{
+    switch (jobInfo->statsType) {
+    case QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION:
+    case QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP:
+        return qemuDomainMigrationJobInfoToParams(jobInfo, type, params, nparams);
+
+    case QEMU_DOMAIN_JOB_STATS_TYPE_MEMDUMP:
+        return qemuDomainDumpJobInfoToParams(jobInfo, type, params, nparams);
+
+    case QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP:
+        return qemuDomainBackupJobInfoToParams(jobInfo, type, params, nparams);
+
+    case QEMU_DOMAIN_JOB_STATS_TYPE_NONE:
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("invalid job statistics type"));
+        break;
+
+    default:
+        virReportEnumRangeError(qemuDomainJobStatsType, jobInfo->statsType);
+        break;
+    }
+
+    return -1;
+}
+
+
+void
+qemuDomainJobInfoFree(qemuDomainJobInfoPtr info)
+{
+    g_free(info->errmsg);
+    g_free(info);
+}
+
+
+qemuDomainJobInfoPtr
+qemuDomainJobInfoCopy(qemuDomainJobInfoPtr info)
+{
+    qemuDomainJobInfoPtr ret = g_new0(qemuDomainJobInfo, 1);
+
+    memcpy(ret, info, sizeof(*info));
+
+    ret->errmsg = g_strdup(info->errmsg);
+
+    return ret;
+}
+
+
 static void *
 qemuJobAllocPrivate(void)
 {
@@ -105,6 +556,8 @@ qemuJobFreePrivate(void *opaque)
         return;
 
     qemuMigrationParamsFree(priv->migParams);
+    g_clear_pointer(&priv->current, qemuDomainJobInfoFree);
+    g_clear_pointer(&priv->completed, qemuDomainJobInfoFree);
     VIR_FREE(priv);
 }
 
@@ -118,6 +571,7 @@ qemuJobResetPrivate(void *opaque)
     priv->spiceMigrated = false;
     priv->dumpCompleted = false;
     qemuMigrationParamsFree(priv->migParams);
+    g_clear_pointer(&priv->current, qemuDomainJobInfoFree);
     priv->migParams = NULL;
 }
 
@@ -134,6 +588,48 @@ qemuDomainFormatJobPrivate(virBufferPtr buf,
     return 0;
 }
 
+void
+qemuDomainCurrentJobInfoInit(qemuDomainJobObjPtr job,
+                             unsigned long long now)
+{
+    qemuDomainJobPrivatePtr priv = job->privateData;
+    priv->current = g_new0(qemuDomainJobInfo, 1);
+    priv->current->status = QEMU_DOMAIN_JOB_STATUS_ACTIVE;
+    priv->current->started = now;
+
+}
+
+void
+qemuDomainJobInfoSetOperation(qemuDomainJobObjPtr job,
+                              virDomainJobOperation operation)
+{
+    qemuDomainJobPrivatePtr priv = job->privateData;
+    priv->current->operation = operation;
+}
+
+void
+qemuDomainEventEmitJobCompleted(virQEMUDriverPtr driver,
+                                virDomainObjPtr vm)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
+    virObjectEventPtr event;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    int type;
+
+    if (!jobPriv->completed)
+        return;
+
+    if (qemuDomainJobInfoToParams(jobPriv->completed, &type,
+                                  &params, &nparams) < 0) {
+        VIR_WARN("Could not get stats for completed job; domain %s",
+                 vm->def->name);
+    }
+
+    event = virDomainEventJobCompletedNewFromObj(vm, params, nparams);
+    virObjectEventStateQueue(driver->domainEventState, event);
+}
 
 static int
 qemuDomainParseJobPrivate(xmlXPathContextPtr ctxt,
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 29849a7313..90b2f7195a 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -492,6 +492,37 @@ struct _qemuDomainXmlNsDef {
     char **capsdel;
 };
 
+typedef struct _qemuDomainJobInfo qemuDomainJobInfo;
+typedef qemuDomainJobInfo *qemuDomainJobInfoPtr;
+struct _qemuDomainJobInfo {
+    qemuDomainJobStatus status;
+    virDomainJobOperation operation;
+    unsigned long long started; /* When the async job started */
+    unsigned long long stopped; /* When the domain's CPUs were stopped */
+    unsigned long long sent; /* When the source sent status info to the
+                                destination (only for migrations). */
+    unsigned long long received; /* When the destination host received status
+                                    info from the source (migrations only). */
+    /* Computed values */
+    unsigned long long timeElapsed;
+    long long timeDelta; /* delta = received - sent, i.e., the difference
+                            between the source and the destination time plus
+                            the time between the end of Perform phase on the
+                            source and the beginning of Finish phase on the
+                            destination. */
+    bool timeDeltaSet;
+    /* Raw values from QEMU */
+    qemuDomainJobStatsType statsType;
+    union {
+        qemuMonitorMigrationStats mig;
+        qemuMonitorDumpStats dump;
+        qemuDomainBackupStats backup;
+    } stats;
+    qemuDomainMirrorStats mirrorStats;
+
+    char *errmsg; /* optional error message for failed completed jobs */
+};
+
 typedef struct _qemuDomainJobPrivate qemuDomainJobPrivate;
 typedef qemuDomainJobPrivate *qemuDomainJobPrivatePtr;
 struct _qemuDomainJobPrivate {
@@ -500,8 +531,44 @@ struct _qemuDomainJobPrivate {
     bool spiceMigrated;                 /* spice migration completed */
     bool dumpCompleted;                 /* dump completed */
     qemuMigrationParamsPtr migParams;
+    qemuDomainJobInfoPtr current;       /* async job progress data */
+    qemuDomainJobInfoPtr completed;     /* statistics data of a recently completed job */
 };
 
+
+void qemuDomainEventEmitJobCompleted(virQEMUDriverPtr driver,
+                                     virDomainObjPtr vm);
+
+void
+qemuDomainJobInfoFree(qemuDomainJobInfoPtr info);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuDomainJobInfo, qemuDomainJobInfoFree);
+
+qemuDomainJobInfoPtr
+qemuDomainJobInfoCopy(qemuDomainJobInfoPtr info);
+
+void
+qemuDomainCurrentJobInfoInit(qemuDomainJobObjPtr job,
+                             unsigned long long now);
+
+void
+qemuDomainJobInfoSetOperation(qemuDomainJobObjPtr job,
+                              virDomainJobOperation operation);
+
+int qemuDomainJobInfoUpdateTime(qemuDomainJobInfoPtr jobInfo)
+    ATTRIBUTE_NONNULL(1);
+int qemuDomainJobInfoUpdateDowntime(qemuDomainJobInfoPtr jobInfo)
+    ATTRIBUTE_NONNULL(1);
+int qemuDomainJobInfoToInfo(qemuDomainJobInfoPtr jobInfo,
+                            virDomainJobInfoPtr info)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
+                              int *type,
+                              virTypedParameterPtr *params,
+                              int *nparams)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
+    ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
+
 int qemuDomainObjStartWorker(virDomainObjPtr dom);
 void qemuDomainObjStopWorker(virDomainObjPtr dom);
 
diff --git a/src/qemu/qemu_domainjob.c b/src/qemu/qemu_domainjob.c
index 6393cc0b40..1e120cc219 100644
--- a/src/qemu/qemu_domainjob.c
+++ b/src/qemu/qemu_domainjob.c
@@ -115,51 +115,6 @@ qemuDomainAsyncJobPhaseFromString(qemuDomainAsyncJob job,
         return -1;
 }
 
-
-void
-qemuDomainJobInfoFree(qemuDomainJobInfoPtr info)
-{
-    g_free(info->errmsg);
-    g_free(info);
-}
-
-
-qemuDomainJobInfoPtr
-qemuDomainJobInfoCopy(qemuDomainJobInfoPtr info)
-{
-    qemuDomainJobInfoPtr ret = g_new0(qemuDomainJobInfo, 1);
-
-    memcpy(ret, info, sizeof(*info));
-
-    ret->errmsg = g_strdup(info->errmsg);
-
-    return ret;
-}
-
-void
-qemuDomainEventEmitJobCompleted(virQEMUDriverPtr driver,
-                                virDomainObjPtr vm)
-{
-    qemuDomainObjPrivatePtr priv = vm->privateData;
-    virObjectEventPtr event;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    int type;
-
-    if (!priv->job.completed)
-        return;
-
-    if (qemuDomainJobInfoToParams(priv->job.completed, &type,
-                                  &params, &nparams) < 0) {
-        VIR_WARN("Could not get stats for completed job; domain %s",
-                 vm->def->name);
-    }
-
-    event = virDomainEventJobCompletedNewFromObj(vm, params, nparams);
-    virObjectEventStateQueue(driver->domainEventState, event);
-}
-
-
 int
 qemuDomainObjInitJob(qemuDomainJobObjPtr job,
                      qemuDomainObjPrivateJobCallbacksPtr cb)
@@ -216,7 +171,6 @@ qemuDomainObjResetAsyncJob(qemuDomainJobObjPtr job)
     job->mask = QEMU_JOB_DEFAULT_MASK;
     job->abortJob = false;
     VIR_FREE(job->error);
-    g_clear_pointer(&job->current, qemuDomainJobInfoFree);
     job->cb->resetJobPrivate(job->privateData);
     job->apiFlags = 0;
 }
@@ -251,8 +205,6 @@ qemuDomainObjFreeJob(qemuDomainJobObjPtr job)
     qemuDomainObjResetJob(job);
     qemuDomainObjResetAsyncJob(job);
     job->cb->freeJobPrivate(job->privateData);
-    g_clear_pointer(&job->current, qemuDomainJobInfoFree);
-    g_clear_pointer(&job->completed, qemuDomainJobInfoFree);
     virCondDestroy(&job->cond);
     virCondDestroy(&job->asyncCond);
 }
@@ -264,435 +216,6 @@ qemuDomainTrackJob(qemuDomainJob job)
 }
 
 
-int
-qemuDomainJobInfoUpdateTime(qemuDomainJobInfoPtr jobInfo)
-{
-    unsigned long long now;
-
-    if (!jobInfo->started)
-        return 0;
-
-    if (virTimeMillisNow(&now) < 0)
-        return -1;
-
-    if (now < jobInfo->started) {
-        VIR_WARN("Async job starts in the future");
-        jobInfo->started = 0;
-        return 0;
-    }
-
-    jobInfo->timeElapsed = now - jobInfo->started;
-    return 0;
-}
-
-int
-qemuDomainJobInfoUpdateDowntime(qemuDomainJobInfoPtr jobInfo)
-{
-    unsigned long long now;
-
-    if (!jobInfo->stopped)
-        return 0;
-
-    if (virTimeMillisNow(&now) < 0)
-        return -1;
-
-    if (now < jobInfo->stopped) {
-        VIR_WARN("Guest's CPUs stopped in the future");
-        jobInfo->stopped = 0;
-        return 0;
-    }
-
-    jobInfo->stats.mig.downtime = now - jobInfo->stopped;
-    jobInfo->stats.mig.downtime_set = true;
-    return 0;
-}
-
-static virDomainJobType
-qemuDomainJobStatusToType(qemuDomainJobStatus status)
-{
-    switch (status) {
-    case QEMU_DOMAIN_JOB_STATUS_NONE:
-        break;
-
-    case QEMU_DOMAIN_JOB_STATUS_ACTIVE:
-    case QEMU_DOMAIN_JOB_STATUS_MIGRATING:
-    case QEMU_DOMAIN_JOB_STATUS_QEMU_COMPLETED:
-    case QEMU_DOMAIN_JOB_STATUS_POSTCOPY:
-    case QEMU_DOMAIN_JOB_STATUS_PAUSED:
-        return VIR_DOMAIN_JOB_UNBOUNDED;
-
-    case QEMU_DOMAIN_JOB_STATUS_COMPLETED:
-        return VIR_DOMAIN_JOB_COMPLETED;
-
-    case QEMU_DOMAIN_JOB_STATUS_FAILED:
-        return VIR_DOMAIN_JOB_FAILED;
-
-    case QEMU_DOMAIN_JOB_STATUS_CANCELED:
-        return VIR_DOMAIN_JOB_CANCELLED;
-    }
-
-    return VIR_DOMAIN_JOB_NONE;
-}
-
-int
-qemuDomainJobInfoToInfo(qemuDomainJobInfoPtr jobInfo,
-                        virDomainJobInfoPtr info)
-{
-    info->type = qemuDomainJobStatusToType(jobInfo->status);
-    info->timeElapsed = jobInfo->timeElapsed;
-
-    switch (jobInfo->statsType) {
-    case QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION:
-        info->memTotal = jobInfo->stats.mig.ram_total;
-        info->memRemaining = jobInfo->stats.mig.ram_remaining;
-        info->memProcessed = jobInfo->stats.mig.ram_transferred;
-        info->fileTotal = jobInfo->stats.mig.disk_total +
-                          jobInfo->mirrorStats.total;
-        info->fileRemaining = jobInfo->stats.mig.disk_remaining +
-                              (jobInfo->mirrorStats.total -
-                               jobInfo->mirrorStats.transferred);
-        info->fileProcessed = jobInfo->stats.mig.disk_transferred +
-                              jobInfo->mirrorStats.transferred;
-        break;
-
-    case QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP:
-        info->memTotal = jobInfo->stats.mig.ram_total;
-        info->memRemaining = jobInfo->stats.mig.ram_remaining;
-        info->memProcessed = jobInfo->stats.mig.ram_transferred;
-        break;
-
-    case QEMU_DOMAIN_JOB_STATS_TYPE_MEMDUMP:
-        info->memTotal = jobInfo->stats.dump.total;
-        info->memProcessed = jobInfo->stats.dump.completed;
-        info->memRemaining = info->memTotal - info->memProcessed;
-        break;
-
-    case QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP:
-        info->fileTotal = jobInfo->stats.backup.total;
-        info->fileProcessed = jobInfo->stats.backup.transferred;
-        info->fileRemaining = info->fileTotal - info->fileProcessed;
-        break;
-
-    case QEMU_DOMAIN_JOB_STATS_TYPE_NONE:
-        break;
-    }
-
-    info->dataTotal = info->memTotal + info->fileTotal;
-    info->dataRemaining = info->memRemaining + info->fileRemaining;
-    info->dataProcessed = info->memProcessed + info->fileProcessed;
-
-    return 0;
-}
-
-
-static int
-qemuDomainMigrationJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
-                                   int *type,
-                                   virTypedParameterPtr *params,
-                                   int *nparams)
-{
-    qemuMonitorMigrationStats *stats = &jobInfo->stats.mig;
-    qemuDomainMirrorStatsPtr mirrorStats = &jobInfo->mirrorStats;
-    virTypedParameterPtr par = NULL;
-    int maxpar = 0;
-    int npar = 0;
-    unsigned long long mirrorRemaining = mirrorStats->total -
-                                         mirrorStats->transferred;
-
-    if (virTypedParamsAddInt(&par, &npar, &maxpar,
-                             VIR_DOMAIN_JOB_OPERATION,
-                             jobInfo->operation) < 0)
-        goto error;
-
-    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_TIME_ELAPSED,
-                                jobInfo->timeElapsed) < 0)
-        goto error;
-
-    if (jobInfo->timeDeltaSet &&
-        jobInfo->timeElapsed > jobInfo->timeDelta &&
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_TIME_ELAPSED_NET,
-                                jobInfo->timeElapsed - jobInfo->timeDelta) < 0)
-        goto error;
-
-    if (stats->downtime_set &&
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DOWNTIME,
-                                stats->downtime) < 0)
-        goto error;
-
-    if (stats->downtime_set &&
-        jobInfo->timeDeltaSet &&
-        stats->downtime > jobInfo->timeDelta &&
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DOWNTIME_NET,
-                                stats->downtime - jobInfo->timeDelta) < 0)
-        goto error;
-
-    if (stats->setup_time_set &&
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_SETUP_TIME,
-                                stats->setup_time) < 0)
-        goto error;
-
-    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DATA_TOTAL,
-                                stats->ram_total +
-                                stats->disk_total +
-                                mirrorStats->total) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DATA_PROCESSED,
-                                stats->ram_transferred +
-                                stats->disk_transferred +
-                                mirrorStats->transferred) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DATA_REMAINING,
-                                stats->ram_remaining +
-                                stats->disk_remaining +
-                                mirrorRemaining) < 0)
-        goto error;
-
-    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_TOTAL,
-                                stats->ram_total) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_PROCESSED,
-                                stats->ram_transferred) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_REMAINING,
-                                stats->ram_remaining) < 0)
-        goto error;
-
-    if (stats->ram_bps &&
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_BPS,
-                                stats->ram_bps) < 0)
-        goto error;
-
-    if (stats->ram_duplicate_set) {
-        if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                    VIR_DOMAIN_JOB_MEMORY_CONSTANT,
-                                    stats->ram_duplicate) < 0 ||
-            virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                    VIR_DOMAIN_JOB_MEMORY_NORMAL,
-                                    stats->ram_normal) < 0 ||
-            virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                    VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES,
-                                    stats->ram_normal_bytes) < 0)
-            goto error;
-    }
-
-    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE,
-                                stats->ram_dirty_rate) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_ITERATION,
-                                stats->ram_iteration) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_POSTCOPY_REQS,
-                                stats->ram_postcopy_reqs) < 0)
-        goto error;
-
-    if (stats->ram_page_size > 0 &&
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_PAGE_SIZE,
-                                stats->ram_page_size) < 0)
-        goto error;
-
-    /* The remaining stats are disk, mirror, or migration specific
-     * so if this is a SAVEDUMP, we can just skip them */
-    if (jobInfo->statsType == QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP)
-        goto done;
-
-    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DISK_TOTAL,
-                                stats->disk_total +
-                                mirrorStats->total) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DISK_PROCESSED,
-                                stats->disk_transferred +
-                                mirrorStats->transferred) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DISK_REMAINING,
-                                stats->disk_remaining +
-                                mirrorRemaining) < 0)
-        goto error;
-
-    if (stats->disk_bps &&
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_DISK_BPS,
-                                stats->disk_bps) < 0)
-        goto error;
-
-    if (stats->xbzrle_set) {
-        if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                    VIR_DOMAIN_JOB_COMPRESSION_CACHE,
-                                    stats->xbzrle_cache_size) < 0 ||
-            virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                    VIR_DOMAIN_JOB_COMPRESSION_BYTES,
-                                    stats->xbzrle_bytes) < 0 ||
-            virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                    VIR_DOMAIN_JOB_COMPRESSION_PAGES,
-                                    stats->xbzrle_pages) < 0 ||
-            virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                    VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES,
-                                    stats->xbzrle_cache_miss) < 0 ||
-            virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                    VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW,
-                                    stats->xbzrle_overflow) < 0)
-            goto error;
-    }
-
-    if (stats->cpu_throttle_percentage &&
-        virTypedParamsAddInt(&par, &npar, &maxpar,
-                             VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE,
-                             stats->cpu_throttle_percentage) < 0)
-        goto error;
-
- done:
-    *type = qemuDomainJobStatusToType(jobInfo->status);
-    *params = par;
-    *nparams = npar;
-    return 0;
-
- error:
-    virTypedParamsFree(par, npar);
-    return -1;
-}
-
-
-static int
-qemuDomainDumpJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
-                              int *type,
-                              virTypedParameterPtr *params,
-                              int *nparams)
-{
-    qemuMonitorDumpStats *stats = &jobInfo->stats.dump;
-    virTypedParameterPtr par = NULL;
-    int maxpar = 0;
-    int npar = 0;
-
-    if (virTypedParamsAddInt(&par, &npar, &maxpar,
-                             VIR_DOMAIN_JOB_OPERATION,
-                             jobInfo->operation) < 0)
-        goto error;
-
-    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_TIME_ELAPSED,
-                                jobInfo->timeElapsed) < 0)
-        goto error;
-
-    if (virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_TOTAL,
-                                stats->total) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_PROCESSED,
-                                stats->completed) < 0 ||
-        virTypedParamsAddULLong(&par, &npar, &maxpar,
-                                VIR_DOMAIN_JOB_MEMORY_REMAINING,
-                                stats->total - stats->completed) < 0)
-        goto error;
-
-    *type = qemuDomainJobStatusToType(jobInfo->status);
-    *params = par;
-    *nparams = npar;
-    return 0;
-
- error:
-    virTypedParamsFree(par, npar);
-    return -1;
-}
-
-
-static int
-qemuDomainBackupJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
-                                int *type,
-                                virTypedParameterPtr *params,
-                                int *nparams)
-{
-    qemuDomainBackupStats *stats = &jobInfo->stats.backup;
-    g_autoptr(virTypedParamList) par = g_new0(virTypedParamList, 1);
-
-    if (virTypedParamListAddInt(par, jobInfo->operation,
-                                VIR_DOMAIN_JOB_OPERATION) < 0)
-        return -1;
-
-    if (virTypedParamListAddULLong(par, jobInfo->timeElapsed,
-                                   VIR_DOMAIN_JOB_TIME_ELAPSED) < 0)
-        return -1;
-
-    if (stats->transferred > 0 || stats->total > 0) {
-        if (virTypedParamListAddULLong(par, stats->total,
-                                       VIR_DOMAIN_JOB_DISK_TOTAL) < 0)
-            return -1;
-
-        if (virTypedParamListAddULLong(par, stats->transferred,
-                                       VIR_DOMAIN_JOB_DISK_PROCESSED) < 0)
-            return -1;
-
-        if (virTypedParamListAddULLong(par, stats->total - stats->transferred,
-                                       VIR_DOMAIN_JOB_DISK_REMAINING) < 0)
-            return -1;
-    }
-
-    if (stats->tmp_used > 0 || stats->tmp_total > 0) {
-        if (virTypedParamListAddULLong(par, stats->tmp_used,
-                                       VIR_DOMAIN_JOB_DISK_TEMP_USED) < 0)
-            return -1;
-
-        if (virTypedParamListAddULLong(par, stats->tmp_total,
-                                       VIR_DOMAIN_JOB_DISK_TEMP_TOTAL) < 0)
-            return -1;
-    }
-
-    if (jobInfo->status != QEMU_DOMAIN_JOB_STATUS_ACTIVE &&
-        virTypedParamListAddBoolean(par,
-                                    jobInfo->status == QEMU_DOMAIN_JOB_STATUS_COMPLETED,
-                                    VIR_DOMAIN_JOB_SUCCESS) < 0)
-        return -1;
-
-    if (jobInfo->errmsg &&
-        virTypedParamListAddString(par, jobInfo->errmsg, VIR_DOMAIN_JOB_ERRMSG) < 0)
-        return -1;
-
-    *nparams = virTypedParamListStealParams(par, params);
-    *type = qemuDomainJobStatusToType(jobInfo->status);
-    return 0;
-}
-
-
-int
-qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
-                          int *type,
-                          virTypedParameterPtr *params,
-                          int *nparams)
-{
-    switch (jobInfo->statsType) {
-    case QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION:
-    case QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP:
-        return qemuDomainMigrationJobInfoToParams(jobInfo, type, params, nparams);
-
-    case QEMU_DOMAIN_JOB_STATS_TYPE_MEMDUMP:
-        return qemuDomainDumpJobInfoToParams(jobInfo, type, params, nparams);
-
-    case QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP:
-        return qemuDomainBackupJobInfoToParams(jobInfo, type, params, nparams);
-
-    case QEMU_DOMAIN_JOB_STATS_TYPE_NONE:
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("invalid job statistics type"));
-        break;
-
-    default:
-        virReportEnumRangeError(qemuDomainJobStatsType, jobInfo->statsType);
-        break;
-    }
-
-    return -1;
-}
-
-
 void
 qemuDomainObjSetJobPhase(virQEMUDriverPtr driver,
                          virDomainObjPtr obj,
@@ -894,13 +417,11 @@ qemuDomainObjBeginJobInternal(virQEMUDriverPtr driver,
                       qemuDomainAsyncJobTypeToString(asyncJob),
                       obj, obj->def->name);
             qemuDomainObjResetAsyncJob(&priv->job);
-            priv->job.current = g_new0(qemuDomainJobInfo, 1);
-            priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_ACTIVE;
+            qemuDomainCurrentJobInfoInit(&priv->job, now);
             priv->job.asyncJob = asyncJob;
             priv->job.asyncOwner = virThreadSelfID();
             priv->job.asyncOwnerAPI = virThreadJobGet();
             priv->job.asyncStarted = now;
-            priv->job.current->started = now;
         }
     }
 
@@ -1066,7 +587,7 @@ int qemuDomainObjBeginAsyncJob(virQEMUDriverPtr driver,
         return -1;
 
     priv = obj->privateData;
-    priv->job.current->operation = operation;
+    qemuDomainJobInfoSetOperation(&priv->job, operation);
     priv->job.apiFlags = apiFlags;
     return 0;
 }
diff --git a/src/qemu/qemu_domainjob.h b/src/qemu/qemu_domainjob.h
index c83e055647..f658a1f1d1 100644
--- a/src/qemu/qemu_domainjob.h
+++ b/src/qemu/qemu_domainjob.h
@@ -115,45 +115,6 @@ struct _qemuDomainBackupStats {
     unsigned long long tmp_total;
 };
 
-typedef struct _qemuDomainJobInfo qemuDomainJobInfo;
-typedef qemuDomainJobInfo *qemuDomainJobInfoPtr;
-struct _qemuDomainJobInfo {
-    qemuDomainJobStatus status;
-    virDomainJobOperation operation;
-    unsigned long long started; /* When the async job started */
-    unsigned long long stopped; /* When the domain's CPUs were stopped */
-    unsigned long long sent; /* When the source sent status info to the
-                                destination (only for migrations). */
-    unsigned long long received; /* When the destination host received status
-                                    info from the source (migrations only). */
-    /* Computed values */
-    unsigned long long timeElapsed;
-    long long timeDelta; /* delta = received - sent, i.e., the difference
-                            between the source and the destination time plus
-                            the time between the end of Perform phase on the
-                            source and the beginning of Finish phase on the
-                            destination. */
-    bool timeDeltaSet;
-    /* Raw values from QEMU */
-    qemuDomainJobStatsType statsType;
-    union {
-        qemuMonitorMigrationStats mig;
-        qemuMonitorDumpStats dump;
-        qemuDomainBackupStats backup;
-    } stats;
-    qemuDomainMirrorStats mirrorStats;
-
-    char *errmsg; /* optional error message for failed completed jobs */
-};
-
-void
-qemuDomainJobInfoFree(qemuDomainJobInfoPtr info);
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuDomainJobInfo, qemuDomainJobInfoFree);
-
-qemuDomainJobInfoPtr
-qemuDomainJobInfoCopy(qemuDomainJobInfoPtr info);
-
 typedef struct _qemuDomainJobObj qemuDomainJobObj;
 typedef qemuDomainJobObj *qemuDomainJobObjPtr;
 
@@ -198,8 +159,6 @@ struct _qemuDomainJobObj {
     unsigned long long asyncStarted;    /* When the current async job started */
     int phase;                          /* Job phase (mainly for migrations) */
     unsigned long long mask;            /* Jobs allowed during async job */
-    qemuDomainJobInfoPtr current;       /* async job progress data */
-    qemuDomainJobInfoPtr completed;     /* statistics data of a recently completed job */
     bool abortJob;                      /* abort of the job requested */
     char *error;                        /* job event completion error */
     unsigned long apiFlags; /* flags passed to the API which started the async job */
@@ -213,9 +172,6 @@ const char *qemuDomainAsyncJobPhaseToString(qemuDomainAsyncJob job,
 int qemuDomainAsyncJobPhaseFromString(qemuDomainAsyncJob job,
                                       const char *phase);
 
-void qemuDomainEventEmitJobCompleted(virQEMUDriverPtr driver,
-                                     virDomainObjPtr vm);
-
 int qemuDomainObjBeginJob(virQEMUDriverPtr driver,
                           virDomainObjPtr obj,
                           qemuDomainJob job)
@@ -262,20 +218,6 @@ void qemuDomainRemoveInactiveJob(virQEMUDriverPtr driver,
 void qemuDomainRemoveInactiveJobLocked(virQEMUDriverPtr driver,
                                        virDomainObjPtr vm);
 
-int qemuDomainJobInfoUpdateTime(qemuDomainJobInfoPtr jobInfo)
-    ATTRIBUTE_NONNULL(1);
-int qemuDomainJobInfoUpdateDowntime(qemuDomainJobInfoPtr jobInfo)
-    ATTRIBUTE_NONNULL(1);
-int qemuDomainJobInfoToInfo(qemuDomainJobInfoPtr jobInfo,
-                            virDomainJobInfoPtr info)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
-int qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
-                              int *type,
-                              virTypedParameterPtr *params,
-                              int *nparams)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
-    ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
-
 bool qemuDomainTrackJob(qemuDomainJob job);
 
 void qemuDomainObjFreeJob(qemuDomainJobObjPtr job);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 53980d4d78..6b0f84bf78 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -2723,6 +2723,7 @@ qemuDomainGetControlInfo(virDomainPtr dom,
 {
     virDomainObjPtr vm;
     qemuDomainObjPrivatePtr priv;
+    qemuDomainJobPrivatePtr jobPriv;
     int ret = -1;
 
     virCheckFlags(0, -1);
@@ -2737,6 +2738,7 @@ qemuDomainGetControlInfo(virDomainPtr dom,
         goto cleanup;
 
     priv = vm->privateData;
+    jobPriv = priv->job.privateData;
 
     memset(info, 0, sizeof(*info));
 
@@ -2746,9 +2748,9 @@ qemuDomainGetControlInfo(virDomainPtr dom,
     } else if (priv->job.active) {
         if (virTimeMillisNow(&info->stateTime) < 0)
             goto cleanup;
-        if (priv->job.current) {
+        if (jobPriv->current) {
             info->state = VIR_DOMAIN_CONTROL_JOB;
-            info->stateTime -= priv->job.current->started;
+            info->stateTime -= jobPriv->current->started;
         } else {
             if (priv->monStart > 0) {
                 info->state = VIR_DOMAIN_CONTROL_OCCUPIED;
@@ -3313,6 +3315,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver,
     int ret = -1;
     virObjectEventPtr event = NULL;
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     virQEMUSaveDataPtr data = NULL;
     g_autoptr(qemuDomainSaveCookie) cookie = NULL;
 
@@ -3329,7 +3332,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver,
         goto endjob;
     }
 
-    priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP;
+    jobPriv->current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP;
 
     /* Pause */
     if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
@@ -3716,7 +3719,7 @@ qemuDumpWaitForCompletion(virDomainObjPtr vm)
             return -1;
     }
 
-    if (priv->job.current->stats.dump.status == QEMU_MONITOR_DUMP_STATUS_FAILED) {
+    if (jobPriv->current->stats.dump.status == QEMU_MONITOR_DUMP_STATUS_FAILED) {
         if (priv->job.error)
             virReportError(VIR_ERR_OPERATION_FAILED,
                            _("memory-only dump failed: %s"),
@@ -3727,7 +3730,7 @@ qemuDumpWaitForCompletion(virDomainObjPtr vm)
 
         return -1;
     }
-    qemuDomainJobInfoUpdateTime(priv->job.current);
+    qemuDomainJobInfoUpdateTime(jobPriv->current);
 
     return 0;
 }
@@ -3741,6 +3744,7 @@ qemuDumpToFd(virQEMUDriverPtr driver,
              const char *dumpformat)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     bool detach = false;
     int ret = -1;
 
@@ -3756,9 +3760,9 @@ qemuDumpToFd(virQEMUDriverPtr driver,
         return -1;
 
     if (detach)
-        priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_MEMDUMP;
+        jobPriv->current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_MEMDUMP;
     else
-        g_clear_pointer(&priv->job.current, qemuDomainJobInfoFree);
+        g_clear_pointer(&jobPriv->current, qemuDomainJobInfoFree);
 
     if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
         return -1;
@@ -3895,6 +3899,7 @@ qemuDomainCoreDumpWithFormat(virDomainPtr dom,
     virQEMUDriverPtr driver = dom->conn->privateData;
     virDomainObjPtr vm;
     qemuDomainObjPrivatePtr priv = NULL;
+    qemuDomainJobPrivatePtr jobPriv;
     bool resume = false, paused = false;
     int ret = -1;
     virObjectEventPtr event = NULL;
@@ -3919,7 +3924,8 @@ qemuDomainCoreDumpWithFormat(virDomainPtr dom,
         goto endjob;
 
     priv = vm->privateData;
-    priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP;
+    jobPriv = priv->job.privateData;
+    jobPriv->current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP;
 
     /* Migrate will always stop the VM, so the resume condition is
        independent of whether the stop command is issued.  */
@@ -7481,6 +7487,7 @@ qemuDomainObjStart(virConnectPtr conn,
     bool force_boot = (flags & VIR_DOMAIN_START_FORCE_BOOT) != 0;
     unsigned int start_flags = VIR_QEMU_PROCESS_START_COLD;
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
 
     start_flags |= start_paused ? VIR_QEMU_PROCESS_START_PAUSED : 0;
     start_flags |= autodestroy ? VIR_QEMU_PROCESS_START_AUTODESTROY : 0;
@@ -7504,8 +7511,8 @@ qemuDomainObjStart(virConnectPtr conn,
             }
             vm->hasManagedSave = false;
         } else {
-            virDomainJobOperation op = priv->job.current->operation;
-            priv->job.current->operation = VIR_DOMAIN_JOB_OPERATION_RESTORE;
+            virDomainJobOperation op = jobPriv->current->operation;
+            jobPriv->current->operation = VIR_DOMAIN_JOB_OPERATION_RESTORE;
 
             ret = qemuDomainObjRestore(conn, driver, vm, managed_save,
                                        start_paused, bypass_cache, asyncJob);
@@ -7523,7 +7530,7 @@ qemuDomainObjStart(virConnectPtr conn,
                 return ret;
             } else {
                 VIR_WARN("Ignoring incomplete managed state %s", managed_save);
-                priv->job.current->operation = op;
+                jobPriv->current->operation = op;
                 vm->hasManagedSave = false;
             }
         }
@@ -13577,13 +13584,14 @@ qemuDomainGetJobStatsInternal(virQEMUDriverPtr driver,
                               qemuDomainJobInfoPtr *jobInfo)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     int ret = -1;
 
     *jobInfo = NULL;
 
     if (completed) {
-        if (priv->job.completed && !priv->job.current)
-            *jobInfo = qemuDomainJobInfoCopy(priv->job.completed);
+        if (jobPriv->completed && !jobPriv->current)
+            *jobInfo = qemuDomainJobInfoCopy(jobPriv->completed);
 
         return 0;
     }
@@ -13601,11 +13609,11 @@ qemuDomainGetJobStatsInternal(virQEMUDriverPtr driver,
     if (virDomainObjCheckActive(vm) < 0)
         goto cleanup;
 
-    if (!priv->job.current) {
+    if (!jobPriv->current) {
         ret = 0;
         goto cleanup;
     }
-    *jobInfo = qemuDomainJobInfoCopy(priv->job.current);
+    *jobInfo = qemuDomainJobInfoCopy(jobPriv->current);
 
     switch ((*jobInfo)->statsType) {
     case QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION:
@@ -13680,6 +13688,7 @@ qemuDomainGetJobStats(virDomainPtr dom,
     virQEMUDriverPtr driver = dom->conn->privateData;
     virDomainObjPtr vm;
     qemuDomainObjPrivatePtr priv;
+    qemuDomainJobPrivatePtr jobPriv;
     g_autoptr(qemuDomainJobInfo) jobInfo = NULL;
     bool completed = !!(flags & VIR_DOMAIN_JOB_STATS_COMPLETED);
     int ret = -1;
@@ -13694,6 +13703,7 @@ qemuDomainGetJobStats(virDomainPtr dom,
         goto cleanup;
 
     priv = vm->privateData;
+    jobPriv = priv->job.privateData;
     if (qemuDomainGetJobStatsInternal(driver, vm, completed, &jobInfo) < 0)
         goto cleanup;
 
@@ -13709,7 +13719,7 @@ qemuDomainGetJobStats(virDomainPtr dom,
     ret = qemuDomainJobInfoToParams(jobInfo, type, params, nparams);
 
     if (completed && ret == 0 && !(flags & VIR_DOMAIN_JOB_STATS_KEEP_COMPLETED))
-        g_clear_pointer(&priv->job.completed, qemuDomainJobInfoFree);
+        g_clear_pointer(&jobPriv->completed, qemuDomainJobInfoFree);
 
  cleanup:
     virDomainObjEndAPI(&vm);
@@ -13741,6 +13751,7 @@ static int qemuDomainAbortJob(virDomainPtr dom)
     virDomainObjPtr vm;
     int ret = -1;
     qemuDomainObjPrivatePtr priv;
+    qemuDomainJobPrivatePtr jobPriv;
     int reason;
 
     if (!(vm = qemuDomainObjFromDomain(dom)))
@@ -13756,6 +13767,7 @@ static int qemuDomainAbortJob(virDomainPtr dom)
         goto endjob;
 
     priv = vm->privateData;
+    jobPriv = priv->job.privateData;
 
     switch (priv->job.asyncJob) {
     case QEMU_ASYNC_JOB_NONE:
@@ -13776,7 +13788,7 @@ static int qemuDomainAbortJob(virDomainPtr dom)
         break;
 
     case QEMU_ASYNC_JOB_MIGRATION_OUT:
-        if ((priv->job.current->status == QEMU_DOMAIN_JOB_STATUS_POSTCOPY ||
+        if ((jobPriv->current->status == QEMU_DOMAIN_JOB_STATUS_POSTCOPY ||
              (virDomainObjGetState(vm, &reason) == VIR_DOMAIN_PAUSED &&
               reason == VIR_DOMAIN_PAUSED_POSTCOPY))) {
             virReportError(VIR_ERR_OPERATION_INVALID, "%s",
@@ -15448,6 +15460,7 @@ qemuDomainSnapshotCreateActiveExternal(virQEMUDriverPtr driver,
     bool resume = false;
     int ret = -1;
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     g_autofree char *xml = NULL;
     virDomainSnapshotDefPtr snapdef = virDomainSnapshotObjGetDef(snap);
     bool memory = snapdef->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
@@ -15517,7 +15530,7 @@ qemuDomainSnapshotCreateActiveExternal(virQEMUDriverPtr driver,
         if (!qemuMigrationSrcIsAllowed(driver, vm, false, 0))
             goto cleanup;
 
-        priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP;
+        jobPriv->current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP;
 
         /* allow the migration job to be cancelled or the domain to be paused */
         qemuDomainObjSetAsyncJobMask(vm, (QEMU_JOB_DEFAULT_MASK |
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 2c7bf349c3..b8bb02435f 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1008,6 +1008,7 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver,
                                unsigned int flags)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     int port;
     size_t i;
     unsigned long long mirror_speed = speed;
@@ -1052,7 +1053,7 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver,
             return -1;
 
         if (priv->job.abortJob) {
-            priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_CANCELED;
+            jobPriv->current->status = QEMU_DOMAIN_JOB_STATUS_CANCELED;
             virReportError(VIR_ERR_OPERATION_ABORTED, _("%s: %s"),
                            qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
                            _("canceled by client"));
@@ -1070,7 +1071,7 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver,
     }
 
     qemuMigrationSrcFetchMirrorStats(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT,
-                                     priv->job.current);
+                                     jobPriv->current);
 
     /* Okay, all disks are ready. Modify migrate_flags */
     *migrate_flags &= ~(QEMU_MONITOR_MIGRATE_NON_SHARED_DISK |
@@ -1538,7 +1539,8 @@ qemuMigrationJobCheckStatus(virQEMUDriverPtr driver,
                             qemuDomainAsyncJob asyncJob)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
-    qemuDomainJobInfoPtr jobInfo = priv->job.current;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
+    qemuDomainJobInfoPtr jobInfo = jobPriv->current;
     char *error = NULL;
     bool events = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT);
     int ret = -1;
@@ -1608,7 +1610,8 @@ qemuMigrationAnyCompleted(virQEMUDriverPtr driver,
                           unsigned int flags)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
-    qemuDomainJobInfoPtr jobInfo = priv->job.current;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
+    qemuDomainJobInfoPtr jobInfo = jobPriv->current;
     int pauseReason;
 
     if (qemuMigrationJobCheckStatus(driver, vm, asyncJob) < 0)
@@ -1699,7 +1702,8 @@ qemuMigrationSrcWaitForCompletion(virQEMUDriverPtr driver,
                                   unsigned int flags)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
-    qemuDomainJobInfoPtr jobInfo = priv->job.current;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
+    qemuDomainJobInfoPtr jobInfo = jobPriv->current;
     bool events = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT);
     int rv;
 
@@ -1731,9 +1735,9 @@ qemuMigrationSrcWaitForCompletion(virQEMUDriverPtr driver,
 
     qemuDomainJobInfoUpdateTime(jobInfo);
     qemuDomainJobInfoUpdateDowntime(jobInfo);
-    g_clear_pointer(&priv->job.completed, qemuDomainJobInfoFree);
-    priv->job.completed = qemuDomainJobInfoCopy(jobInfo);
-    priv->job.completed->status = QEMU_DOMAIN_JOB_STATUS_COMPLETED;
+    g_clear_pointer(&jobPriv->completed, qemuDomainJobInfoFree);
+    jobPriv->completed = qemuDomainJobInfoCopy(jobInfo);
+    jobPriv->completed->status = QEMU_DOMAIN_JOB_STATUS_COMPLETED;
 
     if (asyncJob != QEMU_ASYNC_JOB_MIGRATION_OUT &&
         jobInfo->status == QEMU_DOMAIN_JOB_STATUS_QEMU_COMPLETED)
@@ -3006,16 +3010,16 @@ qemuMigrationSrcConfirmPhase(virQEMUDriverPtr driver,
         return -1;
 
     if (retcode == 0)
-        jobInfo = priv->job.completed;
+        jobInfo = jobPriv->completed;
     else
-        g_clear_pointer(&priv->job.completed, qemuDomainJobInfoFree);
+        g_clear_pointer(&jobPriv->completed, qemuDomainJobInfoFree);
 
     /* Update times with the values sent by the destination daemon */
     if (mig->jobInfo && jobInfo) {
         int reason;
 
         /* We need to refresh migration statistics after a completed post-copy
-         * migration since priv->job.completed contains obsolete data from the
+         * migration since jobPriv->completed contains obsolete data from the
          * time we switched to post-copy mode.
          */
         if (virDomainObjGetState(vm, &reason) == VIR_DOMAIN_PAUSED &&
@@ -3467,6 +3471,7 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver,
     int ret = -1;
     unsigned int migrate_flags = QEMU_MONITOR_MIGRATE_BACKGROUND;
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     g_autoptr(qemuMigrationCookie) mig = NULL;
     g_autofree char *tlsAlias = NULL;
     qemuMigrationIOThreadPtr iothread = NULL;
@@ -3624,7 +3629,7 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver,
         /* explicitly do this *after* we entered the monitor,
          * as this is a critical section so we are guaranteed
          * priv->job.abortJob will not change */
-        priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_CANCELED;
+        jobPriv->current->status = QEMU_DOMAIN_JOB_STATUS_CANCELED;
         virReportError(VIR_ERR_OPERATION_ABORTED, _("%s: %s"),
                        qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
                        _("canceled by client"));
@@ -3729,7 +3734,7 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver,
      * resume it now once we finished all block jobs and wait for the real
      * end of the migration.
      */
-    if (priv->job.current->status == QEMU_DOMAIN_JOB_STATUS_PAUSED) {
+    if (jobPriv->current->status == QEMU_DOMAIN_JOB_STATUS_PAUSED) {
         if (qemuMigrationSrcContinue(driver, vm,
                                      QEMU_MONITOR_MIGRATION_STATUS_PRE_SWITCHOVER,
                                      QEMU_ASYNC_JOB_MIGRATION_OUT) < 0)
@@ -3757,11 +3762,11 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver,
             goto error;
     }
 
-    if (priv->job.completed) {
-        priv->job.completed->stopped = priv->job.current->stopped;
-        qemuDomainJobInfoUpdateTime(priv->job.completed);
-        qemuDomainJobInfoUpdateDowntime(priv->job.completed);
-        ignore_value(virTimeMillisNow(&priv->job.completed->sent));
+    if (jobPriv->completed) {
+        jobPriv->completed->stopped = jobPriv->current->stopped;
+        qemuDomainJobInfoUpdateTime(jobPriv->completed);
+        qemuDomainJobInfoUpdateDowntime(jobPriv->completed);
+        ignore_value(virTimeMillisNow(&jobPriv->completed->sent));
     }
 
     cookieFlags |= QEMU_MIGRATION_COOKIE_NETWORK |
@@ -3789,7 +3794,7 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver,
 
     if (virDomainObjIsActive(vm)) {
         if (cancel &&
-            priv->job.current->status != QEMU_DOMAIN_JOB_STATUS_QEMU_COMPLETED &&
+            jobPriv->current->status != QEMU_DOMAIN_JOB_STATUS_QEMU_COMPLETED &&
             qemuDomainObjEnterMonitorAsync(driver, vm,
                                            QEMU_ASYNC_JOB_MIGRATION_OUT) == 0) {
             qemuMonitorMigrateCancel(priv->mon);
@@ -3802,8 +3807,8 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver,
                                           QEMU_ASYNC_JOB_MIGRATION_OUT,
                                           dconn);
 
-        if (priv->job.current->status != QEMU_DOMAIN_JOB_STATUS_CANCELED)
-            priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_FAILED;
+        if (jobPriv->current->status != QEMU_DOMAIN_JOB_STATUS_CANCELED)
+            jobPriv->current->status = QEMU_DOMAIN_JOB_STATUS_FAILED;
     }
 
     if (iothread)
@@ -5011,7 +5016,7 @@ qemuMigrationDstFinish(virQEMUDriverPtr driver,
                                        : QEMU_MIGRATION_PHASE_FINISH2);
 
     qemuDomainCleanupRemove(vm, qemuMigrationDstPrepareCleanup);
-    g_clear_pointer(&priv->job.completed, qemuDomainJobInfoFree);
+    g_clear_pointer(&jobPriv->completed, qemuDomainJobInfoFree);
 
     cookie_flags = QEMU_MIGRATION_COOKIE_NETWORK |
                    QEMU_MIGRATION_COOKIE_STATS |
@@ -5103,7 +5108,7 @@ qemuMigrationDstFinish(virQEMUDriverPtr driver,
             goto endjob;
     }
 
-    if (priv->job.current->status == QEMU_DOMAIN_JOB_STATUS_POSTCOPY)
+    if (jobPriv->current->status == QEMU_DOMAIN_JOB_STATUS_POSTCOPY)
         inPostCopy = true;
 
     if (!(flags & VIR_MIGRATE_PAUSED)) {
@@ -5217,9 +5222,9 @@ qemuMigrationDstFinish(virQEMUDriverPtr driver,
 
     if (dom) {
         if (jobInfo) {
-            priv->job.completed = g_steal_pointer(&jobInfo);
-            priv->job.completed->status = QEMU_DOMAIN_JOB_STATUS_COMPLETED;
-            priv->job.completed->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION;
+            jobPriv->completed = g_steal_pointer(&jobInfo);
+            jobPriv->completed->status = QEMU_DOMAIN_JOB_STATUS_COMPLETED;
+            jobPriv->completed->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION;
         }
 
         if (qemuMigrationBakeCookie(mig, driver, vm,
@@ -5232,7 +5237,7 @@ qemuMigrationDstFinish(virQEMUDriverPtr driver,
          * is obsolete anyway.
          */
         if (inPostCopy)
-            g_clear_pointer(&priv->job.completed, qemuDomainJobInfoFree);
+            g_clear_pointer(&jobPriv->completed, qemuDomainJobInfoFree);
     }
 
     qemuMigrationParamsReset(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN,
@@ -5461,6 +5466,7 @@ qemuMigrationJobStart(virQEMUDriverPtr driver,
                       unsigned long apiFlags)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     virDomainJobOperation op;
     unsigned long long mask;
 
@@ -5477,7 +5483,7 @@ qemuMigrationJobStart(virQEMUDriverPtr driver,
     if (qemuDomainObjBeginAsyncJob(driver, vm, job, op, apiFlags) < 0)
         return -1;
 
-    priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION;
+    jobPriv->current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION;
 
     qemuDomainObjSetAsyncJobMask(vm, mask);
     return 0;
diff --git a/src/qemu/qemu_migration_cookie.c b/src/qemu/qemu_migration_cookie.c
index 81b557e0a8..a0e8cba8ba 100644
--- a/src/qemu/qemu_migration_cookie.c
+++ b/src/qemu/qemu_migration_cookie.c
@@ -509,12 +509,13 @@ qemuMigrationCookieAddStatistics(qemuMigrationCookiePtr mig,
                                  virDomainObjPtr vm)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
 
-    if (!priv->job.completed)
+    if (!jobPriv->completed)
         return 0;
 
     g_clear_pointer(&mig->jobInfo, qemuDomainJobInfoFree);
-    mig->jobInfo = qemuDomainJobInfoCopy(priv->job.completed);
+    mig->jobInfo = qemuDomainJobInfoCopy(jobPriv->completed);
 
     mig->flags |= QEMU_MIGRATION_COOKIE_STATS;
 
@@ -1465,6 +1466,7 @@ qemuMigrationEatCookie(virQEMUDriverPtr driver,
                        unsigned int flags)
 {
     g_autoptr(qemuMigrationCookie) mig = NULL;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
 
     /* Parse & validate incoming cookie (if any) */
     if (cookiein && cookieinlen &&
@@ -1513,7 +1515,7 @@ qemuMigrationEatCookie(virQEMUDriverPtr driver,
     }
 
     if (flags & QEMU_MIGRATION_COOKIE_STATS && mig->jobInfo)
-        mig->jobInfo->operation = priv->job.current->operation;
+        mig->jobInfo->operation = jobPriv->current->operation;
 
     return g_steal_pointer(&mig);
 }
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index ec6ca14bbd..f0126bb0ef 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -656,6 +656,7 @@ qemuProcessHandleStop(qemuMonitorPtr mon G_GNUC_UNUSED,
     virDomainEventSuspendedDetailType detail;
     g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
 
     virObjectLock(vm);
 
@@ -667,7 +668,7 @@ qemuProcessHandleStop(qemuMonitorPtr mon G_GNUC_UNUSED,
     if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING &&
         !priv->pausedShutdown) {
         if (priv->job.asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT) {
-            if (priv->job.current->status == QEMU_DOMAIN_JOB_STATUS_POSTCOPY)
+            if (jobPriv->current->status == QEMU_DOMAIN_JOB_STATUS_POSTCOPY)
                 reason = VIR_DOMAIN_PAUSED_POSTCOPY;
             else
                 reason = VIR_DOMAIN_PAUSED_MIGRATION;
@@ -679,8 +680,8 @@ qemuProcessHandleStop(qemuMonitorPtr mon G_GNUC_UNUSED,
                   vm->def->name, virDomainPausedReasonTypeToString(reason),
                   detail);
 
-        if (priv->job.current)
-            ignore_value(virTimeMillisNow(&priv->job.current->stopped));
+        if (jobPriv->current)
+            ignore_value(virTimeMillisNow(&jobPriv->current->stopped));
 
         if (priv->signalStop)
             virDomainObjBroadcast(vm);
@@ -1648,6 +1649,7 @@ qemuProcessHandleMigrationStatus(qemuMonitorPtr mon G_GNUC_UNUSED,
                                  void *opaque)
 {
     qemuDomainObjPrivatePtr priv;
+    qemuDomainJobPrivatePtr jobPriv;
     virQEMUDriverPtr driver = opaque;
     virObjectEventPtr event = NULL;
     g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
@@ -1660,12 +1662,13 @@ qemuProcessHandleMigrationStatus(qemuMonitorPtr mon G_GNUC_UNUSED,
               qemuMonitorMigrationStatusTypeToString(status));
 
     priv = vm->privateData;
+    jobPriv = priv->job.privateData;
     if (priv->job.asyncJob == QEMU_ASYNC_JOB_NONE) {
         VIR_DEBUG("got MIGRATION event without a migration job");
         goto cleanup;
     }
 
-    priv->job.current->stats.mig.status = status;
+    jobPriv->current->stats.mig.status = status;
     virDomainObjBroadcast(vm);
 
     if (status == QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY &&
@@ -1746,13 +1749,13 @@ qemuProcessHandleDumpCompleted(qemuMonitorPtr mon G_GNUC_UNUSED,
         goto cleanup;
     }
     jobPriv->dumpCompleted = true;
-    priv->job.current->stats.dump = *stats;
+    jobPriv->current->stats.dump = *stats;
     priv->job.error = g_strdup(error);
 
     /* Force error if extracting the DUMP_COMPLETED status failed */
     if (!error && status < 0) {
         priv->job.error = g_strdup(virGetLastErrorMessage());
-        priv->job.current->stats.dump.status = QEMU_MONITOR_DUMP_STATUS_FAILED;
+        jobPriv->current->stats.dump.status = QEMU_MONITOR_DUMP_STATUS_FAILED;
     }
 
     virDomainObjBroadcast(vm);
@@ -3266,6 +3269,7 @@ int qemuProcessStopCPUs(virQEMUDriverPtr driver,
 {
     int ret = -1;
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
 
     VIR_FREE(priv->lockState);
 
@@ -3284,8 +3288,8 @@ int qemuProcessStopCPUs(virQEMUDriverPtr driver,
     /* de-activate netdevs after stopping CPUs */
     ignore_value(qemuInterfaceStopDevices(vm->def));
 
-    if (priv->job.current)
-        ignore_value(virTimeMillisNow(&priv->job.current->stopped));
+    if (jobPriv->current)
+        ignore_value(virTimeMillisNow(&jobPriv->current->stopped));
 
     /* The STOP event handler will change the domain state with the reason
      * saved in priv->pausedReason and it will also emit corresponding domain
@@ -3582,6 +3586,7 @@ qemuProcessRecoverJob(virQEMUDriverPtr driver,
                       unsigned int *stopFlags)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuDomainJobPrivatePtr jobPriv = priv->job.privateData;
     virDomainState state;
     int reason;
     unsigned long long now;
@@ -3650,11 +3655,11 @@ qemuProcessRecoverJob(virQEMUDriverPtr driver,
         /* We reset the job parameters for backup so that the job will look
          * active. This is possible because we are able to recover the state
          * of blockjobs and also the backup job allows all sub-job types */
-        priv->job.current = g_new0(qemuDomainJobInfo, 1);
-        priv->job.current->operation = VIR_DOMAIN_JOB_OPERATION_BACKUP;
-        priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP;
-        priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_ACTIVE;
-        priv->job.current->started = now;
+        jobPriv->current = g_new0(qemuDomainJobInfo, 1);
+        jobPriv->current->operation = VIR_DOMAIN_JOB_OPERATION_BACKUP;
+        jobPriv->current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP;
+        jobPriv->current->status = QEMU_DOMAIN_JOB_STATUS_ACTIVE;
+        jobPriv->current->started = now;
         break;
 
     case QEMU_ASYNC_JOB_NONE:
@@ -3759,7 +3764,6 @@ qemuDomainPerfRestart(virDomainObjPtr vm)
     return 0;
 }
 
-
 static void
 qemuProcessReconnectCheckMemAliasOrderMismatch(virDomainObjPtr vm)
 {
-- 
2.25.1