When starting a domain with custom guest CPU specification QEMU may add or remove some CPU features. There are several reasons for this, e.g., QEMU/KVM does not support some requested features or the definition of the requested CPU model in libvirt's cpu_map.xml differs from the one QEMU is using. We can't really avoid this because CPU models are allowed to change with machine types and libvirt doesn't know (and probably doesn't even want to know) about such changes. Thus when we want to make sure guest ABI doesn't change when a domain gets migrated to another host, we need to update our live CPU definition according to the CPU QEMU created. Once updated, we will change CPU checking to VIR_CPU_CHECK_FULL to make sure the virtual CPU created after migration exactly matches the one on the source. https://bugzilla.redhat.com/show_bug.cgi?id=822148 https://bugzilla.redhat.com/show_bug.cgi?id=824989 Signed-off-by: Jiri Denemark <jdenemar@xxxxxxxxxx> --- src/cpu/cpu.c | 44 ++++++++++++++++++++++++++++++++++++ src/cpu/cpu.h | 12 ++++++++++ src/cpu/cpu_x86.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 1 + src/qemu/qemu_process.c | 19 +++++++++++----- 5 files changed, 129 insertions(+), 6 deletions(-) diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 5b1940b47..992a0339c 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -713,6 +713,50 @@ virCPUUpdate(virArch arch, /** + * virCPUUpdateLive: + * + * @arch: CPU architecture + * @cpu: guest CPU definition to be updated + * @dataEnabled: CPU data of the virtual CPU + * @dataDisabled: CPU data with features requested by @cpu but disabled by the + * hypervisor + * + * Update custom mode CPU according to the virtual CPU created by the + * hypervisor. + * + * Returns -1 on error, + * 0 when the CPU was successfully updated, + * 1 when the operation does not make sense on the CPU or it is not + * supported for the given architecture. + */ +int +virCPUUpdateLive(virArch arch, + virCPUDefPtr cpu, + virCPUDataPtr dataEnabled, + virCPUDataPtr dataDisabled) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, cpu=%p, dataEnabled=%p, dataDisabled=%p", + virArchToString(arch), cpu, dataEnabled, dataDisabled); + + if (!(driver = cpuGetSubDriver(arch))) + return -1; + + if (!driver->updateLive) + return 1; + + if (cpu->mode != VIR_CPU_MODE_CUSTOM) + return 1; + + if (driver->updateLive(cpu, dataEnabled, dataDisabled) < 0) + return -1; + + return 0; +} + + +/** * virCPUCheckFeature: * * @arch: CPU architecture diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index c329eb134..7d6d3e921 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -87,6 +87,11 @@ typedef int const virCPUDef *host); typedef int +(*virCPUArchUpdateLive)(virCPUDefPtr cpu, + virCPUDataPtr dataEnabled, + virCPUDataPtr dataDisabled); + +typedef int (*virCPUArchCheckFeature)(const virCPUDef *cpu, const char *feature); @@ -122,6 +127,7 @@ struct cpuArchDriver { virCPUArchGetHost getHost; cpuArchBaseline baseline; virCPUArchUpdate update; + virCPUArchUpdateLive updateLive; virCPUArchCheckFeature checkFeature; virCPUArchDataCheckFeature dataCheckFeature; virCPUArchDataFormat dataFormat; @@ -198,6 +204,12 @@ virCPUUpdate(virArch arch, const virCPUDef *host) ATTRIBUTE_NONNULL(2); +int +virCPUUpdateLive(virArch arch, + virCPUDefPtr cpu, + virCPUDataPtr dataEnabled, + virCPUDataPtr dataDisabled) + ATTRIBUTE_NONNULL(2); int virCPUCheckFeature(virArch arch, diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index 6719acee2..a43bb2bdf 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -2678,6 +2678,64 @@ virCPUx86Update(virCPUDefPtr guest, static int +virCPUx86UpdateLive(virCPUDefPtr cpu, + virCPUDataPtr dataEnabled, + virCPUDataPtr dataDisabled) +{ + virCPUx86MapPtr map; + virCPUx86ModelPtr model = NULL; + virCPUx86Data enabled = VIR_CPU_X86_DATA_INIT; + virCPUx86Data disabled = VIR_CPU_X86_DATA_INIT; + size_t i; + int ret = -1; + + if (!(map = virCPUx86GetMap())) + return -1; + + if (!(model = x86ModelFromCPU(cpu, map, -1))) + goto cleanup; + + if (dataEnabled && + x86DataCopy(&enabled, &dataEnabled->data.x86) < 0) + goto cleanup; + + if (dataDisabled && + x86DataCopy(&disabled, &dataDisabled->data.x86) < 0) + goto cleanup; + + x86DataSubtract(&enabled, &model->data); + + for (i = 0; i < map->nfeatures; i++) { + virCPUx86FeaturePtr feature = map->features[i]; + + if (x86DataIsSubset(&enabled, &feature->data)) { + VIR_DEBUG("Adding feature '%s' enabled by the hypervisor", + feature->name); + if (virCPUDefUpdateFeature(cpu, feature->name, + VIR_CPU_FEATURE_REQUIRE) < 0) + goto cleanup; + } + + if (x86DataIsSubset(&disabled, &feature->data)) { + VIR_DEBUG("Removing feature '%s' disabled by the hypervisor", + feature->name); + if (virCPUDefUpdateFeature(cpu, feature->name, + VIR_CPU_FEATURE_DISABLE) < 0) + goto cleanup; + } + } + + ret = 0; + + cleanup: + x86ModelFree(model); + virCPUx86DataClear(&enabled); + virCPUx86DataClear(&disabled); + return ret; +} + + +static int virCPUx86CheckFeature(const virCPUDef *cpu, const char *name) { @@ -2854,6 +2912,7 @@ struct cpuArchDriver cpuDriverX86 = { #endif .baseline = x86Baseline, .update = virCPUx86Update, + .updateLive = virCPUx86UpdateLive, .checkFeature = virCPUx86CheckFeature, .dataCheckFeature = virCPUx86DataCheckFeature, .dataFormat = virCPUx86DataFormat, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4efea0098..0fbbe257f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1009,6 +1009,7 @@ virCPUGetHost; virCPUGetModels; virCPUTranslate; virCPUUpdate; +virCPUUpdateLive; # cpu/cpu_x86.h diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f2aa134d4..3d9ab0cdc 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3832,12 +3832,13 @@ qemuProcessVerifyCPUFeatures(virDomainDefPtr def, static int -qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob) +qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) { virDomainDefPtr def = vm->def; virCPUDataPtr cpu = NULL; + virCPUDataPtr disabled = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; int rc; int ret = -1; @@ -3846,7 +3847,7 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) goto cleanup; - rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu, NULL); + rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu, &disabled); if (qemuDomainObjExitMonitor(driver, vm) < 0) goto cleanup; @@ -3863,12 +3864,18 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, if (qemuProcessVerifyCPUFeatures(def, cpu) < 0) goto cleanup; + + if ((rc = virCPUUpdateLive(def->os.arch, def->cpu, cpu, disabled)) < 0) + goto cleanup; + else if (rc == 0) + def->cpu->check = VIR_CPU_CHECK_FULL; } ret = 0; cleanup: virCPUDataFree(cpu); + virCPUDataFree(disabled); return ret; } @@ -5712,8 +5719,8 @@ qemuProcessLaunch(virConnectPtr conn, if (qemuConnectAgent(driver, vm) < 0) goto cleanup; - VIR_DEBUG("Detecting if required emulator features are present"); - if (qemuProcessVerifyGuestCPU(driver, vm, asyncJob) < 0) + VIR_DEBUG("Verifying and updating provided guest CPU"); + if (qemuProcessUpdateLiveGuestCPU(driver, vm, asyncJob) < 0) goto cleanup; VIR_DEBUG("Setting up post-init cgroup restrictions"); -- 2.12.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list