qemu: Change CPU comparison algorithm for future models

When starting a domain we check whether the guest CPU definition is
compatible with the host (i.e., when the host supports all features
required both explicitly and by the specified CPU model) as long as
check == 'partial', which is the default.

We are doing so by checking our definition of the CPU model in the CPU
map amending it with explicitly mentioned features and comparing it to
features QEMU would enabled when started with -cpu host. But since our
CPU model definitions often slightly differ from QEMU we may be checking
features which are not actually needed and on the other hand not
checking something that is part of the CPU model in QEMU.

This patch changes the algorithm for CPU models added in the future
(changing it for existing models could cause them to suddenly become
incompatible with the host and domains using them would fail to start).
The new algorithm uses information we probe from QEMU about features
that block each model from being directly usable. If all those features
are explicitly disabled in the CPU definition we consider the base model
compatible with the host. Then we only need to check that all explicitly
required features are supported by QEMU on the host to get the result
for the whole CPU definition.

After this we only use the model definitions (for newly added models)
from CPU map for creating a CPU definition for host-model.

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
Jiri Denemark 2024-10-15 18:18:25 +02:00
parent e373f87034
commit f928eb5fc8

View File

@ -13234,10 +13234,16 @@ qemuDomainStorageUpdatePhysical(virQEMUDriverConfig *cfg,
* @compatCPU: type of CPU used for old style check * @compatCPU: type of CPU used for old style check
* @failIncompatible: return an error instead of VIR_CPU_COMPARE_INCOMPATIBLE * @failIncompatible: return an error instead of VIR_CPU_COMPARE_INCOMPATIBLE
* *
* Perform a "partial" check of the @cpu against a "host CPU". Old style check * Perform a "partial" check of the @cpu against a "host CPU".
* used with all existing CPU models uses cpu_map definition of the model in *
* @cpu and compares it to the host CPU fetched @qemuCaps according to * Old style check used with all existing CPU models uses cpu_map definition of
* @compatCPU. * the model in @cpu and compares it to the host CPU fetched @qemuCaps
* according to @compatCPU.
*
* For future CPU models (without <check partial='compat'/>) only explicitly
* requested features are checked against the host CPU definition provided by
* QEMU and the usability (with a list of blocking features) info for the base
* CPU model. Our definition of the mode in cpu_map is ignored completely.
* *
* Returns VIR_CPU_COMPARE_ERROR on error, VIR_CPU_COMPARE_INCOMPATIBLE when * Returns VIR_CPU_COMPARE_ERROR on error, VIR_CPU_COMPARE_INCOMPATIBLE when
* the two CPUs are incompatible, VIR_CPU_COMPARE_IDENTICAL when the two CPUs * the two CPUs are incompatible, VIR_CPU_COMPARE_IDENTICAL when the two CPUs
@ -13254,12 +13260,31 @@ qemuDomainCheckCPU(virArch arch,
virQEMUCapsHostCPUType compatCPU, virQEMUCapsHostCPUType compatCPU,
bool failIncompatible) bool failIncompatible)
{ {
virCPUDef *host; virCPUDef *hypervisorCPU;
bool compat = false;
char **blockers = NULL;
if (virQEMUCapsIsCPUUsable(qemuCaps, virtType, cpu)) if (virQEMUCapsIsCPUUsable(qemuCaps, virtType, cpu))
return VIR_CPU_COMPARE_SUPERSET; return VIR_CPU_COMPARE_SUPERSET;
host = virQEMUCapsGetHostModel(qemuCaps, virtType, compatCPU); hypervisorCPU = virQEMUCapsGetHostModel(qemuCaps, virtType,
VIR_QEMU_CAPS_HOST_CPU_REPORTED);
return virCPUCompare(arch, host, cpu, failIncompatible); /* Force compat check if the CPU model is not found in qemuCaps or
* we don't have host CPU data from QEMU */
if (!cpu->model ||
hypervisorCPU->fallback != VIR_CPU_FALLBACK_FORBID ||
virQEMUCapsGetCPUBlockers(qemuCaps, virtType,
cpu->model, &blockers) < 0)
compat = true;
else if (virCPUGetCheckMode(arch, cpu, &compat) < 0)
return VIR_CPU_COMPARE_ERROR;
if (compat) {
virCPUDef *host = virQEMUCapsGetHostModel(qemuCaps, virtType, compatCPU);
return virCPUCompare(arch, host, cpu, failIncompatible);
}
return virCPUCompareUnusable(arch, hypervisorCPU, cpu,
blockers, failIncompatible);
} }