This patch introduces several helper methods to deal with jobs and
phases during migration in a simpler manner.
---
src/qemu/MIGRATION.txt | 55 +++++++++++++++++++++++++++
src/qemu/qemu_domain.c | 5 ++
src/qemu/qemu_migration.c | 91 +++++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_migration.h | 37 ++++++++++++++++++
4 files changed, 188 insertions(+), 0 deletions(-)
create mode 100644 src/qemu/MIGRATION.txt
diff --git a/src/qemu/MIGRATION.txt b/src/qemu/MIGRATION.txt
new file mode 100644
index 0000000..6c32998
--- /dev/null
+++ b/src/qemu/MIGRATION.txt
@@ -0,0 +1,55 @@
+ QEMU Migration Locking Rules
+ ============================
+
+Migration is a complicated beast which may span across several APIs on both
+source and destination side and we need to keep the domain we are migrating in
+a consistent state during the whole process.
+
+To avoid anyone from changing the domain in the middle of migration we need to
+keep MIGRATION_OUT job active during migration from Begin to Confirm on the
+source side and MIGRATION_IN job has to be active from Prepare to Finish on
+the destination side.
+
+For this purpose we introduce several helper methods to deal with locking
+primitives (described in THREADS.txt) in the right way:
+
+* qemuMigrationJobStart
+
+* qemuMigrationJobContinue
+
+* qemuMigrationJobStartPhase
+
+* qemuMigrationJobSetPhase
+
+* qemuMigrationJobFinish
+
+The sequence of calling qemuMigrationJob* helper methods is as follows:
+
+- The first API of a migration protocol (Prepare or Perform/Begin depending on
+ migration type and version) has to start migration job and keep it active:
+
+ qemuMigrationJobStart(driver, vm, QEMU_JOB_MIGRATION_{IN,OUT});
+ qemuMigrationJobSetPhase(driver, vm, QEMU_MIGRATION_PHASE_*);
+ ...do work...
+ qemuMigrationJobContinue(vm);
+
+- All consequent phases except for the last one have to keep the job active:
+
+ if (!qemuMigrationJobIsActive(vm, QEMU_JOB_MIGRATION_{IN,OUT}))
+ return;
+ qemuMigrationJobStartPhase(driver, vm, QEMU_MIGRATION_PHASE_*);
+ ...do work...
+ qemuMigrationJobContinue(vm);
+
+- The last migration phase finally finishes the migration job:
+
+ if (!qemuMigrationJobIsActive(vm, QEMU_JOB_MIGRATION_{IN,OUT}))
+ return;
+ qemuMigrationJobStartPhase(driver, vm, QEMU_MIGRATION_PHASE_*);
+ ...do work...
+ qemuMigrationJobFinish(driver, vm);
+
+While migration job is running (i.e., after qemuMigrationJobStart* but before
+qemuMigrationJob{Continue,Finish}), migration phase can be advanced using
+
+ qemuMigrationJobSetPhase(driver, vm, QEMU_MIGRATION_PHASE_*);
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index f9755a4..fee562d 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -26,6 +26,7 @@
#include "qemu_domain.h"
#include "qemu_command.h"
#include "qemu_capabilities.h"
+#include "qemu_migration.h"
#include "memory.h"
#include "logging.h"
#include "virterror_internal.h"
@@ -72,6 +73,8 @@ qemuDomainAsyncJobPhaseToString(enum qemuDomainAsyncJob job,
switch (job) {
case QEMU_ASYNC_JOB_MIGRATION_OUT:
case QEMU_ASYNC_JOB_MIGRATION_IN:
+ return qemuMigrationJobPhaseTypeToString(phase);
+
case QEMU_ASYNC_JOB_SAVE:
case QEMU_ASYNC_JOB_DUMP:
case QEMU_ASYNC_JOB_NONE:
@@ -92,6 +95,8 @@ qemuDomainAsyncJobPhaseFromString(enum qemuDomainAsyncJob job,
switch (job) {
case QEMU_ASYNC_JOB_MIGRATION_OUT:
case QEMU_ASYNC_JOB_MIGRATION_IN:
+ return qemuMigrationJobPhaseTypeFromString(phase);
+
case QEMU_ASYNC_JOB_SAVE:
case QEMU_ASYNC_JOB_DUMP:
case QEMU_ASYNC_JOB_NONE:
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index dfa80e3..9659e8d 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -46,6 +46,19 @@
#define VIR_FROM_THIS VIR_FROM_QEMU
+VIR_ENUM_IMPL(qemuMigrationJobPhase, QEMU_MIGRATION_PHASE_LAST,
+ "none",
+ "preform2",
+ "begin3",
+ "perform3",
+ "perform3_done",
+ "confirm3_cancelled",
+ "confirm3",
+ "prepare",
+ "finish2",
+ "finish3",
+);
+
enum qemuMigrationCookieFlags {
QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS,
QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE,
@@ -2770,3 +2783,81 @@ cleanup:
}
return ret;
}
+
+int
+qemuMigrationJobStart(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ enum qemuDomainAsyncJob job)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ if (qemuDomainObjBeginAsyncJobWithDriver(driver, vm, job) < 0)
+ return -1;
+
+ if (job == QEMU_ASYNC_JOB_MIGRATION_IN)
+ qemuDomainObjSetAsyncJobMask(vm, QEMU_JOB_NONE);
+ else
+ qemuDomainObjSetAsyncJobMask(vm, DEFAULT_JOB_MASK);
+
+ priv->job.info.type = VIR_DOMAIN_JOB_UNBOUNDED;
+
+ return 0;
+}
+
+void
+qemuMigrationJobSetPhase(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ enum qemuMigrationJobPhase phase)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ if (phase < priv->job.phase) {
+ VIR_ERROR(_("migration protocol going backwards %s => %s"),
+ qemuMigrationJobPhaseTypeToString(priv->job.phase),
+ qemuMigrationJobPhaseTypeToString(phase));
+ return;
+ }
+
+ qemuDomainObjSetJobPhase(driver, vm, phase);
+}
+
+void
+qemuMigrationJobStartPhase(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ enum qemuMigrationJobPhase phase)
+{
+ virDomainObjRef(vm);
+ qemuMigrationJobSetPhase(driver, vm, phase);
+}
+
+int
+qemuMigrationJobContinue(virDomainObjPtr vm)
+{
+ return virDomainObjUnref(vm);
+}
+
+bool
+qemuMigrationJobIsActive(virDomainObjPtr vm,
+ enum qemuDomainAsyncJob job)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ if (priv->job.asyncJob != job) {
+ const char *msg;
+
+ if (job == QEMU_ASYNC_JOB_MIGRATION_IN)
+ msg = _("domain '%s' is not processing incoming
migration");
+ else
+ msg = _("domain '%s' is not being migrated");
+
+ qemuReportError(VIR_ERR_OPERATION_INVALID, msg, vm->def->name);
+ return false;
+ }
+ return true;
+}
+
+int
+qemuMigrationJobFinish(struct qemud_driver *driver, virDomainObjPtr vm)
+{
+ return qemuDomainObjEndAsyncJob(driver, vm);
+}
diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
index 3a9b94e..005e415 100644
--- a/src/qemu/qemu_migration.h
+++ b/src/qemu/qemu_migration.h
@@ -23,6 +23,7 @@
# define __QEMU_MIGRATION_H__
# include "qemu_conf.h"
+# include "qemu_domain.h"
/* All supported qemu migration flags. */
# define QEMU_MIGRATION_FLAGS \
@@ -35,6 +36,42 @@
VIR_MIGRATE_NON_SHARED_DISK | \
VIR_MIGRATE_NON_SHARED_INC)
+enum qemuMigrationJobPhase {
+ QEMU_MIGRATION_PHASE_NONE = 0,
+ QEMU_MIGRATION_PHASE_PERFORM2,
+ QEMU_MIGRATION_PHASE_BEGIN3,
+ QEMU_MIGRATION_PHASE_PERFORM3,
+ QEMU_MIGRATION_PHASE_PERFORM3_DONE,
+ QEMU_MIGRATION_PHASE_CONFIRM3_CANCELLED,
+ QEMU_MIGRATION_PHASE_CONFIRM3,
+ QEMU_MIGRATION_PHASE_PREPARE,
+ QEMU_MIGRATION_PHASE_FINISH2,
+ QEMU_MIGRATION_PHASE_FINISH3,
+
+ QEMU_MIGRATION_PHASE_LAST
+};
+VIR_ENUM_DECL(qemuMigrationJobPhase)
+
+int qemuMigrationJobStart(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ enum qemuDomainAsyncJob job)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+void qemuMigrationJobSetPhase(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ enum qemuMigrationJobPhase phase)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+void qemuMigrationJobStartPhase(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ enum qemuMigrationJobPhase phase)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int qemuMigrationJobContinue(virDomainObjPtr obj)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+bool qemuMigrationJobIsActive(virDomainObjPtr vm,
+ enum qemuDomainAsyncJob job)
+ ATTRIBUTE_NONNULL(1);
+int qemuMigrationJobFinish(struct qemud_driver *driver, virDomainObjPtr obj)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+
bool qemuMigrationIsAllowed(virDomainDefPtr def)
ATTRIBUTE_NONNULL(1);
int qemuMigrationSetOffline(struct qemud_driver *driver,
--
1.7.6