[PATCH 00/10] qemu: Fix CPU feature reporting on AMD CPUs
See the last patch for detailed description. Jiri Denemark (10): qemu_capabilities: Split virQEMUCapsFillDomainCPUCaps qemu: Move domain caps flags handling to virQEMUCapsFillDomainCPUHostModel qemu_capabilities: Use g_autoptr in virQEMUCapsInitHostCPUModel qemu_capabilities: Split conditions in virQEMUCapsInitHostCPUModel qemu_capabilities: Cache expanded CPU util: Publish and mock virHostCPUGetMSRFromKVM cpu_x86: Introduce virCPUx86DataAddMSR cpu: Introduce virCPUUpdateFeatures Clarify documentation of VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES qemu_capabilities: Fix domain capabilities on AMD CPUs docs/manpages/virsh.rst | 4 +- include/libvirt/libvirt-domain.h | 2 +- src/cpu/cpu.c | 33 +++++ src/cpu/cpu.h | 12 ++ src/cpu/cpu_x86.c | 54 ++++++-- src/cpu/cpu_x86.h | 4 + src/libvirt-domain.c | 7 +- src/libvirt_private.syms | 3 + src/qemu/qemu_capabilities.c | 223 ++++++++++++++++++++++--------- src/qemu/qemu_capabilities.h | 7 +- src/qemu/qemu_conf.c | 6 +- src/qemu/qemu_conf.h | 3 +- src/qemu/qemu_driver.c | 15 +-- src/util/virhostcpu.c | 16 ++- src/util/virhostcpu.h | 3 + tests/domaincapstest.c | 2 +- tests/qemucpumock.c | 22 +++ tools/virsh-host.c | 2 +- 18 files changed, 319 insertions(+), 99 deletions(-) -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> Each CPU mode is filled in its own dedicated function. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 96 ++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 32 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 12af7c4616..90bd43e628 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6606,6 +6606,66 @@ virQEMUCapsFillDomainOSCaps(virDomainCapsOS *os, } +static void +virQEMUCapsFillDomainCPUHostPassthrough(virQEMUCaps *qemuCaps, + virDomainCaps *domCaps) +{ + domCaps->cpu.hostPassthrough = true; + + domCaps->cpu.hostPassthroughMigratable.report = true; + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE)) { + VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.hostPassthroughMigratable, + VIR_TRISTATE_SWITCH_ON); + } + VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.hostPassthroughMigratable, + VIR_TRISTATE_SWITCH_OFF); +} + + +static void +virQEMUCapsFillDomainCPUMaximum(virDomainCaps *domCaps) +{ + domCaps->cpu.maximum = true; + + domCaps->cpu.maximumMigratable.report = true; + VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.maximumMigratable, + VIR_TRISTATE_SWITCH_ON); + VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.maximumMigratable, + VIR_TRISTATE_SWITCH_OFF); +} + + +static void +virQEMUCapsFillDomainCPUHostModel(virQEMUCaps *qemuCaps, + virDomainCaps *domCaps) +{ + virCPUDef *cpu = virQEMUCapsGetHostModel(qemuCaps, domCaps->virttype, + VIR_QEMU_CAPS_HOST_CPU_REPORTED); + + domCaps->cpu.hostModel = virCPUDefCopy(cpu); + domCaps->cpu.hostModel->addr = virQEMUCapsGetHostPhysAddr(qemuCaps, + domCaps->virttype); +} + + +static void +virQEMUCapsFillDomainCPUCustom(virQEMUCaps *qemuCaps, + virDomainCaps *domCaps) +{ + const char *forbidden[] = { "host", NULL }; + g_auto(GStrv) models = NULL; + + if (virCPUGetModels(domCaps->arch, &models) >= 0) { + domCaps->cpu.custom = virQEMUCapsGetCPUModels(qemuCaps, + domCaps->virttype, + (const char **)models, + forbidden); + } else { + domCaps->cpu.custom = NULL; + } +} + + static void virQEMUCapsFillDomainCPUCaps(virQEMUCaps *qemuCaps, virArch hostarch, @@ -6614,53 +6674,25 @@ virQEMUCapsFillDomainCPUCaps(virQEMUCaps *qemuCaps, if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype, VIR_CPU_MODE_HOST_PASSTHROUGH, domCaps->machine)) { - domCaps->cpu.hostPassthrough = true; - - domCaps->cpu.hostPassthroughMigratable.report = true; - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE)) { - VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.hostPassthroughMigratable, - VIR_TRISTATE_SWITCH_ON); - } - VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.hostPassthroughMigratable, - VIR_TRISTATE_SWITCH_OFF); + virQEMUCapsFillDomainCPUHostPassthrough(qemuCaps, domCaps); } if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype, VIR_CPU_MODE_MAXIMUM, domCaps->machine)) { - domCaps->cpu.maximum = true; - - domCaps->cpu.maximumMigratable.report = true; - VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.maximumMigratable, - VIR_TRISTATE_SWITCH_ON); - VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.maximumMigratable, - VIR_TRISTATE_SWITCH_OFF); + virQEMUCapsFillDomainCPUMaximum(domCaps); } if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype, VIR_CPU_MODE_HOST_MODEL, domCaps->machine)) { - virCPUDef *cpu = virQEMUCapsGetHostModel(qemuCaps, domCaps->virttype, - VIR_QEMU_CAPS_HOST_CPU_REPORTED); - domCaps->cpu.hostModel = virCPUDefCopy(cpu); - domCaps->cpu.hostModel->addr = virQEMUCapsGetHostPhysAddr(qemuCaps, - domCaps->virttype); + virQEMUCapsFillDomainCPUHostModel(qemuCaps, domCaps); } if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype, VIR_CPU_MODE_CUSTOM, domCaps->machine)) { - const char *forbidden[] = { "host", NULL }; - g_auto(GStrv) models = NULL; - - if (virCPUGetModels(domCaps->arch, &models) >= 0) { - domCaps->cpu.custom = virQEMUCapsGetCPUModels(qemuCaps, - domCaps->virttype, - (const char **)models, - forbidden); - } else { - domCaps->cpu.custom = NULL; - } + virQEMUCapsFillDomainCPUCustom(qemuCaps, domCaps); } } -- 2.54.0
On Wed, May 27, 2026 at 12:07:13 +0200, Jiri Denemark via Devel wrote:
From: Jiri Denemark <jdenemar@redhat.com>
Each CPU mode is filled in its own dedicated function.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 96 ++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 32 deletions(-)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
From: Jiri Denemark <jdenemar@redhat.com> We will need to generate the capabilities in a different way based on the flags. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 36 ++++++++++++++++++++++++++---------- src/qemu/qemu_capabilities.h | 3 ++- src/qemu/qemu_conf.c | 6 ++++-- src/qemu/qemu_conf.h | 3 ++- src/qemu/qemu_driver.c | 15 ++------------- tests/domaincapstest.c | 2 +- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 90bd43e628..a612011ee1 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -795,6 +795,9 @@ struct _virQEMUCapsHostCPUData { unsigned int physAddrSize; /* Host CPU definition reported in domain capabilities. */ virCPUDef *reported; + /* Host CPU definition with possibly extra features that are supported by + * the stack, but should not be included in a host-model CPU. */ + virCPUDef *supported; /* Migratable host CPU definition used for updating guest CPU. */ virCPUDef *migratable; /* CPU definition with features detected by libvirt using virCPUGetHost @@ -6637,14 +6640,25 @@ virQEMUCapsFillDomainCPUMaximum(virDomainCaps *domCaps) static void virQEMUCapsFillDomainCPUHostModel(virQEMUCaps *qemuCaps, - virDomainCaps *domCaps) + virDomainCaps *domCaps, + unsigned int flags) { - virCPUDef *cpu = virQEMUCapsGetHostModel(qemuCaps, domCaps->virttype, - VIR_QEMU_CAPS_HOST_CPU_REPORTED); + virQEMUCapsHostCPUType cpuType = VIR_QEMU_CAPS_HOST_CPU_REPORTED; + virCPUDef *cpu; - domCaps->cpu.hostModel = virCPUDefCopy(cpu); - domCaps->cpu.hostModel->addr = virQEMUCapsGetHostPhysAddr(qemuCaps, - domCaps->virttype); + cpu = virCPUDefCopy(virQEMUCapsGetHostModel(qemuCaps, domCaps->virttype, + cpuType)); + domCaps->cpu.hostModel = cpu; + + cpu->addr = virQEMUCapsGetHostPhysAddr(qemuCaps, domCaps->virttype); + + if (flags & VIR_CONNECT_GET_DOMAIN_CAPABILITIES_DISABLE_DEPRECATED_FEATURES) { + virQEMUCapsUpdateCPUDeprecatedFeatures(qemuCaps, domCaps->virttype, + cpu, VIR_CPU_FEATURE_DISABLE); + } + + if (flags & VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES) + virCPUExpandFeatures(domCaps->arch, cpu); } @@ -6669,7 +6683,8 @@ virQEMUCapsFillDomainCPUCustom(virQEMUCaps *qemuCaps, static void virQEMUCapsFillDomainCPUCaps(virQEMUCaps *qemuCaps, virArch hostarch, - virDomainCaps *domCaps) + virDomainCaps *domCaps, + unsigned int flags) { if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype, VIR_CPU_MODE_HOST_PASSTHROUGH, @@ -6686,7 +6701,7 @@ virQEMUCapsFillDomainCPUCaps(virQEMUCaps *qemuCaps, if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype, VIR_CPU_MODE_HOST_MODEL, domCaps->machine)) { - virQEMUCapsFillDomainCPUHostModel(qemuCaps, domCaps); + virQEMUCapsFillDomainCPUHostModel(qemuCaps, domCaps, flags); } if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype, @@ -7315,7 +7330,8 @@ virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, virQEMUCaps *qemuCaps, virArch hostarch, virDomainCaps *domCaps, - bool privileged) + bool privileged, + unsigned int flags) { virDomainCapsOS *os = &domCaps->os; virDomainCapsDeviceDisk *disk = &domCaps->disk; @@ -7358,7 +7374,7 @@ virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, firmwares, nfirmwares) < 0) return -1; - virQEMUCapsFillDomainCPUCaps(qemuCaps, hostarch, domCaps); + virQEMUCapsFillDomainCPUCaps(qemuCaps, hostarch, domCaps, flags); virQEMUCapsFillDomainMemoryBackingCaps(qemuCaps, memoryBacking); virQEMUCapsFillDomainDeviceDiskCaps(qemuCaps, domCaps->machine, disk); virQEMUCapsFillDomainDeviceGraphicsCaps(cfg, qemuCaps, graphics); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index c26e06104d..b26b5d3145 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -903,7 +903,8 @@ int virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, virQEMUCaps *qemuCaps, virArch hostarch, virDomainCaps *domCaps, - bool privileged); + bool privileged, + unsigned int flags); void virQEMUCapsFillDomainMemoryBackingCaps(virQEMUCaps *qemuCaps, virDomainCapsMemoryBacking *memoryBacking); diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 9c32310096..9b94733a38 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1753,7 +1753,8 @@ virQEMUDriverGetDomainCapabilities(virQEMUDriver *driver, virQEMUCaps *qemuCaps, const char *machine, virArch arch, - virDomainVirtType virttype) + virDomainVirtType virttype, + unsigned int flags) { g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); g_autoptr(virDomainCaps) domCaps = NULL; @@ -1787,7 +1788,8 @@ virQEMUDriverGetDomainCapabilities(virQEMUDriver *driver, qemuCaps, driver->hostarch, domCaps, - driver->privileged) < 0) + driver->privileged, + flags) < 0) return NULL; return g_steal_pointer(&domCaps); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 511ab77f71..a2ca4ddbdb 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -383,7 +383,8 @@ virQEMUDriverGetDomainCapabilities(virQEMUDriver *driver, virQEMUCaps *qemuCaps, const char *machine, virArch arch, - virDomainVirtType virttype); + virDomainVirtType virttype, + unsigned int flags); int qemuDriverAllocateID(virQEMUDriver *driver); virDomainXMLOption *virQEMUDriverCreateXMLConf(virQEMUDriver *driver, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index bcafacfb60..c351ce40af 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16982,21 +16982,10 @@ qemuConnectGetDomainCapabilities(virConnectPtr conn, if (!(domCaps = virQEMUDriverGetDomainCapabilities(driver, qemuCaps, machine, - arch, virttype))) + arch, virttype, + flags))) return NULL; - if (flags & VIR_CONNECT_GET_DOMAIN_CAPABILITIES_DISABLE_DEPRECATED_FEATURES) { - virQEMUCapsUpdateCPUDeprecatedFeatures(qemuCaps, virttype, - domCaps->cpu.hostModel, - VIR_CPU_FEATURE_DISABLE); - } - - if (flags & VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES) { - virCPUDef *cpu = domCaps->cpu.hostModel; - if (cpu && virCPUExpandFeatures(arch, cpu) < 0) - return NULL; - } - return virDomainCapsFormat(domCaps); } diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c index 5b2fc80f0a..f2248c2435 100644 --- a/tests/domaincapstest.c +++ b/tests/domaincapstest.c @@ -101,7 +101,7 @@ fillQemuCaps(virDomainCaps *domCaps, if (virQEMUCapsFillDomainCaps(cfg, qemuCaps, domCaps->arch, domCaps, - false) < 0) + false, 0) < 0) return -1; /* As of f05b6a918e28 we are expecting to see OVMF_CODE.fd file which -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index a612011ee1..581b853589 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -4103,11 +4103,11 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, virArch hostArch, virDomainVirtType type) { - virCPUDef *cpu = NULL; - virCPUDef *cpuExpanded = NULL; - virCPUDef *migCPU = NULL; - virCPUDef *hostCPU = NULL; - virCPUDef *fullCPU = NULL; + g_autoptr(virCPUDef) cpu = NULL; + g_autoptr(virCPUDef) cpuExpanded = NULL; + g_autoptr(virCPUDef) migCPU = NULL; + g_autoptr(virCPUDef) hostCPU = NULL; + g_autoptr(virCPUDef) fullCPU = NULL; unsigned int physAddrSize = 0; size_t i; int rc; @@ -4181,19 +4181,15 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, if (virQEMUCapsTypeIsAccelerated(type)) virHostCPUGetPhysAddrSize(hostArch, &physAddrSize); - virQEMUCapsSetHostModel(qemuCaps, type, physAddrSize, cpu, migCPU, fullCPU); + virQEMUCapsSetHostModel(qemuCaps, type, physAddrSize, + g_steal_pointer(&cpu), + g_steal_pointer(&migCPU), + g_steal_pointer(&fullCPU)); - cleanup: - virCPUDefFree(cpuExpanded); - virCPUDefFree(hostCPU); return; error: - virCPUDefFree(cpu); - virCPUDefFree(migCPU); - virCPUDefFree(fullCPU); virResetLastError(); - goto cleanup; } -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> Having 'else' after goto is useless. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 581b853589..166d6ed540 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -4118,9 +4118,17 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, if (!(cpu = virQEMUCapsNewHostCPUModel())) goto error; - if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, cpu, false)) < 0) { + if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, cpu, false)) < 0) goto error; - } else if (rc == 1) { + + if (rc == 2) { + VIR_DEBUG("QEMU does not provide CPU model for arch=%s virttype=%s", + virArchToString(qemuCaps->arch), + virDomainVirtTypeToString(type)); + goto error; + } + + if (rc == 1) { g_autoptr(virDomainCapsCPUModels) cpuModels = NULL; VIR_DEBUG("No host CPU model info from QEMU; probing host CPU directly"); @@ -4131,11 +4139,6 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, virCPUDefCopyModelFilter(cpu, hostCPU, true, virQEMUCapsCPUFilterFeatures, &qemuCaps->arch); - } else if (rc == 2) { - VIR_DEBUG("QEMU does not provide CPU model for arch=%s virttype=%s", - virArchToString(qemuCaps->arch), - virDomainVirtTypeToString(type)); - goto error; } else if (virQEMUCapsTypeIsAccelerated(type) && virCPUGetHostIsSupported(qemuCaps->arch)) { if (!(fullCPU = virQEMUCapsProbeHostCPU(qemuCaps->arch, NULL))) @@ -4156,9 +4159,10 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, if (!(migCPU = virQEMUCapsNewHostCPUModel())) goto error; - if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, migCPU, true)) < 0) { + if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, migCPU, true)) < 0) goto error; - } else if (rc == 1) { + + if (rc == 1) { VIR_DEBUG("CPU migratability not provided by QEMU"); virCPUDefFree(migCPU); -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> When probing host model CPU we already expand it to get a list of all CPU features. Let's store the expanded CPU definition in virQEMUCaps and copy it to domain capabilities when requested by the VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES flag instead of expanding the CPU over and over on each request. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 36 +++++++++++++++++++++++++----------- src/qemu/qemu_capabilities.h | 4 ++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 166d6ed540..8295a9ae9d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -1978,6 +1978,9 @@ virQEMUCapsHostCPUDataCopy(virQEMUCapsHostCPUData *dst, if (src->reported) dst->reported = virCPUDefCopy(src->reported); + if (src->supported) + dst->supported = virCPUDefCopy(src->supported); + if (src->migratable) dst->migratable = virCPUDefCopy(src->migratable); @@ -1991,6 +1994,7 @@ virQEMUCapsHostCPUDataClear(virQEMUCapsHostCPUData *cpuData) { qemuMonitorCPUModelInfoFree(cpuData->info); virCPUDefFree(cpuData->reported); + virCPUDefFree(cpuData->supported); virCPUDefFree(cpuData->migratable); virCPUDefFree(cpuData->full); @@ -2317,6 +2321,9 @@ virQEMUCapsGetHostModel(virQEMUCaps *qemuCaps, /* 'full' is non-NULL only if we have data from both QEMU and * virCPUGetHost */ return cpuData->full ? cpuData->full : cpuData->reported; + + case VIR_QEMU_CAPS_HOST_CPU_SUPPORTED: + return cpuData->supported; } return NULL; @@ -2328,6 +2335,7 @@ virQEMUCapsSetHostModel(virQEMUCaps *qemuCaps, virDomainVirtType type, unsigned int physAddrSize, virCPUDef *reported, + virCPUDef *supported, virCPUDef *migratable, virCPUDef *full) { @@ -2336,6 +2344,7 @@ virQEMUCapsSetHostModel(virQEMUCaps *qemuCaps, cpuData = &virQEMUCapsGetAccel(qemuCaps, type)->hostCPU; cpuData->physAddrSize = physAddrSize; cpuData->reported = reported; + cpuData->supported = supported; cpuData->migratable = migratable; cpuData->full = full; } @@ -4139,16 +4148,18 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, virCPUDefCopyModelFilter(cpu, hostCPU, true, virQEMUCapsCPUFilterFeatures, &qemuCaps->arch); - } else if (virQEMUCapsTypeIsAccelerated(type) && - virCPUGetHostIsSupported(qemuCaps->arch)) { + } + + cpuExpanded = virCPUDefCopy(cpu); + if (virCPUExpandFeatures(qemuCaps->arch, cpuExpanded) < 0) + goto error; + + if (rc == 0 && + virQEMUCapsTypeIsAccelerated(type) && + virCPUGetHostIsSupported(qemuCaps->arch)) { if (!(fullCPU = virQEMUCapsProbeHostCPU(qemuCaps->arch, NULL))) goto error; - cpuExpanded = virCPUDefCopy(cpu); - - if (virCPUExpandFeatures(qemuCaps->arch, cpuExpanded) < 0) - goto error; - for (i = 0; i < cpuExpanded->nfeatures; i++) { if (cpuExpanded->features[i].policy == VIR_CPU_FEATURE_REQUIRE) virCPUDefUpdateFeature(fullCPU, cpuExpanded->features[i].name, @@ -4187,6 +4198,7 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, virQEMUCapsSetHostModel(qemuCaps, type, physAddrSize, g_steal_pointer(&cpu), + g_steal_pointer(&cpuExpanded), g_steal_pointer(&migCPU), g_steal_pointer(&fullCPU)); @@ -6643,9 +6655,14 @@ virQEMUCapsFillDomainCPUHostModel(virQEMUCaps *qemuCaps, virDomainCaps *domCaps, unsigned int flags) { - virQEMUCapsHostCPUType cpuType = VIR_QEMU_CAPS_HOST_CPU_REPORTED; + virQEMUCapsHostCPUType cpuType; virCPUDef *cpu; + if (flags & VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES) + cpuType = VIR_QEMU_CAPS_HOST_CPU_SUPPORTED; + else + cpuType = VIR_QEMU_CAPS_HOST_CPU_REPORTED; + cpu = virCPUDefCopy(virQEMUCapsGetHostModel(qemuCaps, domCaps->virttype, cpuType)); domCaps->cpu.hostModel = cpu; @@ -6656,9 +6673,6 @@ virQEMUCapsFillDomainCPUHostModel(virQEMUCaps *qemuCaps, virQEMUCapsUpdateCPUDeprecatedFeatures(qemuCaps, domCaps->virttype, cpu, VIR_CPU_FEATURE_DISABLE); } - - if (flags & VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES) - virCPUExpandFeatures(domCaps->arch, cpu); } diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index b26b5d3145..53db699ae7 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -795,6 +795,10 @@ typedef enum { * combined with features reported by QEMU. This is used for backward * compatible comparison between a guest CPU and a host CPU. */ VIR_QEMU_CAPS_HOST_CPU_FULL, + /* Host CPU definition containing all supported features, including those + * that should not be automatically included in a host-model CPU and + * features that are implicitly enabled by the selected CPU model. */ + VIR_QEMU_CAPS_HOST_CPU_SUPPORTED, } virQEMUCapsHostCPUType; virCPUDef *virQEMUCapsGetHostModel(virQEMUCaps *qemuCaps, -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> The function will later be called when probing QEMU capabilities. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virhostcpu.c | 16 +++++++++++++++- src/util/virhostcpu.h | 3 +++ tests/qemucpumock.c | 22 ++++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2391f01bc7..3653459ef1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2619,6 +2619,7 @@ virHostCPUGetKVMMaxVCPUs; virHostCPUGetMap; virHostCPUGetMicrocodeVersion; virHostCPUGetMSR; +virHostCPUGetMSRFromKVM; virHostCPUGetOnline; virHostCPUGetOnlineBitmap; virHostCPUGetPhysAddrSize; diff --git a/src/util/virhostcpu.c b/src/util/virhostcpu.c index 60743765a4..afa9ee3849 100644 --- a/src/util/virhostcpu.c +++ b/src/util/virhostcpu.c @@ -1376,7 +1376,12 @@ virHostCPUGetMicrocodeVersion(virArch hostArch G_GNUC_UNUSED) #if WITH_LINUX_KVM_H && defined(KVM_GET_MSRS) && \ (defined(__i386__) || defined(__x86_64__)) -static int +/* + * Returns 0 on success, + * 1 when the MSR is not supported by the host CPU, + * -1 on error. + */ +int virHostCPUGetMSRFromKVM(unsigned long index, uint64_t *result) { @@ -1613,6 +1618,15 @@ virHostCPUGetCPUID(void) return NULL; } +int +virHostCPUGetMSRFromKVM(unsigned long index G_GNUC_UNUSED, + uint64_t *result G_GNUC_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Reading MSRs is not supported on this platform")); + return -1; +} + int virHostCPUGetMSR(unsigned long index G_GNUC_UNUSED, uint64_t *msr G_GNUC_UNUSED) diff --git a/src/util/virhostcpu.h b/src/util/virhostcpu.h index 238054ed34..4df126dcc4 100644 --- a/src/util/virhostcpu.h +++ b/src/util/virhostcpu.h @@ -78,6 +78,9 @@ int virHostCPUGetOnline(unsigned int cpu, bool *online); unsigned int virHostCPUGetMicrocodeVersion(virArch hostArch) ATTRIBUTE_MOCKABLE; +int virHostCPUGetMSRFromKVM(unsigned long index, + uint64_t *result) ATTRIBUTE_MOCKABLE; + int virHostCPUGetMSR(unsigned long index, uint64_t *msr); diff --git a/tests/qemucpumock.c b/tests/qemucpumock.c index c85721f448..f8410912d4 100644 --- a/tests/qemucpumock.c +++ b/tests/qemucpumock.c @@ -23,6 +23,7 @@ #include "qemu/qemu_capspriv.h" #include "testutilshostcpus.h" #include "virarch.h" +#include "util/virhostcpu.h" virCPUDef * @@ -33,3 +34,24 @@ virQEMUCapsProbeHostCPU(virArch hostArch G_GNUC_UNUSED, return testUtilsHostCpusGetDefForModel(model); } + + +int +virHostCPUGetMSRFromKVM(unsigned long index, + uint64_t *result) +{ + if (index == 0x10a) { + /* Return some arbitrary bits in arch-capabilities MSR */ + *result = + 0x00000001 | /* rdctl-no */ + 0x00000008 | /* skip-l1dfl-vmentry */ + 0x00000020 | /* mds-no */ + 0x00000040 | /* pschange-mc-no */ + 0x04000000 | /* gds-no */ + 0x08000000; /* rfds-no */ + return 0; + } + + errno = ENOTSUP; + return -1; +} -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> This just makes the relevant part of virCPUx86GetHost reusable in other places. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/cpu/cpu_x86.c | 31 +++++++++++++++++++------------ src/cpu/cpu_x86.h | 4 ++++ src/libvirt_private.syms | 1 + 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index 3a18d859dc..93f1427e36 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -2911,18 +2911,8 @@ virCPUx86GetHost(virCPUDef *cpu, /* This is best effort since there might be no way to read the MSR * when we are not running as root. */ for (i = 0; i < nmsrs; i++) { - if (virHostCPUGetMSR(msrs[i], &msr) == 0) { - virCPUx86DataItem item = { - .type = VIR_CPU_X86_DATA_MSR, - .data.msr = { - .index = msrs[i], - .eax = msr & 0xffffffff, - .edx = msr >> 32, - }, - }; - - virCPUx86DataAdd(cpuData, &item); - } + if (virHostCPUGetMSR(msrs[i], &msr) == 0) + virCPUx86DataAddMSR(cpuData, msrs[i], msr); } ret = x86DecodeCPUData(cpu, cpuData, models); @@ -3460,6 +3450,23 @@ virCPUx86DataAdd(virCPUData *cpuData, } +void virCPUx86DataAddMSR(virCPUData *cpuData, + uint32_t index, + uint64_t value) +{ + virCPUx86DataItem item = { + .type = VIR_CPU_X86_DATA_MSR, + .data.msr = { + .index = index, + .eax = value & 0xffffffff, + .edx = value >> 32, + }, + }; + + virCPUx86DataAdd(cpuData, &item); +} + + void virCPUx86DataSetSignature(virCPUData *cpuData, unsigned int family, diff --git a/src/cpu/cpu_x86.h b/src/cpu/cpu_x86.h index 2cd965fea4..bbc2a16447 100644 --- a/src/cpu/cpu_x86.h +++ b/src/cpu/cpu_x86.h @@ -28,6 +28,10 @@ extern struct cpuArchDriver cpuDriverX86; void virCPUx86DataAdd(virCPUData *cpuData, const virCPUx86DataItem *cpuid); +void virCPUx86DataAddMSR(virCPUData *cpuData, + uint32_t index, + uint64_t value); + void virCPUx86DataSetSignature(virCPUData *cpuData, unsigned int family, unsigned int model, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3653459ef1..9d5e927b8a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1598,6 +1598,7 @@ virCPUValidateFeatures; # cpu/cpu_x86.h virCPUx86DataAdd; +virCPUx86DataAddMSR; virCPUx86DataGetSignature; virCPUx86DataSetSignature; virCPUx86DataSetVendor; -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> This new API can be used to update an existing CPU definition with features described by CPU data. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/cpu/cpu.c | 33 +++++++++++++++++++++++++++++++++ src/cpu/cpu.h | 12 ++++++++++++ src/cpu/cpu_x86.c | 23 +++++++++++++++++++++++ src/libvirt_private.syms | 1 + 4 files changed, 69 insertions(+) diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 233686485d..4231fffa02 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -1372,6 +1372,39 @@ virCPUGetCanonicalModel(virArch arch, } +/** virCPUUpdateFeatures: + * + * @arch: CPU architecture + * @cpu: CPU definition to update + * @cpuData: CPU data describing features + * @policy: to be used by the updated features + * + * Updates features described in @cpuData to use the specified @policy. Missing + * features will be automatically added to the CPU definition. + * + * Returns 0 on success, -1 otherwise. + */ +int +virCPUUpdateFeatures(virArch arch, + virCPUDef *cpu, + virCPUData *cpuData, + virCPUFeaturePolicy policy) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, cpu=%p, model=%s", + virArchToString(arch), cpu, NULLSTR(cpu->model)); + + if (!(driver = cpuGetSubDriver(arch))) + return -1; + + if (!driver->updateFeatures) + return 0; + + return driver->updateFeatures(cpu, cpuData, policy); +} + + /** * virCPUArchIsSupported: * diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index 36fd123675..65711ac085 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -143,6 +143,11 @@ typedef int typedef const char * (*virCPUArchGetCanonicalModel)(const char *model); +typedef int +(*virCPUArchUpdateFeatures)(virCPUDef *cpu, + virCPUData *cpuData, + virCPUFeaturePolicy policy); + struct cpuArchDriver { const char *name; const virArch *arch; @@ -172,6 +177,7 @@ struct cpuArchDriver { virCPUArchDataGetHost dataGetHost; virCPUArchGetCheckMode getCheckMode; virCPUArchGetCanonicalModel getCanonicalModel; + virCPUArchUpdateFeatures updateFeatures; }; @@ -332,6 +338,12 @@ const char * virCPUGetCanonicalModel(virArch arch, const char *model); +int +virCPUUpdateFeatures(virArch arch, + virCPUDef *cpu, + virCPUData *cpuData, + virCPUFeaturePolicy policy); + bool virCPUArchIsSupported(virArch arch); diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index 93f1427e36..f6a6a2a46c 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -3780,6 +3780,28 @@ virCPUx86GetCanonicalModel(const char *modelName) } +static int +virCPUx86UpdateFeatures(virCPUDef *cpu, + virCPUData *cpuData, + virCPUFeaturePolicy policy) +{ + virCPUx86Data *data = &cpuData->data.x86; + virCPUx86Map *map; + size_t i; + + if (!(map = virCPUx86GetMap())) + return -1; + + for (i = 0; i < map->nfeatures; i++) { + virCPUx86Feature *feature = map->features[i]; + if (x86DataIsSubset(data, &feature->data)) + virCPUDefUpdateFeature(cpu, feature->name, policy); + } + + return 0; +} + + struct cpuArchDriver cpuDriverX86 = { .name = "x86", .arch = archs, @@ -3814,4 +3836,5 @@ struct cpuArchDriver cpuDriverX86 = { #endif .getCheckMode = virCPUx86GetCheckMode, .getCanonicalModel = virCPUx86GetCanonicalModel, + .updateFeatures = virCPUx86UpdateFeatures, }; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 9d5e927b8a..e1f499899e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1592,6 +1592,7 @@ virCPUGetVendorForModel; virCPUProbeHost; virCPUTranslate; virCPUUpdate; +virCPUUpdateFeatures; virCPUUpdateLive; virCPUValidateFeatures; -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> The flag is designed for showing all CPU features supported by the virt stack. But the description was not really consistent in all places. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- docs/manpages/virsh.rst | 4 +++- include/libvirt/libvirt-domain.h | 2 +- src/libvirt-domain.c | 7 +++++-- tools/virsh-host.c | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index d1901c82c2..8c445c685a 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -641,7 +641,9 @@ features will be paired with the "disable" policy. The **--expand-cpu-features** option will cause the host-model CPU definition to contain all CPU features supported on the host including those implicitly -enabled by the selected CPU model. +enabled by the selected CPU model. The CPU definition may also contain extra +features that are supported on the host, but should not be enabled by default +when starting a domain with host-model CPU. pool-capabilities diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 8f07ef2156..b7b0b80318 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -1547,7 +1547,7 @@ int virDomainMigrateStartPostCopy(virDomainPtr domain, typedef enum { /* Report host model with deprecated features disabled. (Since: 11.0.0) */ VIR_CONNECT_GET_DOMAIN_CAPABILITIES_DISABLE_DEPRECATED_FEATURES = (1 << 0), - /* Report all host model CPU features. (Since: 12.2.0) */ + /* Report all CPU features supported on the host. (Since: 12.2.0) */ VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES = (1 << 1), } virConnectGetDomainCapabilitiesFlags; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index a4cbeb8ad4..eeeb060f0d 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -12354,8 +12354,11 @@ virDomainSetUserPassword(virDomainPtr dom, * * If @flags includes VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES, * libvirt will explicitly list all CPU features (in host-model CPU definition) - * that are supported on the host. Without this flag features that are part of - * the CPU model itself will not be listed. + * that are supported on the host. Specifically, features that are normally + * hidden as included in the CPU model itself will be shown as well. The CPU + * definition may also contain extra features that are supported on the host, + * but should not be enabled by default when starting a domain with host-model + * CPU. * * Returns NULL in case of error or an XML string * defining the capabilities. diff --git a/tools/virsh-host.c b/tools/virsh-host.c index ef91e22fed..8f323cbd96 100644 --- a/tools/virsh-host.c +++ b/tools/virsh-host.c @@ -121,7 +121,7 @@ static const vshCmdOptDef opts_domcapabilities[] = { }, {.name = "expand-cpu-features", .type = VSH_OT_BOOL, - .help = N_("show all features in host CPU model"), + .help = N_("show all supported features in host CPU model"), }, {.name = NULL} }; -- 2.54.0
From: Jiri Denemark <jdenemar@redhat.com> The arch-capabilities MSR is not defined on AMD CPUs, but KVM has always been emulating them. Unfortunately, this may cause Windows to crash so QEMU (since 10.1, commit d3a24134e37d57abd3e7445842cda2717f49e96d) decided to mask the MSR by default with some additional compatibility code for older machine types. This is all mostly transparent except for probing when we run QEMU without a machine type and expand the "host" CPU model. With QEMU 10.1 and newer none of the arch-capabilities features will be shown as enabled, which may cause unexpected issues for users (such as KubeVirt) that get the list of all supported features from the host-model CPU definition in domain capabilities to select possible target nodes for migration. As a result of the change, no AMD host with new QEMU will be shown as available for incoming migration from older hosts. While we don't want to add the features in host-model definition by default, we can show them when a user wants to see all features supported on the host, i.e., when a recently introduced VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES flag is passed. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 39 ++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 8295a9ae9d..b0406b348a 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -4093,6 +4093,41 @@ virQEMUCapsInitCPUModel(virQEMUCaps *qemuCaps, } +static virCPUDef * +virQEMUCapsInitExpandedHostCPUModel(virQEMUCaps *qemuCaps, + virCPUDef *hostModel) +{ + g_autoptr(virCPUDef) cpu = virCPUDefCopy(hostModel); + virArch arch = qemuCaps->arch; + + if (ARCH_IS_X86(arch) && + STREQ_NULLABLE(cpu->vendor, "AMD") && + virCPUCheckFeature(arch, cpu, "arch-capabilities") == 0) { + uint32_t index = 0x10a; + uint64_t msr = 0; + /* While the arch-capabilities MSR is not defined on AMD CPUs, KVM has + * always been emulating them. Unfortunately, this may cause some + * Windows version to crash so QEMU decided to mask the MSR by default. + * Let's just add the emulated features back to the expanded host-model + * definition used for reporting all CPU features supported on a host. + */ + if (virHostCPUGetMSRFromKVM(index, &msr) == 0) { + g_autoptr(virCPUData) data = virCPUDataNew(arch); + virCPUFeaturePolicy policy = VIR_CPU_FEATURE_REQUIRE; + + virCPUx86DataAddMSR(data, index, msr); + virCPUUpdateFeatures(arch, cpu, data, policy); + virCPUDefUpdateFeature(cpu, "arch-capabilities", policy); + } + } + + if (virCPUExpandFeatures(arch, cpu) < 0) + return NULL; + + return g_steal_pointer(&cpu); +} + + static virCPUDef * virQEMUCapsNewHostCPUModel(void) { @@ -4150,8 +4185,8 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, &qemuCaps->arch); } - cpuExpanded = virCPUDefCopy(cpu); - if (virCPUExpandFeatures(qemuCaps->arch, cpuExpanded) < 0) + cpuExpanded = virQEMUCapsInitExpandedHostCPUModel(qemuCaps, cpu); + if (!cpuExpanded) goto error; if (rc == 0 && -- 2.54.0
On Wed, May 27, 2026 at 12:07:12 +0200, Jiri Denemark wrote:
See the last patch for detailed description.
Jiri Denemark (10): qemu_capabilities: Split virQEMUCapsFillDomainCPUCaps qemu: Move domain caps flags handling to virQEMUCapsFillDomainCPUHostModel qemu_capabilities: Use g_autoptr in virQEMUCapsInitHostCPUModel qemu_capabilities: Split conditions in virQEMUCapsInitHostCPUModel qemu_capabilities: Cache expanded CPU util: Publish and mock virHostCPUGetMSRFromKVM
Hmm, I mocked this function, but it won't really be used anywhere as VIR_CONNECT_GET_DOMAIN_CAPABILITIES_EXPAND_CPU_FEATURES is not tested. I will send v2 with the tests added. Jirka
participants (3)
-
Jiri Denemark -
Jiří Denemark -
Peter Krempa