From 3b5aa0e6272a4b507034bba3d093734ebd658b3b Mon Sep 17 00:00:00 2001 Message-Id: <3b5aa0e6272a4b507034bba3d093734ebd658b3b@dist-git> From: Jiri Denemark Date: Wed, 31 May 2017 12:34:10 +0200 Subject: [PATCH] qemu: Use updated CPU when starting QEMU if possible If QEMU is new enough and we have the live updated CPU definition in either save or migration cookie, we can use it to enforce ABI. The original guest CPU from domain XML will be stored in private data. Signed-off-by: Jiri Denemark Reviewed-by: Pavel Hrdina (cherry picked from commit 8e34f478137c2a6b5e57e382729bd2776b042301) https://bugzilla.redhat.com/show_bug.cgi?id=1441662 Signed-off-by: Jiri Denemark --- src/qemu/qemu_domain.c | 40 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 5 +++++ src/qemu/qemu_driver.c | 30 ++++++++++++++++++++++++------ src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_process.c | 24 ++++++++++++++++++++++-- src/qemu/qemu_process.h | 2 ++ 6 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 7897a44634..82dacb3c9c 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -9225,3 +9225,43 @@ virSaveCookieCallbacks virQEMUDriverDomainSaveCookie = { .parse = qemuDomainSaveCookieParse, .format = qemuDomainSaveCookieFormat, }; + + +/** + * qemuDomainUpdateCPU: + * @vm: domain which is being started + * @cpu: CPU updated when the domain was running previously (before migration, + * snapshot, or save) + * @origCPU: where to store the original CPU from vm->def in case @cpu was + * used instead + * + * Replace the CPU definition with the updated one when QEMU is new enough to + * allow us to check extra features it is about to enable or disable when + * starting a domain. The original CPU is stored in @origCPU. + * + * Returns 0 on success, -1 on error. + */ +int +qemuDomainUpdateCPU(virDomainObjPtr vm, + virCPUDefPtr cpu, + virCPUDefPtr *origCPU) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + *origCPU = NULL; + + if (!cpu || !vm->def->cpu || + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION) || + virCPUDefIsEqual(vm->def->cpu, cpu, false)) + return 0; + + if (!(cpu = virCPUDefCopy(cpu))) + return -1; + + VIR_DEBUG("Replacing CPU def with the updated one"); + + *origCPU = vm->def->cpu; + vm->def->cpu = cpu; + + return 0; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 2eea7924fb..d635d2995f 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -921,4 +921,9 @@ char *qemuDomainDiskBackingStoreGetName(virDomainDiskDefPtr disk, virStorageSourcePtr qemuDomainGetStorageSourceByDevstr(const char *devstr, virDomainDefPtr def); +int +qemuDomainUpdateCPU(virDomainObjPtr vm, + virCPUDefPtr cpu, + virCPUDefPtr *origCPU); + #endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f5c5c302be..5567103c37 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1782,7 +1782,7 @@ static virDomainPtr qemuDomainCreateXML(virConnectPtr conn, goto cleanup; } - if (qemuProcessStart(conn, driver, vm, QEMU_ASYNC_JOB_START, + if (qemuProcessStart(conn, driver, vm, NULL, QEMU_ASYNC_JOB_START, NULL, -1, NULL, NULL, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags) < 0) { @@ -6500,8 +6500,8 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, } } - if (qemuProcessStart(conn, driver, vm, asyncJob, - "stdio", *fd, path, NULL, + if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL, + asyncJob, "stdio", *fd, path, NULL, VIR_NETDEV_VPORT_PROFILE_OP_RESTORE, VIR_QEMU_PROCESS_START_PAUSED) == 0) restored = true; @@ -7118,7 +7118,7 @@ qemuDomainObjStart(virConnectPtr conn, } } - ret = qemuProcessStart(conn, driver, vm, asyncJob, + ret = qemuProcessStart(conn, driver, vm, NULL, asyncJob, NULL, -1, NULL, NULL, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags); virDomainAuditStart(vm, "booted", ret >= 0); @@ -15295,6 +15295,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, virCapsPtr caps = NULL; bool was_running = false; bool was_stopped = false; + qemuDomainSaveCookiePtr cookie; + virCPUDefPtr origCPU = NULL; virCheckFlags(VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING | VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED | @@ -15400,6 +15402,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, goto endjob; } + cookie = (qemuDomainSaveCookiePtr) snap->def->cookie; + switch ((virDomainState) snap->def->state) { case VIR_DOMAIN_RUNNING: case VIR_DOMAIN_PAUSED: @@ -15411,6 +15415,15 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, * to have finer control. */ if (virDomainObjIsActive(vm)) { /* Transitions 5, 6, 8, 9 */ + /* Replace the CPU in config and put the original one in priv + * once we're done. + */ + if (cookie && cookie->cpu && config->cpu) { + origCPU = config->cpu; + if (!(config->cpu = virCPUDefCopy(cookie->cpu))) + goto endjob; + } + /* Check for ABI compatibility. We need to do this check against * the migratable XML or it will always fail otherwise */ if (config && @@ -15470,8 +15483,11 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, * failed loadvm attempt? */ goto endjob; } - if (config) + if (config) { virDomainObjAssignDef(vm, config, false, NULL); + virCPUDefFree(priv->origCPU); + VIR_STEAL_PTR(priv->origCPU, origCPU); + } } else { /* Transitions 2, 3 */ load: @@ -15480,6 +15496,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, virDomainObjAssignDef(vm, config, false, NULL); rc = qemuProcessStart(snapshot->domain->conn, driver, vm, + cookie ? cookie->cpu : NULL, QEMU_ASYNC_JOB_START, NULL, -1, NULL, snap, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, VIR_QEMU_PROCESS_START_PAUSED); @@ -15573,7 +15590,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, start_flags |= paused ? VIR_QEMU_PROCESS_START_PAUSED : 0; qemuDomainEventQueue(driver, event); - rc = qemuProcessStart(snapshot->domain->conn, driver, vm, + rc = qemuProcessStart(snapshot->domain->conn, driver, vm, NULL, QEMU_ASYNC_JOB_START, NULL, -1, NULL, NULL, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags); @@ -15645,6 +15662,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, virObjectUnref(caps); virObjectUnref(cfg); virNWFilterUnlockFilterUpdates(); + virCPUDefFree(origCPU); return ret; } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index c03f1be575..a4540ce3c4 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2650,7 +2650,7 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, goto stopjob; } - if (qemuProcessInit(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN, + if (qemuProcessInit(driver, vm, mig->cpu, QEMU_ASYNC_JOB_MIGRATION_IN, true, VIR_QEMU_PROCESS_START_AUTODESTROY) < 0) goto stopjob; stopProcess = true; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 79f780ed46..a7abfc720e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3946,6 +3946,13 @@ qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver, if (qemuProcessVerifyCPUFeatures(def, cpu) < 0) goto cleanup; + /* Don't update the CPU if we already did so when starting a domain + * during migration, restore or snapshot revert. */ + if (priv->origCPU) { + ret = 0; + goto cleanup; + } + if (!(orig = virCPUDefCopy(def->cpu))) goto cleanup; @@ -4864,6 +4871,7 @@ qemuProcessStartValidate(virQEMUDriverPtr driver, int qemuProcessInit(virQEMUDriverPtr driver, virDomainObjPtr vm, + virCPUDefPtr updatedCPU, qemuDomainAsyncJob asyncJob, bool migration, unsigned int flags) @@ -4872,6 +4880,7 @@ qemuProcessInit(virQEMUDriverPtr driver, virCapsPtr caps = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; int stopFlags; + virCPUDefPtr origCPU = NULL; int ret = -1; VIR_DEBUG("vm=%p name=%s id=%d migration=%d", @@ -4896,6 +4905,9 @@ qemuProcessInit(virQEMUDriverPtr driver, vm->def->os.machine))) goto cleanup; + if (qemuDomainUpdateCPU(vm, updatedCPU, &origCPU) < 0) + goto cleanup; + if (qemuProcessStartValidate(driver, vm, priv->qemuCaps, caps, flags) < 0) goto cleanup; @@ -4928,11 +4940,14 @@ qemuProcessInit(virQEMUDriverPtr driver, if (qemuDomainSetPrivatePaths(driver, vm) < 0) goto stop; + + VIR_STEAL_PTR(priv->origCPU, origCPU); } ret = 0; cleanup: + virCPUDefFree(origCPU); virObjectUnref(cfg); virObjectUnref(caps); return ret; @@ -5963,6 +5978,7 @@ int qemuProcessStart(virConnectPtr conn, virQEMUDriverPtr driver, virDomainObjPtr vm, + virCPUDefPtr updatedCPU, qemuDomainAsyncJob asyncJob, const char *migrateFrom, int migrateFd, @@ -5993,7 +6009,8 @@ qemuProcessStart(virConnectPtr conn, if (!migrateFrom && !snapshot) flags |= VIR_QEMU_PROCESS_START_NEW; - if (qemuProcessInit(driver, vm, asyncJob, !!migrateFrom, flags) < 0) + if (qemuProcessInit(driver, vm, updatedCPU, + asyncJob, !!migrateFrom, flags) < 0) goto cleanup; if (migrateFrom) { @@ -6072,7 +6089,8 @@ qemuProcessCreatePretendCmd(virConnectPtr conn, flags |= VIR_QEMU_PROCESS_START_PRETEND; flags |= VIR_QEMU_PROCESS_START_NEW; - if (qemuProcessInit(driver, vm, QEMU_ASYNC_JOB_NONE, !!migrateURI, flags) < 0) + if (qemuProcessInit(driver, vm, NULL, QEMU_ASYNC_JOB_NONE, + !!migrateURI, flags) < 0) goto cleanup; if (qemuProcessPrepareDomain(conn, driver, vm, flags) < 0) @@ -6474,6 +6492,8 @@ void qemuProcessStop(virQEMUDriverPtr driver, /* clean up migration data */ VIR_FREE(priv->migTLSAlias); + virCPUDefFree(priv->origCPU); + priv->origCPU = NULL; /* clear previously used namespaces */ virBitmapFree(priv->namespaces); diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index 830d8cef84..c38310b47a 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -75,6 +75,7 @@ typedef enum { int qemuProcessStart(virConnectPtr conn, virQEMUDriverPtr driver, virDomainObjPtr vm, + virCPUDefPtr updatedCPU, qemuDomainAsyncJob asyncJob, const char *migrateFrom, int stdin_fd, @@ -93,6 +94,7 @@ virCommandPtr qemuProcessCreatePretendCmd(virConnectPtr conn, int qemuProcessInit(virQEMUDriverPtr driver, virDomainObjPtr vm, + virCPUDefPtr updatedCPU, qemuDomainAsyncJob asyncJob, bool migration, unsigned int flags); -- 2.13.1