Query the host-recommended CPU model from QEMU, if it is supported. This model will be stored in the QEMU capabilities file underneath the closing </hostCPU> tag, labeled <hostRecCPU>. An example follows, (shortened as to not overwhelm the commit message): <hostRecCPU type='kvm' model='gen16a-base' migratability='no'> <property name='aen' type='boolean' value='true'/> <property name='cmmnt' type='boolean' value='true'/> <property name='aefsi' type='boolean' value='true'/> <property name='diag318' type='boolean' value='true'/> <property name='csske' type='boolean' value='false'/> <property name='mepoch' type='boolean' value='true'/> <property name='msa8' type='boolean' value='true'/> ... <property name='te' type='boolean' value='false'/> <property name='cmm' type='boolean' value='true'/> </hostRecCPU> Signed-off-by: Collin Walling <walling@xxxxxxxxxxxxx> Reviewed-by: Boris Fiuczynski <fiuczy@xxxxxxxxxxxxx> --- src/conf/schemas/cputypes.rng | 1 + src/qemu/qemu_capabilities.c | 108 +++++++++++++++--- src/qemu/qemu_capabilities.h | 3 + src/qemu/qemu_capspriv.h | 6 +- tests/cputest.c | 4 +- .../caps_8.1.0_s390x.replies | 74 ++++++++++++ .../qemucapabilitiesdata/caps_8.1.0_s390x.xml | 54 +++++++++ 7 files changed, 231 insertions(+), 19 deletions(-) diff --git a/src/conf/schemas/cputypes.rng b/src/conf/schemas/cputypes.rng index db1aa57158..5e89d67c8e 100644 --- a/src/conf/schemas/cputypes.rng +++ b/src/conf/schemas/cputypes.rng @@ -10,6 +10,7 @@ <value>host-model</value> <value>host-passthrough</value> <value>maximum</value> + <value>host-recommended</value> </choice> </attribute> </define> diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 40cdcffbfe..59403808ee 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -724,6 +724,11 @@ struct _virQEMUCapsHostCPUData { qemuMonitorCPUModelInfo *info; /* Physical address size of the host CPU or 0 if unknown or not applicable. */ unsigned int physAddrSize; + /* Host CPU model with delta changes reported by the hypervisor to ensure + * migration compatibility with newer machines that may exclude certain + * features available on older machines. + */ + qemuMonitorCPUModelInfo *hostRec; /* Host CPU definition reported in domain capabilities. */ virCPUDef *reported; /* Migratable host CPU definition used for updating guest CPU. */ @@ -732,6 +737,8 @@ struct _virQEMUCapsHostCPUData { * combined with features reported by QEMU. This is used for backward * compatible comparison between a guest CPU and a host CPU. */ virCPUDef *full; + /* CPU definition converted from hostRec model info */ + virCPUDef *recommended; }; typedef struct _virQEMUCapsAccel virQEMUCapsAccel; @@ -1834,6 +1841,12 @@ virQEMUCapsHostCPUDataCopy(virQEMUCapsHostCPUData *dst, if (src->full) dst->full = virCPUDefCopy(src->full); + + if (src->hostRec) + dst->hostRec = qemuMonitorCPUModelInfoCopy(src->hostRec); + + if (src->recommended) + dst->recommended = virCPUDefCopy(src->recommended); } @@ -1841,9 +1854,11 @@ static void virQEMUCapsHostCPUDataClear(virQEMUCapsHostCPUData *cpuData) { qemuMonitorCPUModelInfoFree(cpuData->info); + qemuMonitorCPUModelInfoFree(cpuData->hostRec); virCPUDefFree(cpuData->reported); virCPUDefFree(cpuData->migratable); virCPUDefFree(cpuData->full); + virCPUDefFree(cpuData->recommended); memset(cpuData, 0, sizeof(*cpuData)); } @@ -2190,6 +2205,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_RECOMMENDED: + return cpuData->recommended; } return NULL; @@ -2202,7 +2220,8 @@ virQEMUCapsSetHostModel(virQEMUCaps *qemuCaps, unsigned int physAddrSize, virCPUDef *reported, virCPUDef *migratable, - virCPUDef *full) + virCPUDef *full, + virCPUDef *recommended) { virQEMUCapsHostCPUData *cpuData; @@ -2211,6 +2230,7 @@ virQEMUCapsSetHostModel(virQEMUCaps *qemuCaps, cpuData->reported = reported; cpuData->migratable = migratable; cpuData->full = full; + cpuData->recommended = recommended; } @@ -3048,6 +3068,7 @@ virQEMUCapsProbeQMPHostCPU(virQEMUCaps *qemuCaps, { const char *model = virQEMUCapsTypeIsAccelerated(virtType) ? "host" : "max"; g_autoptr(qemuMonitorCPUModelInfo) modelInfo = NULL; + g_autoptr(qemuMonitorCPUModelInfo) hostRecInfo = NULL; g_autoptr(qemuMonitorCPUModelInfo) nonMigratable = NULL; g_autoptr(virCPUDef) cpu = NULL; qemuMonitorCPUModelExpansionType type; @@ -3133,6 +3154,19 @@ virQEMUCapsProbeQMPHostCPU(virQEMUCaps *qemuCaps, return -1; } + /* Retrieve the hypervisor recommended host CPU */ + if (virQEMUCapsTypeIsAccelerated(virtType) && + ARCH_IS_S390(qemuCaps->arch) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION_STATIC_RECOMMENDED)) { + type = QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_REC; + + if (qemuMonitorGetCPUModelExpansion(mon, type, cpu, true, false, fail_no_props, + &hostRecInfo) < 0) + return -1; + + accel->hostCPU.hostRec = g_steal_pointer(&hostRecInfo); + } + accel->hostCPU.info = g_steal_pointer(&modelInfo); return 0; } @@ -3157,7 +3191,7 @@ virQEMUCapsGetCPUFeatures(virQEMUCaps *qemuCaps, size_t n; *features = NULL; - modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, virtType); + modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, virtType, false); if (!modelInfo) return 0; @@ -3725,14 +3759,19 @@ int virQEMUCapsInitCPUModel(virQEMUCaps *qemuCaps, virDomainVirtType type, virCPUDef *cpu, - bool migratable) + bool migratable, + bool recommended) { - qemuMonitorCPUModelInfo *modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, type); + qemuMonitorCPUModelInfo *modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, type, + recommended); int ret = 1; if (migratable && modelInfo && !modelInfo->migratability) return 1; + if (recommended && !modelInfo) + return 2; + if (ARCH_IS_S390(qemuCaps->arch)) { ret = virQEMUCapsInitCPUModelS390(qemuCaps, type, modelInfo, cpu, migratable); @@ -3775,6 +3814,7 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, virCPUDef *hostCPU = NULL; virCPUDef *fullCPU = NULL; unsigned int physAddrSize = 0; + virCPUDef *recCPU = NULL; size_t i; int rc; @@ -3784,7 +3824,7 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, if (!(cpu = virQEMUCapsNewHostCPUModel())) goto error; - if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, cpu, false)) < 0) { + if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, cpu, false, false)) < 0) { goto error; } else if (rc == 1) { g_autoptr(virDomainCapsCPUModels) cpuModels = NULL; @@ -3823,7 +3863,7 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, if (!(migCPU = virQEMUCapsNewHostCPUModel())) goto error; - if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, migCPU, true)) < 0) { + if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, migCPU, true, false)) < 0) { goto error; } else if (rc == 1) { VIR_DEBUG("CPU migratability not provided by QEMU"); @@ -3833,6 +3873,17 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, goto error; } + if (!(recCPU = virQEMUCapsNewHostCPUModel())) + goto error; + + if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, recCPU, false, true)) < 0) { + goto error; + } else if (rc == 2) { + VIR_DEBUG("CPU reccomendation not provided by QEMU"); + virCPUDefFree(recCPU); + recCPU = NULL; + } + if (ARCH_IS_X86(qemuCaps->arch) && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_UNAVAILABLE_FEATURES)) { if (cpu && @@ -3851,7 +3902,7 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, if (virQEMUCapsTypeIsAccelerated(type)) virHostCPUGetPhysAddrSize(hostArch, &physAddrSize); - virQEMUCapsSetHostModel(qemuCaps, type, physAddrSize, cpu, migCPU, fullCPU); + virQEMUCapsSetHostModel(qemuCaps, type, physAddrSize, cpu, migCPU, fullCPU, recCPU); cleanup: virCPUDefFree(cpuExpanded); @@ -3862,6 +3913,7 @@ virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps, virCPUDefFree(cpu); virCPUDefFree(migCPU); virCPUDefFree(fullCPU); + virCPUDefFree(recCPU); virResetLastError(); goto cleanup; } @@ -3878,8 +3930,12 @@ virQEMUCapsUpdateHostCPUModel(virQEMUCaps *qemuCaps, qemuMonitorCPUModelInfo * virQEMUCapsGetCPUModelInfo(virQEMUCaps *qemuCaps, - virDomainVirtType type) + virDomainVirtType type, + bool recommended) { + if (recommended) + return virQEMUCapsGetAccel(qemuCaps, type)->hostCPU.hostRec; + return virQEMUCapsGetAccel(qemuCaps, type)->hostCPU.info; } @@ -4006,6 +4062,17 @@ virQEMUCapsLoadHostCPUModelInfo(virQEMUCapsAccel *caps, } +static int +virQEMUCapsLoadHostRecCPUModelInfo(virQEMUCapsAccel *caps, + xmlXPathContextPtr ctxt, + const char *typeStr) +{ + g_autofree char *xpath = g_strdup_printf("./hostRecCPU[@type='%s']", typeStr); + + return virQEMUCapsLoadCPUModelInfo(&caps->hostCPU.hostRec, ctxt, xpath); +} + + static int virQEMUCapsLoadCPUModels(virArch arch, virQEMUCapsAccel *caps, @@ -4169,6 +4236,10 @@ virQEMUCapsLoadAccel(virQEMUCaps *qemuCaps, if (virQEMUCapsLoadHostCPUModelInfo(caps, ctxt, typeStr) < 0) return -1; + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION_STATIC_RECOMMENDED) && + virQEMUCapsLoadHostRecCPUModelInfo(caps, ctxt, typeStr) < 0) + return -1; + if (virQEMUCapsLoadCPUModels(qemuCaps->arch, caps, ctxt, typeStr) < 0) return -1; @@ -4705,18 +4776,24 @@ virQEMUCapsLoadCache(virArch hostArch, static void virQEMUCapsFormatHostCPUModelInfo(virQEMUCapsAccel *caps, virBuffer *buf, - const char *typeStr) + const char *typeStr, + bool recommended) { - qemuMonitorCPUModelInfo *model = caps->hostCPU.info; + qemuMonitorCPUModelInfo *model; size_t i; + if (recommended) + model = caps->hostCPU.hostRec; + else + model = caps->hostCPU.info; + if (!model) return; virBufferAsprintf(buf, - "<hostCPU type='%s' model='%s' migratability='%s'>\n", - typeStr, model->name, - model->migratability ? "yes" : "no"); + "<%s type='%s' model='%s' migratability='%s'>\n", + recommended ? "hostRecCPU" : "hostCPU", typeStr, + model->name, model->migratability ? "yes" : "no"); virBufferAdjustIndent(buf, 2); for (i = 0; i < model->nprops; i++) { @@ -4752,7 +4829,7 @@ virQEMUCapsFormatHostCPUModelInfo(virQEMUCapsAccel *caps, } virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</hostCPU>\n"); + virBufferAsprintf(buf, "</%s>\n", recommended ? "hostRecCPU" : "hostCPU"); } @@ -4846,7 +4923,8 @@ virQEMUCapsFormatAccel(virQEMUCaps *qemuCaps, virQEMUCapsAccel *caps = virQEMUCapsGetAccel(qemuCaps, type); const char *typeStr = virQEMUCapsAccelStr(type); - virQEMUCapsFormatHostCPUModelInfo(caps, buf, typeStr); + virQEMUCapsFormatHostCPUModelInfo(caps, buf, typeStr, false); + virQEMUCapsFormatHostCPUModelInfo(caps, buf, typeStr, true); virQEMUCapsFormatCPUModels(qemuCaps->arch, caps, buf, typeStr); virQEMUCapsFormatMachines(caps, buf, typeStr); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 4fa64b6435..bb68b74328 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -726,6 +726,9 @@ 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 with a modified feature set based on dropped + * features on newer CPU models. */ + VIR_QEMU_CAPS_HOST_CPU_RECOMMENDED, } virQEMUCapsHostCPUType; virCPUDef *virQEMUCapsGetHostModel(virQEMUCaps *qemuCaps, diff --git a/src/qemu/qemu_capspriv.h b/src/qemu/qemu_capspriv.h index 06b36d2eb8..a97710eecf 100644 --- a/src/qemu/qemu_capspriv.h +++ b/src/qemu/qemu_capspriv.h @@ -64,11 +64,13 @@ int virQEMUCapsInitCPUModel(virQEMUCaps *qemuCaps, virDomainVirtType type, virCPUDef *cpu, - bool migratable); + bool migratable, + bool recommended); qemuMonitorCPUModelInfo * virQEMUCapsGetCPUModelInfo(virQEMUCaps *qemuCaps, - virDomainVirtType type); + virDomainVirtType type, + bool recommended); void virQEMUCapsSetCPUModelInfo(virQEMUCaps *qemuCaps, diff --git a/tests/cputest.c b/tests/cputest.c index b3253e3116..8d3a0dd4d9 100644 --- a/tests/cputest.c +++ b/tests/cputest.c @@ -857,7 +857,7 @@ cpuTestJSONCPUID(const void *arg) cpu->match = VIR_CPU_MATCH_EXACT; cpu->fallback = VIR_CPU_FALLBACK_FORBID; - if (virQEMUCapsInitCPUModel(qemuCaps, VIR_DOMAIN_VIRT_KVM, cpu, false) != 0) + if (virQEMUCapsInitCPUModel(qemuCaps, VIR_DOMAIN_VIRT_KVM, cpu, false, false) != 0) return -1; return cpuTestCompareXML(data->arch, cpu, result); @@ -875,7 +875,7 @@ cpuTestJSONSignature(const void *arg) if (!(qemuCaps = cpuTestMakeQEMUCaps(data))) return -1; - modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, VIR_DOMAIN_VIRT_KVM); + modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, VIR_DOMAIN_VIRT_KVM, false); if (!(hostData = virQEMUCapsGetCPUModelX86Data(qemuCaps, modelInfo, false))) return -1; diff --git a/tests/qemucapabilitiesdata/caps_8.1.0_s390x.replies b/tests/qemucapabilitiesdata/caps_8.1.0_s390x.replies index 8cd7312bea..2720408726 100644 --- a/tests/qemucapabilitiesdata/caps_8.1.0_s390x.replies +++ b/tests/qemucapabilitiesdata/caps_8.1.0_s390x.replies @@ -29649,6 +29649,80 @@ "id": "libvirt-37" } +{ + "execute": "query-cpu-model-expansion", + "arguments": { + "type": "static-recommended", + "model": { + "name": "host" + } + }, + "id": "libvirt-38" +} + +{ + "return": { + "model": { + "name": "gen16a-base", + "props": { + "nnpa": true, + "aen": true, + "cmmnt": true, + "vxpdeh": true, + "aefsi": true, + "diag318": true, + "csske": false, + "mepoch": true, + "msa9": true, + "msa8": true, + "msa7": true, + "msa6": true, + "msa5": true, + "msa4": true, + "msa3": true, + "msa2": true, + "msa1": true, + "sthyi": true, + "edat": true, + "ri": true, + "deflate": true, + "edat2": true, + "etoken": true, + "vx": true, + "ipter": true, + "pai": true, + "paie": true, + "mepochptff": true, + "ap": true, + "vxeh": true, + "vxpd": true, + "esop": true, + "msa9_pckmo": true, + "vxeh2": true, + "esort": true, + "apqi": true, + "apft": true, + "els": true, + "iep": true, + "apqci": true, + "cte": false, + "ais": true, + "bpb": false, + "gs": true, + "ppa15": true, + "zpci": true, + "rdp": true, + "sea_esop2": true, + "beareh": true, + "te": false, + "cmm": true, + "vxpdeh2": true + } + } + }, + "id": "libvirt-38" +} + { "execute": "qmp_capabilities", "id": "libvirt-1" diff --git a/tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml b/tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml index 0bb4233383..c580cbb5b0 100644 --- a/tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml +++ b/tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml @@ -171,6 +171,60 @@ <property name='cmm' type='boolean' value='true'/> <property name='vxpdeh2' type='boolean' value='true'/> </hostCPU> + <hostRecCPU type='kvm' model='gen16a-base' migratability='no'> + <property name='nnpa' type='boolean' value='true'/> + <property name='aen' type='boolean' value='true'/> + <property name='cmmnt' type='boolean' value='true'/> + <property name='vxpdeh' type='boolean' value='true'/> + <property name='aefsi' type='boolean' value='true'/> + <property name='diag318' type='boolean' value='true'/> + <property name='csske' type='boolean' value='false'/> + <property name='mepoch' type='boolean' value='true'/> + <property name='msa9' type='boolean' value='true'/> + <property name='msa8' type='boolean' value='true'/> + <property name='msa7' type='boolean' value='true'/> + <property name='msa6' type='boolean' value='true'/> + <property name='msa5' type='boolean' value='true'/> + <property name='msa4' type='boolean' value='true'/> + <property name='msa3' type='boolean' value='true'/> + <property name='msa2' type='boolean' value='true'/> + <property name='msa1' type='boolean' value='true'/> + <property name='sthyi' type='boolean' value='true'/> + <property name='edat' type='boolean' value='true'/> + <property name='ri' type='boolean' value='true'/> + <property name='deflate' type='boolean' value='true'/> + <property name='edat2' type='boolean' value='true'/> + <property name='etoken' type='boolean' value='true'/> + <property name='vx' type='boolean' value='true'/> + <property name='ipter' type='boolean' value='true'/> + <property name='pai' type='boolean' value='true'/> + <property name='paie' type='boolean' value='true'/> + <property name='mepochptff' type='boolean' value='true'/> + <property name='ap' type='boolean' value='true'/> + <property name='vxeh' type='boolean' value='true'/> + <property name='vxpd' type='boolean' value='true'/> + <property name='esop' type='boolean' value='true'/> + <property name='msa9_pckmo' type='boolean' value='true'/> + <property name='vxeh2' type='boolean' value='true'/> + <property name='esort' type='boolean' value='true'/> + <property name='apqi' type='boolean' value='true'/> + <property name='apft' type='boolean' value='true'/> + <property name='els' type='boolean' value='true'/> + <property name='iep' type='boolean' value='true'/> + <property name='apqci' type='boolean' value='true'/> + <property name='cte' type='boolean' value='false'/> + <property name='ais' type='boolean' value='true'/> + <property name='bpb' type='boolean' value='false'/> + <property name='gs' type='boolean' value='true'/> + <property name='ppa15' type='boolean' value='true'/> + <property name='zpci' type='boolean' value='true'/> + <property name='rdp' type='boolean' value='true'/> + <property name='sea_esop2' type='boolean' value='true'/> + <property name='beareh' type='boolean' value='true'/> + <property name='te' type='boolean' value='false'/> + <property name='cmm' type='boolean' value='true'/> + <property name='vxpdeh2' type='boolean' value='true'/> + </hostRecCPU> <cpu type='kvm' name='gen16a-base' typename='gen16a-base-s390x-cpu' usable='yes'/> <cpu type='kvm' name='gen16a' typename='gen16a-s390x-cpu' usable='yes'/> <cpu type='kvm' name='z800-base' typename='z800-base-s390x-cpu' usable='yes'/> -- 2.41.0