diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 173fbca6ed..05e8b96aa4 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -10027,6 +10027,82 @@ qemuDomainUpdateCPU(virDomainObjPtr vm, return 0; } + +/** + * qemuDomainFixupCPUS: + * @vm: domain object + * @origCPU: original CPU used when the domain was started + * + * Libvirt older than 3.9.0 could have messed up the expansion of host-model + * CPU when reconnecting to a running domain by adding features QEMU does not + * support (such as cmt). This API fixes both the actual CPU provided by QEMU + * (stored in the domain object) and the @origCPU used when starting the + * domain. + * + * This is safe even if the original CPU definition used mode='custom' (rather + * than host-model) since we know QEMU was able to start the domain and thus + * the CPU definitions do not contain any features unknown to QEMU. + * + * This function can only be used on an active domain or when restoring a + * domain which was running. + * + * Returns 0 on success, -1 on error. + */ +int +qemuDomainFixupCPUs(virDomainObjPtr vm, + virCPUDefPtr *origCPU) +{ + virCPUDefPtr fixedCPU = NULL; + virCPUDefPtr fixedOrig = NULL; + virArch arch = vm->def->os.arch; + int ret = 0; + + if (!ARCH_IS_X86(arch)) + return 0; + + if (!vm->def->cpu || + vm->def->cpu->mode != VIR_CPU_MODE_CUSTOM || + !vm->def->cpu->model) + return 0; + + /* Missing origCPU means QEMU created exactly the same virtual CPU which + * we asked for or libvirt was too old to mess up the translation from + * host-model. + */ + if (!*origCPU) + return 0; + + if (virCPUDefFindFeature(vm->def->cpu, "cmt") && + (!(fixedCPU = virCPUDefCopyWithoutModel(vm->def->cpu)) || + virCPUDefCopyModelFilter(fixedCPU, vm->def->cpu, false, + virQEMUCapsCPUFilterFeatures, &arch) < 0)) + goto cleanup; + + if (virCPUDefFindFeature(*origCPU, "cmt") && + (!(fixedOrig = virCPUDefCopyWithoutModel(*origCPU)) || + virCPUDefCopyModelFilter(fixedOrig, *origCPU, false, + virQEMUCapsCPUFilterFeatures, &arch) < 0)) + goto cleanup; + + if (fixedCPU) { + virCPUDefFree(vm->def->cpu); + VIR_STEAL_PTR(vm->def->cpu, fixedCPU); + } + + if (fixedOrig) { + virCPUDefFree(*origCPU); + VIR_STEAL_PTR(*origCPU, fixedOrig); + } + + ret = 0; + + cleanup: + virCPUDefFree(fixedCPU); + virCPUDefFree(fixedOrig); + return ret; +} + + char * qemuDomainGetMachineName(virDomainObjPtr vm) { diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 00226a3fc8..5201c6a0ac 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -971,6 +971,10 @@ qemuDomainUpdateCPU(virDomainObjPtr vm, virCPUDefPtr cpu, virCPUDefPtr *origCPU); +int +qemuDomainFixupCPUs(virDomainObjPtr vm, + virCPUDefPtr *origCPU); + char * qemuDomainGetMachineName(virDomainObjPtr vm); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7c8ad32675..fb4d722368 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6551,6 +6551,13 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, } } + /* No cookie means libvirt which saved the domain was too old to mess up + * the CPU definitions. + */ + if (cookie && + qemuDomainFixupCPUs(vm, &cookie->cpu) < 0) + goto cleanup; + if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL, asyncJob, "stdio", *fd, path, NULL, VIR_NETDEV_VPORT_PROFILE_OP_RESTORE, @@ -15756,6 +15763,13 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, if (config) virDomainObjAssignDef(vm, config, false, NULL); + /* No cookie means libvirt which saved the domain was too old to + * mess up the CPU definitions. + */ + if (cookie && + qemuDomainFixupCPUs(vm, &cookie->cpu) < 0) + goto cleanup; + rc = qemuProcessStart(snapshot->domain->conn, driver, vm, cookie ? cookie->cpu : NULL, QEMU_ASYNC_JOB_START, NULL, -1, NULL, snap, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f1ad7ad1f1..e062194294 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6881,6 +6881,7 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, virDomainObjPtr vm) { virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false); + qemuDomainObjPrivatePtr priv = vm->privateData; virCPUDefPtr host = NULL; virCPUDefPtr cpu = NULL; int ret = -1; @@ -6915,6 +6916,14 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, if (qemuProcessUpdateCPU(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) goto cleanup; + } else if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION)) { + /* We only try to fix CPUs when the libvirt/QEMU combo used to start + * the domain did not know about query-cpu-model-expansion in which + * case the host-model is known to not contain features which QEMU + * doesn't know about. + */ + if (qemuDomainFixupCPUs(vm, &priv->origCPU) < 0) + goto cleanup; } ret = 0;