diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 5b1940b47f..992a0339cc 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -712,6 +712,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: * diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index c329eb1349..7d6d3e9211 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -86,6 +86,11 @@ typedef int (*virCPUArchUpdate)(virCPUDefPtr guest, 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 6719acee25..a43bb2bdf2 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -2677,6 +2677,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 8af5454b40..165d8cb252 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1016,6 +1016,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 db98a2f2ca..780f9587a8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3840,12 +3840,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; @@ -3854,7 +3855,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; @@ -3871,12 +3872,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; } @@ -5720,8 +5727,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");