QEMU 2.8.0 adds support for unavailable-features in
query-cpu-definitions reply. The unavailable-features array lists CPU
features which prevent a corresponding CPU model from being usable on
current host. It can only be used when all the unavailable features are
disabled. Empty array means the CPU model can be used without
modifications.
We can use unavailable-features for providing CPU model usability info
in domain capabilities XML:
<domainCapabilities>
...
<cpu>
<mode name='host-passthrough' supported='yes'/>
<mode name='host-model' supported='yes'>
<model fallback='allow'>Skylake-Client</model>
...
</mode>
<mode name='custom' supported='yes'>
<model usable='yes'>qemu64</model>
<model usable='yes'>qemu32</model>
<model usable='no'>phenom</model>
<model usable='yes'>pentium3</model>
<model usable='yes'>pentium2</model>
<model usable='yes'>pentium</model>
<model usable='yes'>n270</model>
<model usable='yes'>kvm64</model>
<model usable='yes'>kvm32</model>
<model usable='yes'>coreduo</model>
<model usable='yes'>core2duo</model>
<model usable='no'>athlon</model>
<model usable='yes'>Westmere</model>
<model usable='yes'>Skylake-Client</model>
...
</mode>
</cpu>
...
</domainCapabilities>
Signed-off-by: Jiri Denemark <jdenemar(a)redhat.com>
---
src/qemu/qemu_capabilities.c | 53 ++++++++++++++++++++++++++++++++++----------
src/qemu/qemu_capabilities.h | 6 +++--
src/qemu/qemu_monitor.h | 1 +
src/qemu/qemu_monitor_json.c | 27 ++++++++++++++++------
src/qemu/qemu_process.c | 2 +-
tests/qemumonitorjsontest.c | 25 +++++++++++++++++----
tests/qemuxml2argvtest.c | 12 ++++++----
7 files changed, 96 insertions(+), 30 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 7a8202a..9fce7a6 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -2306,7 +2306,8 @@ const char *virQEMUCapsGetPackage(virQEMUCapsPtr qemuCaps)
int
virQEMUCapsAddCPUDefinitions(virQEMUCapsPtr qemuCaps,
const char **name,
- size_t count)
+ size_t count,
+ virDomainCapsCPUUsable usable)
{
size_t i;
@@ -2316,7 +2317,7 @@ virQEMUCapsAddCPUDefinitions(virQEMUCapsPtr qemuCaps,
for (i = 0; i < count; i++) {
if (virDomainCapsCPUModelsAdd(qemuCaps->cpuDefinitions, name[i], -1,
- VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
+ usable) < 0)
return -1;
}
@@ -2327,10 +2328,12 @@ virQEMUCapsAddCPUDefinitions(virQEMUCapsPtr qemuCaps,
int
virQEMUCapsGetCPUDefinitions(virQEMUCapsPtr qemuCaps,
char ***names,
- size_t *count)
+ size_t *count,
+ bool usable)
{
size_t i;
char **models = NULL;
+ size_t n = 0;
*count = 0;
if (names)
@@ -2344,17 +2347,21 @@ virQEMUCapsGetCPUDefinitions(virQEMUCapsPtr qemuCaps,
for (i = 0; i < qemuCaps->cpuDefinitions->nmodels; i++) {
virDomainCapsCPUModelPtr cpu = qemuCaps->cpuDefinitions->models + i;
- if (models && VIR_STRDUP(models[i], cpu->name) < 0)
- goto error;
+ if (!usable ||
+ cpu->usable == VIR_DOMCAPS_CPU_USABLE_YES) {
+ if (models && VIR_STRDUP(models[n], cpu->name) < 0)
+ goto error;
+ n++;
+ }
}
if (names)
*names = models;
- *count = qemuCaps->cpuDefinitions->nmodels;
+ *count = n;
return 0;
error:
- virStringFreeListCount(models, i);
+ virStringFreeListCount(models, n);
return -1;
}
@@ -2713,9 +2720,15 @@ virQEMUCapsProbeQMPCPUDefinitions(virQEMUCapsPtr qemuCaps,
goto cleanup;
for (i = 0; i < ncpus; i++) {
+ virDomainCapsCPUUsable usable = VIR_DOMCAPS_CPU_USABLE_UNKNOWN;
+
+ if (cpus[i]->usable == VIR_TRISTATE_BOOL_YES)
+ usable = VIR_DOMCAPS_CPU_USABLE_YES;
+ else if (cpus[i]->usable == VIR_TRISTATE_BOOL_NO)
+ usable = VIR_DOMCAPS_CPU_USABLE_NO;
+
if (virDomainCapsCPUModelsAddSteal(qemuCaps->cpuDefinitions,
- &cpus[i]->name,
- VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
+ &cpus[i]->name, usable) < 0)
goto cleanup;
}
@@ -3128,6 +3141,17 @@ virQEMUCapsLoadCache(virCapsPtr caps,
goto cleanup;
for (i = 0; i < n; i++) {
+ int usable = VIR_DOMCAPS_CPU_USABLE_UNKNOWN;
+
+ if ((str = virXMLPropString(nodes[i], "usable")) &&
+ (usable = virDomainCapsCPUUsableTypeFromString(str)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown value '%s' in attribute
'usable'"),
+ str);
+ goto cleanup;
+ }
+ VIR_FREE(str);
+
if (!(str = virXMLPropString(nodes[i], "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing cpu name in QEMU capabilities
cache"));
@@ -3135,8 +3159,7 @@ virQEMUCapsLoadCache(virCapsPtr caps,
}
if (virDomainCapsCPUModelsAddSteal(qemuCaps->cpuDefinitions,
- &str,
- VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
+ &str, usable) < 0)
goto cleanup;
}
}
@@ -3301,7 +3324,13 @@ virQEMUCapsFormatCache(virQEMUCapsPtr qemuCaps,
if (qemuCaps->cpuDefinitions) {
for (i = 0; i < qemuCaps->cpuDefinitions->nmodels; i++) {
virDomainCapsCPUModelPtr cpu = qemuCaps->cpuDefinitions->models + i;
- virBufferEscapeString(&buf, "<cpu name='%s'/>\n",
cpu->name);
+ virBufferEscapeString(&buf, "<cpu name='%s'",
cpu->name);
+ if (cpu->usable) {
+ const char *usable;
+ usable = virDomainCapsCPUUsableTypeToString(cpu->usable);
+ virBufferAsprintf(&buf, " usable='%s'", usable);
+ }
+ virBufferAddLit(&buf, "/>\n");
}
}
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 6e7a855..c77ba13 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -428,10 +428,12 @@ const char *virQEMUCapsGetPackage(virQEMUCapsPtr qemuCaps);
unsigned int virQEMUCapsGetKVMVersion(virQEMUCapsPtr qemuCaps);
int virQEMUCapsAddCPUDefinitions(virQEMUCapsPtr qemuCaps,
const char **name,
- size_t count);
+ size_t count,
+ virDomainCapsCPUUsable usable);
int virQEMUCapsGetCPUDefinitions(virQEMUCapsPtr qemuCaps,
char ***names,
- size_t *count);
+ size_t *count,
+ bool usable);
virCPUDefPtr virQEMUCapsGetHostModel(virQEMUCapsPtr qemuCaps);
bool virQEMUCapsIsCPUModeSupported(virQEMUCapsPtr qemuCaps,
virCapsPtr caps,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index c3133c4..f81de68 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -913,6 +913,7 @@ typedef struct _qemuMonitorCPUDefInfo qemuMonitorCPUDefInfo;
typedef qemuMonitorCPUDefInfo *qemuMonitorCPUDefInfoPtr;
struct _qemuMonitorCPUDefInfo {
+ virTristateBool usable;
char *name;
};
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 6c13832..e2dfb34 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -4925,13 +4925,8 @@ qemuMonitorJSONGetCPUDefinitions(qemuMonitorPtr mon,
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
- if (!(data = virJSONValueObjectGetArray(reply, "return"))) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("query-cpu-definitions reply was missing return
data"));
- goto cleanup;
- }
-
- if ((n = virJSONValueArraySize(data)) < 0) {
+ if (!(data = virJSONValueObjectGetArray(reply, "return")) ||
+ (n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-cpu-definitions reply data was not an
array"));
goto cleanup;
@@ -4958,6 +4953,24 @@ qemuMonitorJSONGetCPUDefinitions(qemuMonitorPtr mon,
if (VIR_STRDUP(cpu->name, tmp) < 0)
goto cleanup;
+
+ if (virJSONValueObjectHasKey(child, "unavailable-features")) {
+ virJSONValuePtr blockers;
+
+ blockers = virJSONValueObjectGetArray(child,
+ "unavailable-features");
+ if (!blockers) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("unavailable-features in query-cpu-definitions
"
+ "reply data was not an array"));
+ goto cleanup;
+ }
+
+ if (virJSONValueArraySize(blockers) > 0)
+ cpu->usable = VIR_TRISTATE_BOOL_NO;
+ else
+ cpu->usable = VIR_TRISTATE_BOOL_YES;
+ }
}
ret = n;
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 1b67aee..50fb3de 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -5057,7 +5057,7 @@ qemuProcessUpdateGuestCPU(virDomainDefPtr def,
virQEMUCapsGetHostModel(qemuCaps)) < 0)
goto cleanup;
- if (virQEMUCapsGetCPUDefinitions(qemuCaps, &models, &nmodels) < 0 ||
+ if (virQEMUCapsGetCPUDefinitions(qemuCaps, &models, &nmodels, false) < 0
||
virCPUTranslate(def->os.arch, def->cpu, models, nmodels) < 0)
goto cleanup;
diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c
index d8fd958..7111bc8 100644
--- a/tests/qemumonitorjsontest.c
+++ b/tests/qemumonitorjsontest.c
@@ -432,10 +432,12 @@ testQemuMonitorJSONGetCPUDefinitions(const void *data)
" \"name\": \"qemu64\"
"
" }, "
" { "
- " \"name\": \"Opteron_G4\"
"
+ " \"name\": \"Opteron_G4\",
"
+ " \"unavailable-features\":
[\"vme\"]"
" }, "
" { "
- " \"name\": \"Westmere\"
"
+ " \"name\": \"Westmere\",
"
+ " \"unavailable-features\": []"
" } "
" ]"
"}") < 0)
@@ -452,6 +454,13 @@ testQemuMonitorJSONGetCPUDefinitions(const void *data)
}
#define CHECK(i, wantname) \
+ CHECK_FULL(i, wantname, VIR_TRISTATE_BOOL_ABSENT)
+
+#define CHECK_USABLE(i, wantname, usable) \
+ CHECK_FULL(i, wantname, \
+ usable ? VIR_TRISTATE_BOOL_YES : VIR_TRISTATE_BOOL_NO)
+
+#define CHECK_FULL(i, wantname, Usable) \
do { \
if (STRNEQ(cpus[i]->name, (wantname))) { \
virReportError(VIR_ERR_INTERNAL_ERROR, \
@@ -459,13 +468,21 @@ testQemuMonitorJSONGetCPUDefinitions(const void *data)
cpus[i]->name, (wantname)); \
goto cleanup; \
} \
+ if (cpus[i]->usable != (Usable)) { \
+ virReportError(VIR_ERR_INTERNAL_ERROR, \
+ "%s: expecting usable flag %d, got %d", \
+ cpus[i]->name, Usable, cpus[i]->usable); \
+ goto cleanup; \
+ } \
} while (0)
CHECK(0, "qemu64");
- CHECK(1, "Opteron_G4");
- CHECK(2, "Westmere");
+ CHECK_USABLE(1, "Opteron_G4", false);
+ CHECK_USABLE(2, "Westmere", true);
#undef CHECK
+#undef CHECK_USABLE
+#undef CHECK_FULL
ret = 0;
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 90d6aaf..a3fb8e7 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -303,20 +303,24 @@ testAddCPUModels(virQEMUCapsPtr caps, bool skipLegacy)
if (ARCH_IS_X86(arch)) {
if (virQEMUCapsAddCPUDefinitions(caps, x86Models,
- ARRAY_CARDINALITY(x86Models)) < 0)
+ ARRAY_CARDINALITY(x86Models),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
return -1;
if (!skipLegacy &&
virQEMUCapsAddCPUDefinitions(caps, x86LegacyModels,
- ARRAY_CARDINALITY(x86LegacyModels)) < 0)
+ ARRAY_CARDINALITY(x86LegacyModels),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
return -1;
} else if (ARCH_IS_ARM(arch)) {
if (virQEMUCapsAddCPUDefinitions(caps, armModels,
- ARRAY_CARDINALITY(armModels)) < 0)
+ ARRAY_CARDINALITY(armModels),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
return -1;
} else if (ARCH_IS_PPC64(arch)) {
if (virQEMUCapsAddCPUDefinitions(caps, ppc64Models,
- ARRAY_CARDINALITY(ppc64Models)) < 0)
+ ARRAY_CARDINALITY(ppc64Models),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
return -1;
}
--
2.10.2