When parsing a domain XML which uses a non-versioned CPU model we want to replace it with the appropriate version variant similarly to what we do with machine types. Theoretically QEMU supports per machine type specification of a version with which a non-versioned CPU model is replaced, but this is always 1 for all machine types and the query-machines QMP command does not even report the value. Luckily after talking to Igor, having a single number per machine type does not really allow for setting it to anything but 1 as CPU models have different number of versions. Each machine type would need to define a specific version for each CPU model, which would be a maintenance nightmare. For this reason there's no desire to ever resolve non-versioned CPU models to anything but v1 in QEMU and the per machine type setting will most likely even be removed completely. Thus it is safe for us to always use v1 as the canonical CPU model. Some non-versioned CPU models, however, are actually aliases to specific versions of a base model rather than being base models themselves. These are the old CPU model variants before model versions were introduced, e.g., -noTSX, -IBRS, etc. The mapping of these names to versions is hardcoded and will never change. We do not translate such CPU models to the corresponding versioned names. This allows us to introduce the corresponding -v* variants that match the QEMU models rather than the existing definitions in our CPU map. The guest CPU will be the same either way, but the way libvirt checks the CPU model compatibility with the host will be different. The old "partial" check done by libvirt using the definition from CPU map will still be used for the old names (we can't change this for compatibility reasons), but the corresponding versioned variants (as well as all other versions that do not have a non-versioned alias) will benefit from the recently introduced new "partial" check which uses only the information we get from QEMU to check whether a specific CPU definition is usable on the host. Signed-off-by: Jiri Denemark <jdenemar@xxxxxxxxxx> --- src/qemu/qemu_capabilities.c | 53 ++++++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 3 ++ src/qemu/qemu_domain.c | 6 ++++ src/qemu/qemu_postparse.c | 19 +++++++++++++ 4 files changed, 81 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 5ac9f306f5..455fb5acbe 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -2379,6 +2379,59 @@ virQEMUCapsGetCanonicalMachine(virQEMUCaps *qemuCaps, } +/** + * virQEMUCapsGetCanonicalCPU: + * @qemuCaps: QEMU capabilities + * @type: virtualization type + * @model: CPU model name + * + * Resolve a CPU model without explicit version to its versioned twin. We only + * replace a base CPU model with its -v1 variant as that's what QEMU is doing. + * Theoretically they have a per machine type configuration of the default CPU + * version to be used if no explicit version is specified, but it's always 1 + * and they will not change it. And they do not report the default CPU version + * via QMP anyway. + * + * The old named variants of other CPU models (-noTSX, -IBRS, etc.) are ignored + * as they are hardcoded aliases to specific versions and not "dynamically" + * resolved. + * + * Returns the canonical versioned name of the CPU model or NULL if the CPU + * model should stay unchanged. The pointer (which must never be freed by the + * caller) is valid as long as the caller holds a reference to the qemuCaps + * object. + */ +const char * +virQEMUCapsGetCanonicalCPU(virQEMUCaps *qemuCaps, + virDomainVirtType type, + const char *model) +{ + qemuMonitorCPUDefs *models; + size_t i; + + if (!ARCH_IS_X86(qemuCaps->arch) || !model) + return NULL; + + models = virQEMUCapsGetAccel(qemuCaps, type)->cpuModels; + if (!models) + return NULL; + + for (i = 0; i < models->ncpus; i++) { + qemuMonitorCPUDefInfo *cpu = models->cpus + i; + const char *p = STRSKIP(cpu->name, model); + + if (p && STREQ(p, "-v1")) { + if (virCPUCheckModel(qemuCaps->arch, cpu->name)) + return cpu->name; + + break; + } + } + + return NULL; +} + + int virQEMUCapsGetMachineMaxCpus(virQEMUCaps *qemuCaps, virDomainVirtType virtType, diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 48e4530c95..7decf31b97 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -778,6 +778,9 @@ bool virQEMUCapsIsCPUModeSupported(virQEMUCaps *qemuCaps, const char *virQEMUCapsGetCanonicalMachine(virQEMUCaps *qemuCaps, virDomainVirtType virtType, const char *name); +const char *virQEMUCapsGetCanonicalCPU(virQEMUCaps *qemuCaps, + virDomainVirtType type, + const char *model); bool virQEMUCapsIsMachineSupported(virQEMUCaps *qemuCaps, virDomainVirtType virtType, const char *canonical_machine) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 153bd56e86..d922f482c2 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -5057,6 +5057,12 @@ qemuDomainMakeCPUMigratable(virArch arch, !ARCH_IS_X86(arch)) return 0; + /* Strip -v1 suffix from the CPU model for backward compatibility with + * libvirt releases that do not support versioned CPU models. + */ + if (g_str_has_suffix(cpu->model, "-v1")) + cpu->model[strlen(cpu->model) - 3] = '\0'; + if (STREQ(cpu->model, "Icelake-Server")) { /* Originally Icelake-Server CPU model contained pconfig CPU feature. * It was never actually enabled and thus it was removed. To enable diff --git a/src/qemu/qemu_postparse.c b/src/qemu/qemu_postparse.c index 03b5ef825a..d3d85de11c 100644 --- a/src/qemu/qemu_postparse.c +++ b/src/qemu/qemu_postparse.c @@ -1656,6 +1656,23 @@ qemuDomainDefVcpusPostParse(virDomainDef *def) } +static void +qemuDomainCanonicalizeCPU(virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + const char *model; + + model = virQEMUCapsGetCanonicalCPU(qemuCaps, def->virtType, def->cpu->model); + if (!model) + return; + + VIR_DEBUG("Replacing CPU model '%1$s' with '%2$s'", def->cpu->model, model); + + g_free(def->cpu->model); + def->cpu->model = g_strdup(model); +} + + static int qemuDomainDefCPUPostParse(virDomainDef *def, virQEMUCaps *qemuCaps) @@ -1667,6 +1684,8 @@ qemuDomainDefCPUPostParse(virDomainDef *def, if (!def->cpu) return 0; + qemuDomainCanonicalizeCPU(def, qemuCaps); + for (i = 0; i < def->cpu->nfeatures; i++) { virCPUFeatureDef *feature = &def->cpu->features[i]; -- 2.47.0