[PATCH 0/6] Make virConnectBaselineHypervisorCPU a bit more sane

See 2/6 for description of the issue this series is trying to deal with. Jiri Denemark (6): cpu: Show input CPU model names in debug log Clarify documentation of virConnectBaselineHypervisorCPU Change documentation style of virConnectBaselineCPUFlags Introduce VIR_CONNECT_BASELINE_CPU_IGNORE_HOST flag qemu: Implement VIR_CONNECT_BASELINE_CPU_IGNORE_HOST virsh: Add support for VIR_CONNECT_BASELINE_CPU_IGNORE_HOST flag docs/manpages/virsh.rst | 20 +++++++++++++++----- include/libvirt/libvirt-host.h | 9 +++++++-- src/cpu/cpu.c | 2 +- src/libvirt-host.c | 30 +++++++++++++++++++++--------- src/qemu/qemu_driver.c | 30 +++++++++++++++++++++--------- tools/virsh-host.c | 8 ++++++++ 6 files changed, 73 insertions(+), 26 deletions(-) -- 2.50.0

From: Jiri Denemark <jdenemar@redhat.com> Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/cpu/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 77afb7e9b3..233686485d 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -581,7 +581,7 @@ virCPUBaseline(virArch arch, virArchToString(arch), ncpus, models, features, migratable); if (cpus) { for (i = 0; i < ncpus; i++) - VIR_DEBUG("cpus[%zu]=%p", i, cpus[i]); + VIR_DEBUG("cpus[%zu]=%p (%s)", i, cpus[i], NULLSTR(cpus[i]->model)); } if (models) { for (i = 0; i < models->nmodels; i++) -- 2.50.0

From: Jiri Denemark <jdenemar@redhat.com> The API was apparently never considered for being used on a host that is not represented in the input set of CPU definitions. The result is limited to the set of features and CPU models known to the host's hypervisor. This would likely not be a big issue, but thanks to a side effect of commit v3.8.0-99-g9c9620af1d usability blockers come to play as well. When converting CPU data (CPUID and MSR bits) to each named model for comparison, we disable features that block usability of the model on the current hypervisor, the rest of the features are set according to the data without taking host capabilities into account. Thus the process of comparing and selecting the most appropriate CPU model for the given data is significantly influenced by the host, but it doesn't behave as if the host CPU model was included in the input data. The documentation tried to say the result was tied to the host's hypervisor, but it wasn't very clear. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- docs/manpages/virsh.rst | 11 ++++++++--- src/libvirt-host.c | 24 +++++++++++++++--------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index e13b5020b5..7082ca773a 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1015,9 +1015,14 @@ hypervisor-cpu-baseline [--features] [--migratable] [model] Compute a baseline CPU which will be compatible with all CPUs defined in an XML -*file* and with the CPU the hypervisor is able to provide on the host. (This -is different from ``cpu-baseline`` which does not consider any hypervisor -abilities when computing the baseline CPU.) +*FILE*. This command must be called on one of the hosts described in *FILE*. +Calling it on another host results in an undefined behavior as the computed CPU +model is influenced by the hypervisor (the result may use an unexpected CPU +model or some features may disabled even though they are supported on all input +CPUs). + +This is different from ``cpu-baseline`` which does not consider any hypervisor +abilities when computing the baseline CPU. As an alternative for *FILE* in case the XML would only contain a CPU model with no additional features the CPU model name itself can be passed as *model*. diff --git a/src/libvirt-host.c b/src/libvirt-host.c index d82ff993c2..8d2107fd62 100644 --- a/src/libvirt-host.c +++ b/src/libvirt-host.c @@ -1300,24 +1300,30 @@ virConnectBaselineCPU(virConnectPtr conn, * @ncpus: number of CPUs in xmlCPUs * @flags: bitwise-OR of virConnectBaselineCPUFlags * - * Computes the most feature-rich CPU which is compatible with all given CPUs - * and can be provided by the specified hypervisor. For best results the - * host-model CPUs as advertised by virConnectGetDomainCapabilities() should be - * passed in @xmlCPUs. Any of @emulator, @arch, @machine, and @virttype - * parameters may be NULL; libvirt will choose sensible defaults tailored to - * the host and its current configuration. + * Computes the most feature-rich CPU which is compatible with all given CPUs. + * This API must be called on one of the hosts specified in @xmlCPUs. Calling + * it on another host results in an undefined behavior as the computed CPU + * model is influenced by the hypervisor (the result may use an unexpected CPU + * model or some features may disabled even though they are supported on all + * input CPUs). * * This is different from virConnectBaselineCPU() which doesn't consider any * hypervisor abilities when computing the best CPU. * + * The @xmlCPUs array should contain guest CPU definitions created from the + * host CPU model description as advertised by virConnectGetDomainCapabilities() + * in <mode name="host-model"> element. Any of @emulator, @arch, @machine, and + * @virttype parameters may be NULL; libvirt will choose sensible defaults + * tailored to the host and its current configuration. + * * If @ncpus == 1, the result will be the first (and only) CPU in @xmlCPUs * tailored to what the hypervisor can support on the current host. * Specifically if this single CPU definition contains no feature elements and * a CPU model listed as usable='no' in domain capabilities XML, the result * will contain a list usability blockers, i.e., a list of features that would - * need to be disabled to for the model to be usable on this host. This list - * may contain more features than what the hypervisor reports as blockers in - * case the CPU model definition in libvirt differs from QEMU definition. + * need to be disabled for the model to be usable on this host. This list may + * contain more features than what the hypervisor reports as blockers in case + * the CPU model definition in libvirt differs from QEMU definition. * * If @flags includes VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES then libvirt * will explicitly list all CPU features that are part of the computed CPU, -- 2.50.0

From: Jiri Denemark <jdenemar@redhat.com> Moving the documentation above each enum item gives us more space for it. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- include/libvirt/libvirt-host.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt-host.h b/include/libvirt/libvirt-host.h index 3112f2b676..91214ea21b 100644 --- a/include/libvirt/libvirt-host.h +++ b/include/libvirt/libvirt-host.h @@ -970,8 +970,10 @@ int virConnectGetCPUModelNames(virConnectPtr conn, * Since: 1.1.2 */ typedef enum { - VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES = (1 << 0), /* show all features (Since: 1.1.2) */ - VIR_CONNECT_BASELINE_CPU_MIGRATABLE = (1 << 1), /* filter out non-migratable features (Since: 1.2.14) */ + /* show all features (Since: 1.1.2) */ + VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES = (1 << 0), + /* filter out non-migratable features (Since: 1.2.14) */ + VIR_CONNECT_BASELINE_CPU_MIGRATABLE = (1 << 1), } virConnectBaselineCPUFlags; char *virConnectBaselineCPU(virConnectPtr conn, -- 2.50.0

From: Jiri Denemark <jdenemar@redhat.com> With this new flag virConnectHypervisorBaselineCPU can be used on any host (rather than being limited to hosts described by individual CPUs passed to the API). Using the flag makes the API behave similarly to the old virConnectBaselineCPU. The main difference is the CPU definition accepted by both APIs: the old one only accepts host CPU definition, i.e., without 'policy' attributes as seen in the host capabilities XML. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- include/libvirt/libvirt-host.h | 3 +++ src/libvirt-host.c | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-host.h b/include/libvirt/libvirt-host.h index 91214ea21b..19043235b2 100644 --- a/include/libvirt/libvirt-host.h +++ b/include/libvirt/libvirt-host.h @@ -974,6 +974,9 @@ typedef enum { VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES = (1 << 0), /* filter out non-migratable features (Since: 1.2.14) */ VIR_CONNECT_BASELINE_CPU_MIGRATABLE = (1 << 1), + /* when computing a baseline from several CPUs, do not make the result + * dependent on the current host (Since: 11.5.0) */ + VIR_CONNECT_BASELINE_CPU_IGNORE_HOST = (1 << 2), } virConnectBaselineCPUFlags; char *virConnectBaselineCPU(virConnectPtr conn, diff --git a/src/libvirt-host.c b/src/libvirt-host.c index 8d2107fd62..da75f5f30b 100644 --- a/src/libvirt-host.c +++ b/src/libvirt-host.c @@ -1305,7 +1305,8 @@ virConnectBaselineCPU(virConnectPtr conn, * it on another host results in an undefined behavior as the computed CPU * model is influenced by the hypervisor (the result may use an unexpected CPU * model or some features may disabled even though they are supported on all - * input CPUs). + * input CPUs). The undefined behavior can be avoided using + * VIR_CONNECT_BASELINE_CPU_IGNORE_HOST flag (see below). * * This is different from virConnectBaselineCPU() which doesn't consider any * hypervisor abilities when computing the best CPU. @@ -1333,6 +1334,11 @@ virConnectBaselineCPU(virConnectPtr conn, * If @flags includes VIR_CONNECT_BASELINE_CPU_MIGRATABLE, the resulting * CPU will not include features that block migration. * + * If @flags contains VIR_CONNECT_BASELINE_CPU_IGNORE_HOST and @ncpus > 1, the + * API will not consider hypervisor abilities when computing the baseline. With + * this flag baseline can be safely computed on any host (even those not + * described in @xmlCPUs). + * * Returns XML description of the computed CPU (caller frees) or NULL on error. * * Since: 4.4.0 -- 2.50.0

From: Jiri Denemark <jdenemar@redhat.com> Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_driver.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2c06e50454..cf7407ce2d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11970,13 +11970,20 @@ qemuConnectBaselineHypervisorCPU(virConnectPtr conn, size_t i; virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES | - VIR_CONNECT_BASELINE_CPU_MIGRATABLE, NULL); + VIR_CONNECT_BASELINE_CPU_MIGRATABLE | + VIR_CONNECT_BASELINE_CPU_IGNORE_HOST, NULL); if (virConnectBaselineHypervisorCPUEnsureACL(conn) < 0) goto cleanup; migratable = !!(flags & VIR_CONNECT_BASELINE_CPU_MIGRATABLE); + if ((flags & VIR_CONNECT_BASELINE_CPU_IGNORE_HOST) && ncpus < 2) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("ignoring host is only allowed when computing baseline from multiple CPUs")); + goto cleanup; + } + if (!(cpus = virCPUDefListParse(xmlCPUs, ncpus, VIR_CPU_TYPE_AUTO))) goto cleanup; @@ -11999,14 +12006,19 @@ qemuConnectBaselineHypervisorCPU(virConnectPtr conn, } if (ARCH_IS_X86(arch)) { - int rc = virQEMUCapsGetCPUFeatures(qemuCaps, virttype, - migratable, &features); - if (rc < 0) - goto cleanup; - if (features && rc == 0) { - /* We got only migratable features from QEMU if we asked for them, - * no further filtering in virCPUBaseline is desired. */ - migratable = false; + if (flags & VIR_CONNECT_BASELINE_CPU_IGNORE_HOST) { + VIR_DEBUG("Not adding host's features as VIR_CONNECT_BASELINE_CPU_IGNORE_HOST was set"); + g_clear_pointer(&cpuModels, virObjectUnref); + } else { + int rc = virQEMUCapsGetCPUFeatures(qemuCaps, virttype, + migratable, &features); + if (rc < 0) + goto cleanup; + if (features && rc == 0) { + /* We got only migratable features from QEMU if we asked for them, + * no further filtering in virCPUBaseline is desired. */ + migratable = false; + } } if (!(cpu = virCPUBaseline(arch, cpus, ncpus, cpuModels, -- 2.50.0

From: Jiri Denemark <jdenemar@redhat.com> Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- docs/manpages/virsh.rst | 11 ++++++++--- tools/virsh-host.c | 8 ++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 7082ca773a..bcb5495ed9 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1012,14 +1012,15 @@ hypervisor-cpu-baseline :: hypervisor-cpu-baseline [FILE] [virttype] [emulator] [arch] [machine] - [--features] [--migratable] [model] + [--features] [--migratable] [--ignore-host] [model] Compute a baseline CPU which will be compatible with all CPUs defined in an XML *FILE*. This command must be called on one of the hosts described in *FILE*. Calling it on another host results in an undefined behavior as the computed CPU model is influenced by the hypervisor (the result may use an unexpected CPU model or some features may disabled even though they are supported on all input -CPUs). +CPUs). The undefined behavior can be avoided using *--ignore-host* option (see +below). This is different from ``cpu-baseline`` which does not consider any hypervisor abilities when computing the baseline CPU. @@ -1059,7 +1060,11 @@ specifies the path to the emulator, *arch* specifies the CPU architecture, and resulting XML description will explicitly include all features that make up the CPU, without this option features that are part of the CPU model will not be listed in the XML description. If *--migratable* is specified, features that -block migration will not be included in the resulting CPU. +block migration will not be included in the resulting CPU. If *--ignore-host* +is specified and *FILE* contains more than one CPU, the command will not +consider hypervisor abilities when computing the baseline. With this option +baseline can be safely computed on any host (even those not described in +*FILE*). hypervisor-cpu-models diff --git a/tools/virsh-host.c b/tools/virsh-host.c index 16f9411730..51b71b512c 100644 --- a/tools/virsh-host.c +++ b/tools/virsh-host.c @@ -1700,6 +1700,12 @@ static const vshCmdOptDef opts_hypervisor_cpu_baseline[] = { .type = VSH_OT_BOOL, .help = N_("Do not include features that block migration") }, + {.name = "ignore-host", + .type = VSH_OT_BOOL, + .help = N_("when computing baseline from several CPUs, do not take " + "hypervisor capabilities into account and work with input " + "data only") + }, {.name = "model", .type = VSH_OT_STRING, .unwanted_positional = true, @@ -1730,6 +1736,8 @@ cmdHypervisorCPUBaseline(vshControl *ctl, flags |= VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES; if (vshCommandOptBool(cmd, "migratable")) flags |= VIR_CONNECT_BASELINE_CPU_MIGRATABLE; + if (vshCommandOptBool(cmd, "ignore-host")) + flags |= VIR_CONNECT_BASELINE_CPU_IGNORE_HOST; if (vshCommandOptString(ctl, cmd, "file", &from) < 0 || vshCommandOptString(ctl, cmd, "virttype", &virttype) < 0 || -- 2.50.0

On 7/4/25 15:52, Jiri Denemark via Devel wrote:
See 2/6 for description of the issue this series is trying to deal with.
Jiri Denemark (6): cpu: Show input CPU model names in debug log Clarify documentation of virConnectBaselineHypervisorCPU Change documentation style of virConnectBaselineCPUFlags Introduce VIR_CONNECT_BASELINE_CPU_IGNORE_HOST flag qemu: Implement VIR_CONNECT_BASELINE_CPU_IGNORE_HOST virsh: Add support for VIR_CONNECT_BASELINE_CPU_IGNORE_HOST flag
docs/manpages/virsh.rst | 20 +++++++++++++++----- include/libvirt/libvirt-host.h | 9 +++++++-- src/cpu/cpu.c | 2 +- src/libvirt-host.c | 30 +++++++++++++++++++++--------- src/qemu/qemu_driver.c | 30 +++++++++++++++++++++--------- tools/virsh-host.c | 8 ++++++++ 6 files changed, 73 insertions(+), 26 deletions(-)
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> and don't forget a NEWS.rst record ;-) Michal

From: Jiri Denemark <jdenemar@redhat.com> Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- NEWS.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index d8bd2559f4..4fd12d94f4 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -17,8 +17,20 @@ v11.6.0 (unreleased) * **New features** + * Introduce VIR_CONNECT_BASELINE_CPU_IGNORE_HOST flag + + This new flag for virConnectBaselineHypervisorCPU can be used for computing + a baseline CPU on any host without being limited to hosts represented by + the input CPU definitions. + * **Improvements** + * Clarify documentation of virConnectBaselineHypervisorCPU + + The documentation makes it clear virConnectBaselineHypervisorCPU is + supposed to be called on one of the hosts represented in the input CPU + definitions. Otherwise the API will give unexpected results. + * **Bug fixes** -- 2.50.0

On Fri, Jul 04, 2025 at 16:56:37 +0200, Jiri Denemark via Devel wrote:
From: Jiri Denemark <jdenemar@redhat.com>
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- NEWS.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/NEWS.rst b/NEWS.rst index d8bd2559f4..4fd12d94f4 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -17,8 +17,20 @@ v11.6.0 (unreleased)
* **New features**
+ * Introduce VIR_CONNECT_BASELINE_CPU_IGNORE_HOST flag + + This new flag for virConnectBaselineHypervisorCPU can be used for computing + a baseline CPU on any host without being limited to hosts represented by + the input CPU definitions.
I think this is a bit confusing. How about: a baseline CPU on any host. Without the VIR_CONNECT_BASELINE_CPU_IGNORE_HOST flag the baseline API would return reasonable output only when run on one of the hosts that the input CPU definitions were collected from.
+ * **Improvements**
+ * Clarify documentation of virConnectBaselineHypervisorCPU + + The documentation makes it clear virConnectBaselineHypervisorCPU is + supposed to be called on one of the hosts represented in the input CPU + definitions. Otherwise the API will give unexpected results. +
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
participants (3)
-
Jiri Denemark
-
Michal Prívozník
-
Peter Krempa