提交 755b53f9 编写于 作者: D Daniel P. Berrange

Avoid blocking all APIs during incoming migration

During incoming migration the QEMU monitor is not able to be
used. The incoming migration code did not keep hold of the
job lock because migration is split across multiple API calls.
This meant that further monitor commands on the guest would
hang until migration finished with no timeout.

In this change the qemuDomainMigratePrepare method sets the
job flag just before it returns. The qemuDomainMigrateFinish
method checks for this job flag & clears it once done. This
prevents any use of the monitor between prepare+finish steps.

The qemuDomainGetJobInfo method is also updated to refresh
the job elapsed time. This means that virsh domjobinfo can
return time data during incoming migration

* src/qemu/qemu_driver.c: Keep a job active during incoming
  migration. Refresh job elapsed time when returning job info
上级 2bad82f7
...@@ -97,7 +97,8 @@ ...@@ -97,7 +97,8 @@
enum qemuDomainJob { enum qemuDomainJob {
QEMU_JOB_NONE = 0, /* Always set to 0 for easy if (jobActive) conditions */ QEMU_JOB_NONE = 0, /* Always set to 0 for easy if (jobActive) conditions */
QEMU_JOB_UNSPECIFIED, QEMU_JOB_UNSPECIFIED,
QEMU_JOB_MIGRATION, QEMU_JOB_MIGRATION_OUT,
QEMU_JOB_MIGRATION_IN,
}; };
enum qemuDomainJobSignals { enum qemuDomainJobSignals {
...@@ -4356,7 +4357,7 @@ static int qemudDomainSuspend(virDomainPtr dom) { ...@@ -4356,7 +4357,7 @@ static int qemudDomainSuspend(virDomainPtr dom) {
priv = vm->privateData; priv = vm->privateData;
if (priv->jobActive == QEMU_JOB_MIGRATION) { if (priv->jobActive == QEMU_JOB_MIGRATION_OUT) {
if (vm->state != VIR_DOMAIN_PAUSED) { if (vm->state != VIR_DOMAIN_PAUSED) {
VIR_DEBUG("Requesting domain pause on %s", VIR_DEBUG("Requesting domain pause on %s",
vm->def->name); vm->def->name);
...@@ -10194,6 +10195,14 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, ...@@ -10194,6 +10195,14 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
char *unixfile = NULL; char *unixfile = NULL;
unsigned long long qemuCmdFlags; unsigned long long qemuCmdFlags;
struct qemuStreamMigFile *qemust = NULL; struct qemuStreamMigFile *qemust = NULL;
qemuDomainObjPrivatePtr priv = NULL;
struct timeval now;
if (gettimeofday(&now, NULL) < 0) {
virReportSystemError(errno, "%s",
_("cannot get time of day"));
return -1;
}
qemuDriverLock(driver); qemuDriverLock(driver);
if (!dom_xml) { if (!dom_xml) {
...@@ -10238,9 +10247,11 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, ...@@ -10238,9 +10247,11 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
goto cleanup; goto cleanup;
} }
def = NULL; def = NULL;
priv = vm->privateData;
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup; goto cleanup;
priv->jobActive = QEMU_JOB_MIGRATION_OUT;
/* Domain starts inactive, even if the domain XML had an id field. */ /* Domain starts inactive, even if the domain XML had an id field. */
vm->def->id = -1; vm->def->id = -1;
...@@ -10316,6 +10327,18 @@ endjob: ...@@ -10316,6 +10327,18 @@ endjob:
qemuDomainObjEndJob(vm) == 0) qemuDomainObjEndJob(vm) == 0)
vm = NULL; vm = NULL;
/* We set a fake job active which is held across
* API calls until the finish() call. This prevents
* any other APIs being invoked while incoming
* migration is taking place
*/
if (vm &&
virDomainObjIsActive(vm)) {
priv->jobActive = QEMU_JOB_MIGRATION_IN;
priv->jobInfo.type = VIR_DOMAIN_JOB_UNBOUNDED;
priv->jobStart = (now.tv_sec * 1000ull) + (now.tv_usec / 1000);
}
cleanup: cleanup:
virDomainDefFree(def); virDomainDefFree(def);
if (unixfile) if (unixfile)
...@@ -10355,6 +10378,14 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, ...@@ -10355,6 +10378,14 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
virDomainEventPtr event = NULL; virDomainEventPtr event = NULL;
int ret = -1; int ret = -1;
int internalret; int internalret;
qemuDomainObjPrivatePtr priv = NULL;
struct timeval now;
if (gettimeofday(&now, NULL) < 0) {
virReportSystemError(errno, "%s",
_("cannot get time of day"));
return -1;
}
virCheckFlags(VIR_MIGRATE_LIVE | virCheckFlags(VIR_MIGRATE_LIVE |
VIR_MIGRATE_PEER2PEER | VIR_MIGRATE_PEER2PEER |
...@@ -10483,9 +10514,11 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, ...@@ -10483,9 +10514,11 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
goto cleanup; goto cleanup;
} }
def = NULL; def = NULL;
priv = vm->privateData;
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup; goto cleanup;
priv->jobActive = QEMU_JOB_MIGRATION_OUT;
/* Domain starts inactive, even if the domain XML had an id field. */ /* Domain starts inactive, even if the domain XML had an id field. */
vm->def->id = -1; vm->def->id = -1;
...@@ -10517,6 +10550,18 @@ endjob: ...@@ -10517,6 +10550,18 @@ endjob:
qemuDomainObjEndJob(vm) == 0) qemuDomainObjEndJob(vm) == 0)
vm = NULL; vm = NULL;
/* We set a fake job active which is held across
* API calls until the finish() call. This prevents
* any other APIs being invoked while incoming
* migration is taking place
*/
if (vm &&
virDomainObjIsActive(vm)) {
priv->jobActive = QEMU_JOB_MIGRATION_IN;
priv->jobInfo.type = VIR_DOMAIN_JOB_UNBOUNDED;
priv->jobStart = (now.tv_sec * 1000ull) + (now.tv_usec / 1000);
}
cleanup: cleanup:
VIR_FREE(hostname); VIR_FREE(hostname);
virDomainDefFree(def); virDomainDefFree(def);
...@@ -10989,7 +11034,7 @@ qemudDomainMigratePerform (virDomainPtr dom, ...@@ -10989,7 +11034,7 @@ qemudDomainMigratePerform (virDomainPtr dom,
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup; goto cleanup;
priv->jobActive = QEMU_JOB_MIGRATION; priv->jobActive = QEMU_JOB_MIGRATION_OUT;
if (!virDomainObjIsActive(vm)) { if (!virDomainObjIsActive(vm)) {
qemuReportError(VIR_ERR_OPERATION_INVALID, qemuReportError(VIR_ERR_OPERATION_INVALID,
...@@ -11078,6 +11123,7 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, ...@@ -11078,6 +11123,7 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
virDomainEventPtr event = NULL; virDomainEventPtr event = NULL;
virErrorPtr orig_err; virErrorPtr orig_err;
int newVM = 1; int newVM = 1;
qemuDomainObjPrivatePtr priv = NULL;
virCheckFlags(VIR_MIGRATE_LIVE | virCheckFlags(VIR_MIGRATE_LIVE |
VIR_MIGRATE_PEER2PEER | VIR_MIGRATE_PEER2PEER |
...@@ -11099,6 +11145,15 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, ...@@ -11099,6 +11145,15 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
goto cleanup; goto cleanup;
} }
priv = vm->privateData;
if (priv->jobActive != QEMU_JOB_MIGRATION_IN) {
qemuReportError(VIR_ERR_NO_DOMAIN,
_("domain '%s' is not processing incoming migration"), dname);
goto cleanup;
}
priv->jobActive = QEMU_JOB_NONE;
memset(&priv->jobInfo, 0, sizeof(priv->jobInfo));
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup; goto cleanup;
...@@ -11141,7 +11196,6 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, ...@@ -11141,7 +11196,6 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
event = NULL; event = NULL;
} }
qemuDomainObjPrivatePtr priv = vm->privateData;
dom = virGetDomain (dconn, vm->def->name, vm->def->uuid); dom = virGetDomain (dconn, vm->def->name, vm->def->uuid);
if (!(flags & VIR_MIGRATE_PAUSED)) { if (!(flags & VIR_MIGRATE_PAUSED)) {
...@@ -11383,7 +11437,23 @@ static int qemuDomainGetJobInfo(virDomainPtr dom, ...@@ -11383,7 +11437,23 @@ static int qemuDomainGetJobInfo(virDomainPtr dom,
if (virDomainObjIsActive(vm)) { if (virDomainObjIsActive(vm)) {
if (priv->jobActive) { if (priv->jobActive) {
struct timeval now;
memcpy(info, &priv->jobInfo, sizeof(*info)); memcpy(info, &priv->jobInfo, sizeof(*info));
/* Refresh elapsed time again just to ensure it
* is fully updated. This is primarily for benefit
* of incoming migration which we don't currently
* monitor actively in the background thread
*/
if (gettimeofday(&now, NULL) < 0) {
virReportSystemError(errno, "%s",
_("cannot get time of day"));
goto cleanup;
}
info->timeElapsed =
((now.tv_sec * 1000ull) + (now.tv_usec / 1000)) -
priv->jobStart;
} else { } else {
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
info->type = VIR_DOMAIN_JOB_NONE; info->type = VIR_DOMAIN_JOB_NONE;
...@@ -11477,7 +11547,7 @@ qemuDomainMigrateSetMaxDowntime(virDomainPtr dom, ...@@ -11477,7 +11547,7 @@ qemuDomainMigrateSetMaxDowntime(virDomainPtr dom,
priv = vm->privateData; priv = vm->privateData;
if (priv->jobActive != QEMU_JOB_MIGRATION) { if (priv->jobActive != QEMU_JOB_MIGRATION_OUT) {
qemuReportError(VIR_ERR_OPERATION_INVALID, qemuReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("domain is not being migrated")); "%s", _("domain is not being migrated"));
goto cleanup; goto cleanup;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册