[libvirt] [PATCH v3 03/14] qemu: Handle postcopy-active migration state

Jiri Denemark jdenemar at redhat.com
Wed Mar 2 11:42:24 UTC 2016


Migration enters "postcopy-active" state after QEMU switches to
post-copy and pauses guest CPUs. From libvirt's point of view this state
is similar to "completed" because we need to transfer guest execution to
the destination host.

Signed-off-by: Jiri Denemark <jdenemar at redhat.com>
---

Notes:
    Version 3:
    - remove completed stats for incoming post-copy migration
    
    Version 2:
    - no change

 src/qemu/qemu_migration.c    | 103 +++++++++++++++++++++++++++++++++++++------
 src/qemu/qemu_monitor.c      |   2 +-
 src/qemu/qemu_monitor.h      |   1 +
 src/qemu/qemu_monitor_json.c |   1 +
 src/qemu/qemu_process.c      |  10 ++++-
 5 files changed, 101 insertions(+), 16 deletions(-)

diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 38fa81c..fdd95c5 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -2460,6 +2460,7 @@ qemuMigrationUpdateJobType(qemuDomainJobInfoPtr jobInfo)
 
     case QEMU_MONITOR_MIGRATION_STATUS_SETUP:
     case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE:
+    case QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY:
     case QEMU_MONITOR_MIGRATION_STATUS_CANCELLING:
     case QEMU_MONITOR_MIGRATION_STATUS_LAST:
         break;
@@ -2577,6 +2578,7 @@ enum qemuMigrationCompletedFlags {
     QEMU_MIGRATION_COMPLETED_ABORT_ON_ERROR = (1 << 0),
     QEMU_MIGRATION_COMPLETED_CHECK_STORAGE  = (1 << 1),
     QEMU_MIGRATION_COMPLETED_UPDATE_STATS   = (1 << 2),
+    QEMU_MIGRATION_COMPLETED_POSTCOPY       = (1 << 3),
 };
 
 /**
@@ -2618,6 +2620,20 @@ qemuMigrationCompleted(virQEMUDriverPtr driver,
         goto error;
     }
 
+    /* In case of postcopy the source considers migration completed at the
+     * moment it switched from active to postcopy-active state. The destination
+     * will continue waiting until the migrate state changes to completed.
+     */
+    if (flags & QEMU_MIGRATION_COMPLETED_POSTCOPY &&
+        jobInfo->type == VIR_DOMAIN_JOB_UNBOUNDED &&
+        jobInfo->stats.status == QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY) {
+        VIR_DEBUG("Migration switched to post-copy");
+        if (updateStats &&
+            qemuMigrationUpdateJobStatus(driver, vm, asyncJob) < 0)
+            goto error;
+        return 1;
+    }
+
     if (jobInfo->type == VIR_DOMAIN_JOB_COMPLETED)
         return 1;
     else
@@ -2651,9 +2667,11 @@ qemuMigrationWaitForCompletion(virQEMUDriverPtr driver,
     qemuDomainObjPrivatePtr priv = vm->privateData;
     qemuDomainJobInfoPtr jobInfo = priv->job.current;
     bool events = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT);
-    unsigned int flags = QEMU_MIGRATION_COMPLETED_UPDATE_STATS;
+    unsigned int flags;
     int rv;
 
+    flags = QEMU_MIGRATION_COMPLETED_UPDATE_STATS |
+            QEMU_MIGRATION_COMPLETED_POSTCOPY;
     if (abort_on_error)
         flags |= QEMU_MIGRATION_COMPLETED_ABORT_ON_ERROR;
     if (storage)
@@ -2692,9 +2710,11 @@ qemuMigrationWaitForCompletion(virQEMUDriverPtr driver,
 static int
 qemuMigrationWaitForDestCompletion(virQEMUDriverPtr driver,
                                    virDomainObjPtr vm,
-                                   qemuDomainAsyncJob asyncJob)
+                                   qemuDomainAsyncJob asyncJob,
+                                   bool postcopy)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    unsigned int flags = 0;
     int rv;
 
     if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT))
