---
src/qemu/qemu_migration.c | 59 ++++++++++++++++++++--
src/qemu/qemu_migration.h | 3 +-
src/qemu/qemu_monitor.c | 43 ++++++++++++++++
src/qemu/qemu_monitor.h | 13 +++++
src/qemu/qemu_monitor_json.c | 118 +++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 5 ++
6 files changed, 237 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 11d7d6c..de8dfec 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1150,6 +1150,47 @@ qemuMigrationSetOffline(virQEMUDriverPtr driver,
static int
+qemuMigrationSetCompression(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ enum qemuDomainAsyncJob job)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int ret;
+
+ if (qemuDomainObjEnterMonitorAsync(driver, vm, job) < 0)
+ return -1;
+
+ ret = qemuMonitorGetMigrationCapability(
+ priv->mon,
+ QEMU_MONITOR_MIGRATION_CAPS_XBZRLE);
+
+ if (ret < 0) {
+ goto cleanup;
+ } else if (ret == 0) {
+ if (job == QEMU_ASYNC_JOB_MIGRATION_IN) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("Compressed migration is not supported by "
+ "target QEMU binary"));
+ } else {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("Compressed migration is not supported by "
+ "source QEMU binary"));
+ }
+ ret = -1;
+ goto cleanup;
+ }
+
+ ret = qemuMonitorSetMigrationCapability(
+ priv->mon,
+ QEMU_MONITOR_MIGRATION_CAPS_XBZRLE);
+
+cleanup:
+ qemuDomainObjExitMonitor(driver, vm);
+ return ret;
+}
+
+
+static int
qemuMigrationUpdateJobStatus(virQEMUDriverPtr driver,
virDomainObjPtr vm,
const char *job,
@@ -1704,13 +1745,16 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
if (virFDStreamOpen(st, dataFD[1]) < 0) {
virReportSystemError(errno, "%s",
_("cannot pass pipe for tunnelled
migration"));
- virDomainAuditStart(vm, "migrated", false);
- qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, 0);
- goto endjob;
+ goto stop;
}
dataFD[1] = -1; /* 'st' owns the FD now & will close it */
}
+ if (flags & VIR_MIGRATE_COMPRESSED &&
+ qemuMigrationSetCompression(driver, vm,
+ QEMU_ASYNC_JOB_MIGRATION_IN) < 0)
+ goto stop;
+
if (mig->lockState) {
VIR_DEBUG("Received lockstate %s", mig->lockState);
VIR_FREE(priv->lockState);
@@ -1776,6 +1820,10 @@ cleanup:
virObjectUnref(caps);
return ret;
+stop:
+ virDomainAuditStart(vm, "migrated", false);
+ qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, 0);
+
endjob:
if (!qemuMigrationJobFinish(driver, vm)) {
vm = NULL;
@@ -2255,6 +2303,11 @@ qemuMigrationRun(virQEMUDriverPtr driver,
goto cleanup;
}
+ if (flags & VIR_MIGRATE_COMPRESSED &&
+ qemuMigrationSetCompression(driver, vm,
+ QEMU_ASYNC_JOB_MIGRATION_OUT) < 0)
+ goto cleanup;
+
if (qemuDomainObjEnterMonitorAsync(driver, vm,
QEMU_ASYNC_JOB_MIGRATION_OUT) < 0)
goto cleanup;
diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
index 1729d73..505e911 100644
--- a/src/qemu/qemu_migration.h
+++ b/src/qemu/qemu_migration.h
@@ -37,7 +37,8 @@
VIR_MIGRATE_NON_SHARED_INC | \
VIR_MIGRATE_CHANGE_PROTECTION | \
VIR_MIGRATE_UNSAFE | \
- VIR_MIGRATE_OFFLINE)
+ VIR_MIGRATE_OFFLINE | \
+ VIR_MIGRATE_COMPRESSED)
enum qemuMigrationJobPhase {
QEMU_MIGRATION_PHASE_NONE = 0,
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 7af571d..631ff92 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -101,6 +101,10 @@ VIR_ENUM_IMPL(qemuMonitorMigrationStatus,
QEMU_MONITOR_MIGRATION_STATUS_LAST,
"inactive", "active", "completed",
"failed", "cancelled")
+VIR_ENUM_IMPL(qemuMonitorMigrationCaps,
+ QEMU_MONITOR_MIGRATION_CAPS_LAST,
+ "xbzrle")
+
VIR_ENUM_IMPL(qemuMonitorVMStatus,
QEMU_MONITOR_VM_STATUS_LAST,
"debug", "inmigrate", "internal-error",
"io-error", "paused",
@@ -3383,3 +3387,42 @@ char *qemuMonitorGetTargetArch(qemuMonitorPtr mon)
return qemuMonitorJSONGetTargetArch(mon);
}
+
+
+int qemuMonitorGetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability)
+{
+ VIR_DEBUG("mon=%p capability=%d", mon, capability);
+
+ if (!mon) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("monitor must not be NULL"));
+ return -1;
+ }
+
+ /* No capability is supported without JSON monitor */
+ if (!mon->json)
+ return 0;
+
+ return qemuMonitorJSONGetMigrationCapability(mon, capability);
+}
+
+int qemuMonitorSetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability)
+{
+ VIR_DEBUG("mon=%p capability=%d", mon, capability);
+
+ if (!mon) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("monitor must not be NULL"));
+ return -1;
+ }
+
+ if (!mon->json) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("JSON monitor is required"));
+ return -1;
+ }
+
+ return qemuMonitorJSONSetMigrationCapability(mon, capability);
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index ac77158..e3a4568 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -347,6 +347,19 @@ int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon,
bool *spice_migrated);
typedef enum {
+ QEMU_MONITOR_MIGRATION_CAPS_XBZRLE,
+
+ QEMU_MONITOR_MIGRATION_CAPS_LAST
+} qemuMonitorMigrationCaps;
+
+VIR_ENUM_DECL(qemuMonitorMigrationCaps);
+
+int qemuMonitorGetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability);
+int qemuMonitorSetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability);
+
+typedef enum {
QEMU_MONITOR_MIGRATE_BACKGROUND = 1 << 0,
QEMU_MONITOR_MIGRATE_NON_SHARED_DISK = 1 << 1, /* migration with non-shared
storage with full disk copy */
QEMU_MONITOR_MIGRATE_NON_SHARED_INC = 1 << 2, /* migration with non-shared
storage with incremental copy */
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index a86d90c..545d4d4 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -4359,3 +4359,121 @@ cleanup:
virJSONValueFree(reply);
return ret;
}
+
+
+int
+qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability)
+{
+ int ret;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr caps;
+ int i;
+
+ if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-capabilities",
+ NULL)))
+ return -1;
+
+ ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+ if (ret == 0) {
+ if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
+ goto cleanup;
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+ }
+
+ if (ret < 0)
+ goto cleanup;
+
+ ret = -1;
+
+ caps = virJSONValueObjectGet(reply, "return");
+ if (!caps || caps->type != VIR_JSON_TYPE_ARRAY) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing migration capabilities"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < virJSONValueArraySize(caps); i++) {
+ virJSONValuePtr cap = virJSONValueArrayGet(caps, i);
+ const char *name;
+
+ if (!cap || cap->type != VIR_JSON_TYPE_OBJECT) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing entry in migration capabilities list"));
+ goto cleanup;
+ }
+
+ if (!(name = virJSONValueObjectGetString(cap, "capability"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing migration capability name"));
+ goto cleanup;
+ }
+
+ if (qemuMonitorMigrationCapsTypeFromString(name) == capability) {
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
+int
+qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability)
+{
+ int ret = -1;
+
+ virJSONValuePtr cmd = NULL;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr cap = NULL;
+ virJSONValuePtr caps;
+
+ if (!(caps = virJSONValueNewArray()))
+ goto cleanup;
+
+ if (!(cap = virJSONValueNewObject()))
+ goto no_memory;
+
+ if (virJSONValueObjectAppendString(
+ cap, "capability",
+ qemuMonitorMigrationCapsTypeToString(capability)) < 0)
+ goto no_memory;
+
+ if (virJSONValueObjectAppendBoolean(cap, "state", 1) < 0)
+ goto no_memory;
+
+ if (virJSONValueArrayAppend(caps, cap) < 0)
+ goto no_memory;
+
+ cap = NULL;
+
+ cmd = qemuMonitorJSONMakeCommand("migrate-set-capabilities",
+ "a:capabilities", caps,
+ NULL);
+ if (!cmd)
+ goto cleanup;
+
+ if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
+ goto cleanup;
+
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+cleanup:
+ virJSONValueFree(cap);
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+
+no_memory:
+ virReportOOMError();
+ goto cleanup;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 925d937..356c10a 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -126,6 +126,11 @@ int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
unsigned long long *remaining,
unsigned long long *total);
+int qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability);
+int qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability);
+
int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
unsigned int flags,
const char *uri);
--
1.8.1.2