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(a)linux.ibm.com>
Reviewed-by: Boris Fiuczynski <fiuczy(a)linux.ibm.com>
---
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