@@ -2702,7 +2722,11 @@ qemuMigrationWaitForDestCompletion(virQEMUDriverPtr driver,
 
     VIR_DEBUG("Waiting for incoming migration to complete");
 
-    while ((rv = qemuMigrationCompleted(driver, vm, asyncJob, NULL, 0)) != 1) {
+    if (postcopy)
+        flags = QEMU_MIGRATION_COMPLETED_POSTCOPY;
+
+    while ((rv = qemuMigrationCompleted(driver, vm, asyncJob,
+                                        NULL, flags)) != 1) {
         if (rv < 0 || virDomainObjWait(vm) < 0)
             return -1;
     }
@@ -2904,7 +2928,7 @@ qemuMigrationRunIncoming(virQEMUDriverPtr driver,
         goto cleanup;
     }
 
-    if (qemuMigrationWaitForDestCompletion(driver, vm, asyncJob) < 0)
+    if (qemuMigrationWaitForDestCompletion(driver, vm, asyncJob, false) < 0)
         goto cleanup;
 
     ret = 0;
@@ -3895,6 +3919,19 @@ qemuMigrationConfirmPhase(virQEMUDriverPtr driver,
 
     /* 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
+         * time we switched to post-copy mode.
+         */
+        if (virDomainObjGetState(vm, &reason) == VIR_DOMAIN_PAUSED &&
+            reason == VIR_DOMAIN_PAUSED_POSTCOPY &&
+            qemuMigrationFetchJobStatus(driver, vm,
+                                        QEMU_ASYNC_JOB_MIGRATION_OUT,
+                                        jobInfo) < 0)
+            VIR_WARN("Could not refresh migration statistics");
+
         qemuDomainJobInfoUpdateTime(jobInfo);
         jobInfo->timeDeltaSet = mig->jobInfo->timeDeltaSet;
         jobInfo->timeDelta = mig->jobInfo->timeDelta;
@@ -4305,6 +4342,7 @@ qemuMigrationRun(virQEMUDriverPtr driver,
     unsigned int cookieFlags = 0;
     bool abort_on_error = !!(flags & VIR_MIGRATE_ABORT_ON_ERROR);
     bool events = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT);
+    bool inPostCopy = false;
     int rc;
 
     VIR_DEBUG("driver=%p, vm=%p, cookiein=%s, cookieinlen=%d, "
@@ -4493,6 +4531,9 @@ qemuMigrationRun(virQEMUDriverPtr driver,
     else if (rc == -1)
         goto cleanup;
 
+    if (priv->job.current->stats.status == QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY)
+        inPostCopy = true;
+
     /* When migration completed, QEMU will have paused the CPUs for us.
      * Wait for the STOP event to be processed or explicitly stop CPUs
      * (for old QEMU which does not send events) to release the lock state.
@@ -4502,15 +4543,12 @@ qemuMigrationRun(virQEMUDriverPtr driver,
             priv->signalStop = true;
             rc = virDomainObjWait(vm);
             priv->signalStop = false;
-            if (rc < 0) {
-                priv->job.current->type = VIR_DOMAIN_JOB_FAILED;
-                goto cleanup;
-            }
+            if (rc < 0)
+                goto cancelPostCopy;
         }
     } else if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING &&
                qemuMigrationSetOffline(driver, vm) < 0) {
-        priv->job.current->type = VIR_DOMAIN_JOB_FAILED;
-        goto cleanup;
+        goto cancelPostCopy;
     }
     if (priv->job.completed)
         priv->job.completed->stopped = priv->job.current->stopped;
@@ -4541,7 +4579,7 @@ qemuMigrationRun(virQEMUDriverPtr driver,
         ignore_value(virTimeMillisNow(&priv->job.completed->sent));
     }
 
-    if (priv->job.current->type == VIR_DOMAIN_JOB_UNBOUNDED)
+    if (priv->job.current->type == VIR_DOMAIN_JOB_UNBOUNDED && !inPostCopy)
         priv->job.current->type = VIR_DOMAIN_JOB_FAILED;
 
     cookieFlags |= QEMU_MIGRATION_COOKIE_NETWORK |
@@ -4581,6 +4619,13 @@ qemuMigrationRun(virQEMUDriverPtr driver,
         }
     }
     goto cleanup;
+
+ cancelPostCopy:
+    priv->job.current->type = VIR_DOMAIN_JOB_FAILED;
+    if (inPostCopy)
+        goto cancel;
+    else
+        goto cleanup;
 }
 
 /* Perform migration using QEMU's native migrate support,
@@ -5736,6 +5781,7 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
     virObjectEventPtr event;
     int rc;
     qemuDomainJobInfoPtr jobInfo = NULL;
+    bool inPostCopy = false;
 
     VIR_DEBUG("driver=%p, dconn=%p, vm=%p, cookiein=%s, cookieinlen=%d, "
               "cookieout=%p, cookieoutlen=%p, flags=%lx, retcode=%d",
@@ -5841,7 +5887,8 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
      * before starting guest CPUs.
      */
     if (qemuMigrationWaitForDestCompletion(driver, vm,
-                                           QEMU_ASYNC_JOB_MIGRATION_IN) < 0) {
+                                           QEMU_ASYNC_JOB_MIGRATION_IN,
+                                           !!(flags & VIR_MIGRATE_POSTCOPY)) < 0) {
         /* There's not much we can do for v2 protocol since the
          * original domain on the source host is already gone.
          */
@@ -5849,13 +5896,17 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
             goto endjob;
     }
 
+    if (priv->job.current->stats.status == QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY)
+        inPostCopy = true;
+
     if (!(flags & VIR_MIGRATE_PAUSED)) {
         /* run 'cont' on the destination, which allows migration on qemu
          * >= 0.10.6 to work properly.  This isn't strictly necessary on
          * older qemu's, but it also doesn't hurt anything there
          */
         if (qemuProcessStartCPUs(driver, vm, dconn,
-                                 VIR_DOMAIN_RUNNING_MIGRATED,
+                                 inPostCopy ? VIR_DOMAIN_RUNNING_POSTCOPY
+                                            : VIR_DOMAIN_RUNNING_MIGRATED,
                                  QEMU_ASYNC_JOB_MIGRATION_IN) < 0) {
             if (virGetLastError() == NULL)
                 virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -5876,6 +5927,13 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
             if (v3proto)
                 goto endjob;
         }
+
+        if (inPostCopy) {
+            event = virDomainEventLifecycleNewFromObj(vm,
+                                        VIR_DOMAIN_EVENT_RESUMED,
+                                        VIR_DOMAIN_EVENT_RESUMED_POSTCOPY);
+            qemuDomainEventQueue(driver, event);
+        }
     }
 
     if (mig->jobInfo) {
@@ -5891,6 +5949,19 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
         qemuDomainJobInfoUpdateDowntime(jobInfo);
     }
 
+    if (inPostCopy) {
+        if (qemuMigrationWaitForDestCompletion(driver, vm,
+                                               QEMU_ASYNC_JOB_MIGRATION_IN,
+                                               false) < 0) {
+            goto endjob;
+        }
+        if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
+            virDomainObjSetState(vm,
+                                 VIR_DOMAIN_RUNNING,
+                                 VIR_DOMAIN_RUNNING_MIGRATED);
+        }
+    }
+
     dom = virGetDomain(dconn, vm->def->name, vm->def->uuid);
 
     event = virDomainEventLifecycleNewFromObj(vm,
@@ -5933,6 +6004,12 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
         if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen,
                                     QEMU_MIGRATION_COOKIE_STATS) < 0)
             VIR_WARN("Unable to encode migration cookie");
+
+        /* Remove completed stats for post-copy, everything but timing fields
+         * is obsolete anyway.
+         */
+        if (inPostCopy)
+            VIR_FREE(priv->job.completed);
     }
 
     qemuMigrationJobFinish(driver, vm);
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index ace3bb4..e831834 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -161,7 +161,7 @@ VIR_ONCE_GLOBAL_INIT(qemuMonitor)
 VIR_ENUM_IMPL(qemuMonitorMigrationStatus,
               QEMU_MONITOR_MIGRATION_STATUS_LAST,
               "inactive", "setup",
-              "active",
+              "active", "postcopy-active",
               "completed", "failed",
               "cancelling", "cancelled")
 
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 4467a41..9331dab 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -473,6 +473,7 @@ typedef enum {
     QEMU_MONITOR_MIGRATION_STATUS_INACTIVE,
     QEMU_MONITOR_MIGRATION_STATUS_SETUP,
     QEMU_MONITOR_MIGRATION_STATUS_ACTIVE,
+    QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY,
     QEMU_MONITOR_MIGRATION_STATUS_COMPLETED,
     QEMU_MONITOR_MIGRATION_STATUS_ERROR,
     QEMU_MONITOR_MIGRATION_STATUS_CANCELLING,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 8352e53..8a2aed7 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -2548,6 +2548,7 @@ qemuMonitorJSONGetMigrationStatsReply(virJSONValuePtr reply,
         break;
 
     case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE:
+    case QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY:
     case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED:
     case QEMU_MONITOR_MIGRATION_STATUS_CANCELLING:
         ram = virJSONValueObjectGetObject(ret, "ram");
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index cd7861e..3757a53 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -711,8 +711,14 @@ qemuProcessHandleStop(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
         }
 
         if (priv->job.asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT) {
-            reason = VIR_DOMAIN_PAUSED_MIGRATION;
-            detail = VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED;
+            if (priv->job.current->stats.status ==
+                        QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY) {
+                reason = VIR_DOMAIN_PAUSED_POSTCOPY;
+                detail = VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY;
+            } else {
+                reason = VIR_DOMAIN_PAUSED_MIGRATION;
+                detail = VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED;
+            }
         }
 
         VIR_DEBUG("Transitioned guest %s to paused state, reason %s",
-- 
2.7.2




More information about the libvir-list mailing list