提交 ee47d8e8 编写于 作者: J Jiri Denemark

qemu: Handle postcopy-active migration state

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: NJiri Denemark <jdenemar@redhat.com>
上级 1a1246ec
...@@ -2471,6 +2471,7 @@ qemuMigrationUpdateJobType(qemuDomainJobInfoPtr jobInfo) ...@@ -2471,6 +2471,7 @@ qemuMigrationUpdateJobType(qemuDomainJobInfoPtr jobInfo)
case QEMU_MONITOR_MIGRATION_STATUS_SETUP: case QEMU_MONITOR_MIGRATION_STATUS_SETUP:
case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE: case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE:
case QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY:
case QEMU_MONITOR_MIGRATION_STATUS_CANCELLING: case QEMU_MONITOR_MIGRATION_STATUS_CANCELLING:
case QEMU_MONITOR_MIGRATION_STATUS_LAST: case QEMU_MONITOR_MIGRATION_STATUS_LAST:
break; break;
...@@ -2588,6 +2589,7 @@ enum qemuMigrationCompletedFlags { ...@@ -2588,6 +2589,7 @@ enum qemuMigrationCompletedFlags {
QEMU_MIGRATION_COMPLETED_ABORT_ON_ERROR = (1 << 0), QEMU_MIGRATION_COMPLETED_ABORT_ON_ERROR = (1 << 0),
QEMU_MIGRATION_COMPLETED_CHECK_STORAGE = (1 << 1), QEMU_MIGRATION_COMPLETED_CHECK_STORAGE = (1 << 1),
QEMU_MIGRATION_COMPLETED_UPDATE_STATS = (1 << 2), QEMU_MIGRATION_COMPLETED_UPDATE_STATS = (1 << 2),
QEMU_MIGRATION_COMPLETED_POSTCOPY = (1 << 3),
}; };
/** /**
...@@ -2629,6 +2631,20 @@ qemuMigrationCompleted(virQEMUDriverPtr driver, ...@@ -2629,6 +2631,20 @@ qemuMigrationCompleted(virQEMUDriverPtr driver,
goto error; 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) if (jobInfo->type == VIR_DOMAIN_JOB_COMPLETED)
return 1; return 1;
else else
...@@ -2662,9 +2678,11 @@ qemuMigrationWaitForCompletion(virQEMUDriverPtr driver, ...@@ -2662,9 +2678,11 @@ qemuMigrationWaitForCompletion(virQEMUDriverPtr driver,
qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjPrivatePtr priv = vm->privateData;
qemuDomainJobInfoPtr jobInfo = priv->job.current; qemuDomainJobInfoPtr jobInfo = priv->job.current;
bool events = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT); bool events = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT);
unsigned int flags = QEMU_MIGRATION_COMPLETED_UPDATE_STATS; unsigned int flags;
int rv; int rv;
flags = QEMU_MIGRATION_COMPLETED_UPDATE_STATS |
QEMU_MIGRATION_COMPLETED_POSTCOPY;
if (abort_on_error) if (abort_on_error)
flags |= QEMU_MIGRATION_COMPLETED_ABORT_ON_ERROR; flags |= QEMU_MIGRATION_COMPLETED_ABORT_ON_ERROR;
if (storage) if (storage)
...@@ -2703,9 +2721,11 @@ qemuMigrationWaitForCompletion(virQEMUDriverPtr driver, ...@@ -2703,9 +2721,11 @@ qemuMigrationWaitForCompletion(virQEMUDriverPtr driver,
static int static int
qemuMigrationWaitForDestCompletion(virQEMUDriverPtr driver, qemuMigrationWaitForDestCompletion(virQEMUDriverPtr driver,
virDomainObjPtr vm, virDomainObjPtr vm,
qemuDomainAsyncJob asyncJob) qemuDomainAsyncJob asyncJob,
bool postcopy)
{ {
qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjPrivatePtr priv = vm->privateData;
unsigned int flags = 0;
int rv; int rv;
if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT)) if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT))
...@@ -2713,7 +2733,11 @@ qemuMigrationWaitForDestCompletion(virQEMUDriverPtr driver, ...@@ -2713,7 +2733,11 @@ qemuMigrationWaitForDestCompletion(virQEMUDriverPtr driver,
VIR_DEBUG("Waiting for incoming migration to complete"); 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) if (rv < 0 || virDomainObjWait(vm) < 0)
return -1; return -1;
} }
...@@ -2915,7 +2939,7 @@ qemuMigrationRunIncoming(virQEMUDriverPtr driver, ...@@ -2915,7 +2939,7 @@ qemuMigrationRunIncoming(virQEMUDriverPtr driver,
goto cleanup; goto cleanup;
} }
if (qemuMigrationWaitForDestCompletion(driver, vm, asyncJob) < 0) if (qemuMigrationWaitForDestCompletion(driver, vm, asyncJob, false) < 0)
goto cleanup; goto cleanup;
ret = 0; ret = 0;
...@@ -3918,6 +3942,19 @@ qemuMigrationConfirmPhase(virQEMUDriverPtr driver, ...@@ -3918,6 +3942,19 @@ qemuMigrationConfirmPhase(virQEMUDriverPtr driver,
/* Update times with the values sent by the destination daemon */ /* Update times with the values sent by the destination daemon */
if (mig->jobInfo && jobInfo) { 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); qemuDomainJobInfoUpdateTime(jobInfo);
jobInfo->timeDeltaSet = mig->jobInfo->timeDeltaSet; jobInfo->timeDeltaSet = mig->jobInfo->timeDeltaSet;
jobInfo->timeDelta = mig->jobInfo->timeDelta; jobInfo->timeDelta = mig->jobInfo->timeDelta;
...@@ -4328,6 +4365,7 @@ qemuMigrationRun(virQEMUDriverPtr driver, ...@@ -4328,6 +4365,7 @@ qemuMigrationRun(virQEMUDriverPtr driver,
unsigned int cookieFlags = 0; unsigned int cookieFlags = 0;
bool abort_on_error = !!(flags & VIR_MIGRATE_ABORT_ON_ERROR); bool abort_on_error = !!(flags & VIR_MIGRATE_ABORT_ON_ERROR);
bool events = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT); bool events = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT);
bool inPostCopy = false;
int rc; int rc;
VIR_DEBUG("driver=%p, vm=%p, cookiein=%s, cookieinlen=%d, " VIR_DEBUG("driver=%p, vm=%p, cookiein=%s, cookieinlen=%d, "
...@@ -4516,6 +4554,9 @@ qemuMigrationRun(virQEMUDriverPtr driver, ...@@ -4516,6 +4554,9 @@ qemuMigrationRun(virQEMUDriverPtr driver,
else if (rc == -1) else if (rc == -1)
goto cleanup; 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. /* When migration completed, QEMU will have paused the CPUs for us.
* Wait for the STOP event to be processed or explicitly stop CPUs * 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. * (for old QEMU which does not send events) to release the lock state.
...@@ -4525,15 +4566,12 @@ qemuMigrationRun(virQEMUDriverPtr driver, ...@@ -4525,15 +4566,12 @@ qemuMigrationRun(virQEMUDriverPtr driver,
priv->signalStop = true; priv->signalStop = true;
rc = virDomainObjWait(vm); rc = virDomainObjWait(vm);
priv->signalStop = false; priv->signalStop = false;
if (rc < 0) { if (rc < 0)
priv->job.current->type = VIR_DOMAIN_JOB_FAILED; goto cancelPostCopy;
goto cleanup;
}
} }
} else if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING && } else if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING &&
qemuMigrationSetOffline(driver, vm) < 0) { qemuMigrationSetOffline(driver, vm) < 0) {
priv->job.current->type = VIR_DOMAIN_JOB_FAILED; goto cancelPostCopy;
goto cleanup;
} }
if (priv->job.completed) if (priv->job.completed)
priv->job.completed->stopped = priv->job.current->stopped; priv->job.completed->stopped = priv->job.current->stopped;
...@@ -4564,7 +4602,7 @@ qemuMigrationRun(virQEMUDriverPtr driver, ...@@ -4564,7 +4602,7 @@ qemuMigrationRun(virQEMUDriverPtr driver,
ignore_value(virTimeMillisNow(&priv->job.completed->sent)); 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; priv->job.current->type = VIR_DOMAIN_JOB_FAILED;
cookieFlags |= QEMU_MIGRATION_COOKIE_NETWORK | cookieFlags |= QEMU_MIGRATION_COOKIE_NETWORK |
...@@ -4604,6 +4642,13 @@ qemuMigrationRun(virQEMUDriverPtr driver, ...@@ -4604,6 +4642,13 @@ qemuMigrationRun(virQEMUDriverPtr driver,
} }
} }
goto cleanup; 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, /* Perform migration using QEMU's native migrate support,
...@@ -5778,6 +5823,7 @@ qemuMigrationFinish(virQEMUDriverPtr driver, ...@@ -5778,6 +5823,7 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
virObjectEventPtr event; virObjectEventPtr event;
int rc; int rc;
qemuDomainJobInfoPtr jobInfo = NULL; qemuDomainJobInfoPtr jobInfo = NULL;
bool inPostCopy = false;
VIR_DEBUG("driver=%p, dconn=%p, vm=%p, cookiein=%s, cookieinlen=%d, " VIR_DEBUG("driver=%p, dconn=%p, vm=%p, cookiein=%s, cookieinlen=%d, "
"cookieout=%p, cookieoutlen=%p, flags=%lx, retcode=%d", "cookieout=%p, cookieoutlen=%p, flags=%lx, retcode=%d",
...@@ -5883,7 +5929,8 @@ qemuMigrationFinish(virQEMUDriverPtr driver, ...@@ -5883,7 +5929,8 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
* before starting guest CPUs. * before starting guest CPUs.
*/ */
if (qemuMigrationWaitForDestCompletion(driver, vm, 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 /* There's not much we can do for v2 protocol since the
* original domain on the source host is already gone. * original domain on the source host is already gone.
*/ */
...@@ -5891,13 +5938,17 @@ qemuMigrationFinish(virQEMUDriverPtr driver, ...@@ -5891,13 +5938,17 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
goto endjob; goto endjob;
} }
if (priv->job.current->stats.status == QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY)
inPostCopy = true;
if (!(flags & VIR_MIGRATE_PAUSED)) { if (!(flags & VIR_MIGRATE_PAUSED)) {
/* run 'cont' on the destination, which allows migration on qemu /* run 'cont' on the destination, which allows migration on qemu
* >= 0.10.6 to work properly. This isn't strictly necessary on * >= 0.10.6 to work properly. This isn't strictly necessary on
* older qemu's, but it also doesn't hurt anything there * older qemu's, but it also doesn't hurt anything there
*/ */
if (qemuProcessStartCPUs(driver, vm, dconn, if (qemuProcessStartCPUs(driver, vm, dconn,
VIR_DOMAIN_RUNNING_MIGRATED, inPostCopy ? VIR_DOMAIN_RUNNING_POSTCOPY
: VIR_DOMAIN_RUNNING_MIGRATED,
QEMU_ASYNC_JOB_MIGRATION_IN) < 0) { QEMU_ASYNC_JOB_MIGRATION_IN) < 0) {
if (virGetLastError() == NULL) if (virGetLastError() == NULL)
virReportError(VIR_ERR_INTERNAL_ERROR, virReportError(VIR_ERR_INTERNAL_ERROR,
...@@ -5918,6 +5969,13 @@ qemuMigrationFinish(virQEMUDriverPtr driver, ...@@ -5918,6 +5969,13 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
if (v3proto) if (v3proto)
goto endjob; goto endjob;
} }
if (inPostCopy) {
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_RESUMED,
VIR_DOMAIN_EVENT_RESUMED_POSTCOPY);
qemuDomainEventQueue(driver, event);
}
} }
if (mig->jobInfo) { if (mig->jobInfo) {
...@@ -5933,6 +5991,19 @@ qemuMigrationFinish(virQEMUDriverPtr driver, ...@@ -5933,6 +5991,19 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
qemuDomainJobInfoUpdateDowntime(jobInfo); 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); dom = virGetDomain(dconn, vm->def->name, vm->def->uuid);
event = virDomainEventLifecycleNewFromObj(vm, event = virDomainEventLifecycleNewFromObj(vm,
...@@ -5975,6 +6046,12 @@ qemuMigrationFinish(virQEMUDriverPtr driver, ...@@ -5975,6 +6046,12 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen, if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen,
QEMU_MIGRATION_COOKIE_STATS) < 0) QEMU_MIGRATION_COOKIE_STATS) < 0)
VIR_WARN("Unable to encode migration cookie"); 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); qemuMigrationJobFinish(driver, vm);
......
...@@ -161,7 +161,7 @@ VIR_ONCE_GLOBAL_INIT(qemuMonitor) ...@@ -161,7 +161,7 @@ VIR_ONCE_GLOBAL_INIT(qemuMonitor)
VIR_ENUM_IMPL(qemuMonitorMigrationStatus, VIR_ENUM_IMPL(qemuMonitorMigrationStatus,
QEMU_MONITOR_MIGRATION_STATUS_LAST, QEMU_MONITOR_MIGRATION_STATUS_LAST,
"inactive", "setup", "inactive", "setup",
"active", "active", "postcopy-active",
"completed", "failed", "completed", "failed",
"cancelling", "cancelled") "cancelling", "cancelled")
......
...@@ -473,6 +473,7 @@ typedef enum { ...@@ -473,6 +473,7 @@ typedef enum {
QEMU_MONITOR_MIGRATION_STATUS_INACTIVE, QEMU_MONITOR_MIGRATION_STATUS_INACTIVE,
QEMU_MONITOR_MIGRATION_STATUS_SETUP, QEMU_MONITOR_MIGRATION_STATUS_SETUP,
QEMU_MONITOR_MIGRATION_STATUS_ACTIVE, QEMU_MONITOR_MIGRATION_STATUS_ACTIVE,
QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY,
QEMU_MONITOR_MIGRATION_STATUS_COMPLETED, QEMU_MONITOR_MIGRATION_STATUS_COMPLETED,
QEMU_MONITOR_MIGRATION_STATUS_ERROR, QEMU_MONITOR_MIGRATION_STATUS_ERROR,
QEMU_MONITOR_MIGRATION_STATUS_CANCELLING, QEMU_MONITOR_MIGRATION_STATUS_CANCELLING,
......
...@@ -2548,6 +2548,7 @@ qemuMonitorJSONGetMigrationStatsReply(virJSONValuePtr reply, ...@@ -2548,6 +2548,7 @@ qemuMonitorJSONGetMigrationStatsReply(virJSONValuePtr reply,
break; break;
case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE: case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE:
case QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY:
case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED: case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED:
case QEMU_MONITOR_MIGRATION_STATUS_CANCELLING: case QEMU_MONITOR_MIGRATION_STATUS_CANCELLING:
ram = virJSONValueObjectGetObject(ret, "ram"); ram = virJSONValueObjectGetObject(ret, "ram");
......
...@@ -711,9 +711,15 @@ qemuProcessHandleStop(qemuMonitorPtr mon ATTRIBUTE_UNUSED, ...@@ -711,9 +711,15 @@ qemuProcessHandleStop(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
} }
if (priv->job.asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT) { if (priv->job.asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT) {
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; reason = VIR_DOMAIN_PAUSED_MIGRATION;
detail = VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED; detail = VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED;
} }
}
VIR_DEBUG("Transitioned guest %s to paused state, reason %s", VIR_DEBUG("Transitioned guest %s to paused state, reason %s",
vm->def->name, virDomainPausedReasonTypeToString(reason)); vm->def->name, virDomainPausedReasonTypeToString(reason));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册