diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 0ddb5077cf1f2ea4171b311966e15dea54feb7ca..4cd5978719072b446c130e2592154cab17de1b3f 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1149,6 +1149,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, @@ -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 1729d73614505ac1a3395977c5464dd1d10e5b4c..505e9114d6279a27a612f3ee36b1e21f8fb46194 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 7af571d99493a41977b09fd2218c384dc55d2d48..5c507530f1b059239d49307903a0d96d9cd3d9fa 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,45 @@ char *qemuMonitorGetTargetArch(qemuMonitorPtr mon) return qemuMonitorJSONGetTargetArch(mon); } + + +/** + * Returns 1 if @capability is supported, 0 if it's not, or -1 on error. + */ +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 ac77158ecca737df859dc4bb2dcea99304c5bc96..e3a456870fa52102461650fddbb06eb56da76b71 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -346,6 +346,19 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, 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 */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a86d90c4b934e20646473c8646b9f5be9f071866..7e9fe47c61679aca60b83cd502c958f3b7a9eded 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4359,3 +4359,124 @@ 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; + + caps = NULL; + + if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) + goto cleanup; + + ret = qemuMonitorJSONCheckError(cmd, reply); + +cleanup: + virJSONValueFree(caps); + 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 925d937cbc6eab2077eb62bf588de27b26ec708e..356c10abf397d3e78a3d1942a2b935697b4f8968 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);