Libvirt can compute baseline hypervisor cpu from list of S390 cpu models
by using QEMU QMP messages to compute baseline within QEMU.
QEMU only baselines one pair of CPU models per query so a sequence of
multiple QMP queries are needed if more than two CPU models are
baselined.
Full expansion of baseline S390 model supported using QEMU QMP messages to
compute expansion within QEMU.
Introduces qemuMonitorCPUModelInfoRemovePropByBoolValue to remove props
by value as required to convert between cpu model property lists that
indicate if property is or isn't include in model with true / false
value and the form of cpu model property lists that only enumerate
properties that are actually included in the model.
Introduces functions for virCPUDef / qemuMonitorCPUModelInfo conversion.
Signed-off-by: Chris Venteicher <cventeic(a)redhat.com>
---
src/qemu/qemu_capabilities.c | 128 +++++++++++++++++++++++++------
src/qemu/qemu_capabilities.h | 4 +
src/qemu/qemu_driver.c | 143 +++++++++++++++++++++++++++++++++--
src/qemu/qemu_monitor.c | 41 ++++++++++
src/qemu/qemu_monitor.h | 10 +++
src/qemu/qemu_monitor_json.c | 60 +++++++++++++++
src/qemu/qemu_monitor_json.h | 7 ++
7 files changed, 365 insertions(+), 28 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 5b1e4f1bbd..c5bf04993b 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -2785,7 +2785,8 @@ virQEMUCapsInitCPUModelS390(virQEMUCapsPtr qemuCaps,
virCPUDefPtr cpu,
bool migratable)
{
- size_t i;
+ virCPUDefPtr tmp = NULL;
+ int ret = -1;
if (!modelInfo) {
if (type == VIR_DOMAIN_VIRT_KVM) {
@@ -2798,32 +2799,18 @@ virQEMUCapsInitCPUModelS390(virQEMUCapsPtr qemuCaps,
return 2;
}
- if (VIR_STRDUP(cpu->model, modelInfo->name) < 0 ||
- VIR_ALLOC_N(cpu->features, modelInfo->nprops) < 0)
- return -1;
-
- cpu->nfeatures_max = modelInfo->nprops;
- cpu->nfeatures = 0;
-
- for (i = 0; i < modelInfo->nprops; i++) {
- virCPUFeatureDefPtr feature = cpu->features + cpu->nfeatures;
- qemuMonitorCPUPropertyPtr prop = modelInfo->props + i;
+ if (!(tmp = virQEMUCapsCPUModelInfoToCPUDef(migratable, modelInfo)))
+ goto cleanup;
- if (prop->type != QEMU_MONITOR_CPU_PROPERTY_BOOLEAN)
- continue;
+ /* Free original then copy over model, vendor, vendor_id and features */
+ virCPUDefStealModel(cpu, tmp, true);
- if (VIR_STRDUP(feature->name, prop->name) < 0)
- return -1;
+ ret = 0;
- if (!prop->value.boolean ||
- (migratable && prop->migratable == VIR_TRISTATE_BOOL_NO))
- feature->policy = VIR_CPU_FEATURE_DISABLE;
- else
- feature->policy = VIR_CPU_FEATURE_REQUIRE;
- cpu->nfeatures++;
- }
+ cleanup:
+ virCPUDefFree(tmp);
- return 0;
+ return ret;
}
@@ -3620,6 +3607,101 @@ virQEMUCapsLoadCache(virArch hostArch,
return ret;
}
+/* virCPUDef model => qemuMonitorCPUModelInfo name
+ * virCPUDef features => qemuMonitorCPUModelInfo boolean properties */
+qemuMonitorCPUModelInfoPtr
+virQEMUCapsCPUModelInfoFromCPUDef(const virCPUDef *cpuDef)
+{
+ size_t i;
+ qemuMonitorCPUModelInfoPtr cpuModel = NULL;
+ qemuMonitorCPUModelInfoPtr ret = NULL;
+
+ if (!cpuDef || (VIR_ALLOC(cpuModel) < 0))
+ goto cleanup;
+
+ VIR_DEBUG("cpuDef->model = %s", NULLSTR(cpuDef->model));
+
+ if (VIR_STRDUP(cpuModel->name, cpuDef->model) < 0 ||
+ VIR_ALLOC_N(cpuModel->props, cpuDef->nfeatures) < 0)
+ goto cleanup;
+
+ cpuModel->nprops = 0;
+
+ for (i = 0; i < cpuDef->nfeatures; i++) {
+ qemuMonitorCPUPropertyPtr prop = &(cpuModel->props[cpuModel->nprops]);
+ virCPUFeatureDefPtr feature = &(cpuDef->features[i]);
+
+ if (!(feature->name) ||
+ VIR_STRDUP(prop->name, feature->name) < 0)
+ goto cleanup;
+
+ prop->type = QEMU_MONITOR_CPU_PROPERTY_BOOLEAN;
+
+ prop->value.boolean = feature->policy == -1 || /* policy undefined */
+ feature->policy == VIR_CPU_FEATURE_FORCE ||
+ feature->policy == VIR_CPU_FEATURE_REQUIRE;
+
+ cpuModel->nprops++;
+ }
+
+ VIR_STEAL_PTR(ret, cpuModel);
+
+ cleanup:
+ qemuMonitorCPUModelInfoFree(cpuModel);
+ return ret;
+}
+
+
+/* qemuMonitorCPUModelInfo name => virCPUDef model
+ * qemuMonitorCPUModelInfo boolean properties => virCPUDef features
+ *
+ * migratable true: mark non-migratable features as disabled
+ * false: allow all features as required
+ */
+virCPUDefPtr
+virQEMUCapsCPUModelInfoToCPUDef(bool migratable, qemuMonitorCPUModelInfoPtr model)
+{
+ virCPUDefPtr cpu = NULL;
+ virCPUDefPtr ret = NULL;
+ size_t i;
+
+ if (!model || VIR_ALLOC(cpu) < 0)
+ goto cleanup;
+
+ VIR_DEBUG("model->name= %s", NULLSTR(model->name));
+
+ if (VIR_STRDUP(cpu->model, model->name) < 0 ||
+ VIR_ALLOC_N(cpu->features, model->nprops) < 0)
+ goto cleanup;
+
+ cpu->nfeatures_max = model->nprops;
+ cpu->nfeatures = 0;
+
+ for (i = 0; i < model->nprops; i++) {
+ virCPUFeatureDefPtr feature = cpu->features + cpu->nfeatures;
+ qemuMonitorCPUPropertyPtr prop = model->props + i;
+
+ if (prop->type != QEMU_MONITOR_CPU_PROPERTY_BOOLEAN)
+ continue;
+
+ if (VIR_STRDUP(feature->name, prop->name) < 0)
+ goto cleanup;
+
+ if (!prop->value.boolean ||
+ (migratable && prop->migratable == VIR_TRISTATE_BOOL_NO))
+ feature->policy = VIR_CPU_FEATURE_DISABLE;
+ else
+ feature->policy = VIR_CPU_FEATURE_REQUIRE;
+
+ cpu->nfeatures++;
+ }
+
+ VIR_STEAL_PTR(ret, cpu);
+
+ cleanup:
+ virCPUDefFree(cpu);
+ return ret;
+}
static void
virQEMUCapsFormatHostCPUModelInfo(virQEMUCapsPtr qemuCaps,
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 934620ed31..98ccec154e 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -571,6 +571,10 @@ int virQEMUCapsGetMachineTypesCaps(virQEMUCapsPtr qemuCaps,
void virQEMUCapsFilterByMachineType(virQEMUCapsPtr qemuCaps,
const char *machineType);
+qemuMonitorCPUModelInfoPtr virQEMUCapsCPUModelInfoFromCPUDef(const virCPUDef *cpuDef);
+virCPUDefPtr virQEMUCapsCPUModelInfoToCPUDef(bool migratable,
+ qemuMonitorCPUModelInfoPtr model);
+
virFileCachePtr virQEMUCapsCacheNew(const char *libDir,
const char *cacheDir,
uid_t uid,
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index b238309852..8db7d01660 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -13406,6 +13406,77 @@ qemuConnectBaselineCPU(virConnectPtr conn ATTRIBUTE_UNUSED,
return cpustr;
}
+/* in:
+ * cpus[0]->model = "z14";
+ * cpus[0]->features[0].name = "xxx";
+ * cpus[0]->features[1].name = "yyy";
+ * ***
+ * cpus[n]->model = "z13";
+ * cpus[n]->features[0].name = "xxx";
+ * cpus[n]->features[1].name = "yyy";
+ *
+ * out:
+ * *baseline->model = "z13-base";
+ * *baseline->features[0].name = "yyy";
+ */
+static int
+qemuConnectBaselineHypervisorCPUViaQEMU(qemuMonitorPtr mon,
+ virCPUDefPtr *cpus,
+ virCPUDefPtr *baseline)
+{
+ qemuMonitorCPUModelInfoPtr model_baseline = NULL;
+ qemuMonitorCPUModelInfoPtr new_model_baseline = NULL;
+ qemuMonitorCPUModelInfoPtr next_model = NULL;
+ bool migratable = true;
+ int ret = -1;
+ size_t i;
+
+ *baseline = NULL;
+
+ if (!cpus || !cpus[0]) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s", _("no cpus"));
+ goto cleanup;
+ }
+
+ for (i = 0; cpus[i]; i++) { /* cpus terminated by NULL element */
+ virCPUDefPtr cpu = cpus[i];
+
+ VIR_DEBUG("cpu[%lu]->model = %s", i, NULLSTR(cpu->model));
+
+ if (!(next_model = virQEMUCapsCPUModelInfoFromCPUDef(cpu)))
+ goto cleanup;
+
+ if (i == 0) {
+ model_baseline = next_model;
+ continue;
+ }
+
+ if (qemuMonitorGetCPUModelBaseline(mon, model_baseline,
+ next_model, &new_model_baseline) < 0)
+ goto cleanup;
+
+ qemuMonitorCPUModelInfoFree(model_baseline);
+ qemuMonitorCPUModelInfoFree(next_model);
+
+ next_model = NULL;
+
+ model_baseline = new_model_baseline;
+ }
+
+ if (!(*baseline = virQEMUCapsCPUModelInfoToCPUDef(migratable, model_baseline)))
+ goto cleanup;
+
+ VIR_DEBUG("baseline->model = %s", (*baseline)->model);
+
+ ret = 0;
+
+ cleanup:
+ qemuMonitorCPUModelInfoFree(model_baseline);
+ qemuMonitorCPUModelInfoFree(next_model);
+
+ return ret;
+}
+
static char *
qemuConnectBaselineHypervisorCPU(virConnectPtr conn,
@@ -13427,6 +13498,11 @@ qemuConnectBaselineHypervisorCPU(virConnectPtr conn,
virCPUDefPtr cpu = NULL;
char *cpustr = NULL;
char **features = NULL;
+ bool forceTCG = false;
+ qemuMonitorCPUModelInfoPtr modelInfo = NULL;
+ qemuMonitorCPUModelInfoPtr expansion = NULL;
+ virDomainObjPtr dom = NULL;
+ qemuMonitorPtr mon = NULL;
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES |
VIR_CONNECT_BASELINE_CPU_MIGRATABLE, NULL);
@@ -13434,8 +13510,6 @@ qemuConnectBaselineHypervisorCPU(virConnectPtr conn,
if (virConnectBaselineHypervisorCPUEnsureACL(conn) < 0)
goto cleanup;
- migratable = !!(flags & VIR_CONNECT_BASELINE_CPU_MIGRATABLE);
-
if (!(cpus = virCPUDefListParse(xmlCPUs, ncpus, VIR_CPU_TYPE_AUTO)))
goto cleanup;
@@ -13448,6 +13522,20 @@ qemuConnectBaselineHypervisorCPU(virConnectPtr conn,
if (!qemuCaps)
goto cleanup;
+ /* QEMU can enumerate non-migratable cpu model features for some archs like x86
+ * migratable == true: ask for and include only migratable features
+ * migratable == false: ask for and include all features
+ */
+ migratable = !!(flags & VIR_CONNECT_BASELINE_CPU_MIGRATABLE);
+
+ if (ARCH_IS_S390(arch)) {
+ /* QEMU for S390 arch only enumerates migratable features
+ * No reason to explicitly ask QEMU for or include non-migratable
+ * features
+ */
+ migratable = true;
+ }
+
if (!(cpuModels = virQEMUCapsGetCPUDefinitions(qemuCaps, virttype)) ||
cpuModels->nmodels == 0) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
@@ -13472,6 +13560,20 @@ qemuConnectBaselineHypervisorCPU(virConnectPtr conn,
if (!(cpu = virCPUBaseline(arch, cpus, ncpus, cpuModels,
(const char **)features, migratable)))
goto cleanup;
+ } else if (ARCH_IS_S390(arch)) {
+ const char *binary = virQEMUCapsGetBinary(qemuCaps);
+ virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
+
+ if (!(dom = qmpDomainObjNew(binary, forceTCG, cfg->libDir, cfg->user,
cfg->group)))
+ goto cleanup;
+
+ if (qemuProcessStartQmp(dom) < 0)
+ goto cleanup;
+
+ mon = QMP_DOMAIN_MONITOR(dom);
+
+ if ((qemuConnectBaselineHypervisorCPUViaQEMU(mon, cpus, &cpu) < 0) ||
!cpu)
+ goto cleanup;
} else {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("computing baseline hypervisor CPU is not supported "
@@ -13481,9 +13583,36 @@ qemuConnectBaselineHypervisorCPU(virConnectPtr conn,
cpu->fallback = VIR_CPU_FALLBACK_FORBID;
- if ((flags & VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES) &&
- virCPUExpandFeatures(arch, cpu) < 0)
- goto cleanup;
+ if (flags & VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES) {
+ if (ARCH_IS_X86(arch)) {
+ if (virCPUExpandFeatures(arch, cpu) < 0)
+ goto cleanup;
+ } else if (ARCH_IS_S390(arch)) {
+ if (!(modelInfo = virQEMUCapsCPUModelInfoFromCPUDef(cpu)))
+ goto cleanup;
+
+ virCPUDefFree(cpu);
+ cpu = NULL;
+
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Feature Expansion not supported with this QEMU
binary"));
+ goto cleanup;
+ }
+
+ if (qemuMonitorGetCPUModelExpansion(mon,
+ QEMU_MONITOR_CPU_MODEL_EXPANSION_FULL,
+ migratable, modelInfo, &expansion)
< 0)
+ goto cleanup;
+
+ /* Expansion enumerates all features
+ * Baseline reply enumerates only in-model (true) features */
+ qemuMonitorCPUModelInfoRemovePropByBoolValue(expansion, false);
+
+ if (!(cpu = virQEMUCapsCPUModelInfoToCPUDef(migratable, expansion)))
+ goto cleanup;
+ }
+ }
cpustr = virCPUDefFormat(cpu, NULL);
@@ -13492,6 +13621,10 @@ qemuConnectBaselineHypervisorCPU(virConnectPtr conn,
virCPUDefFree(cpu);
virObjectUnref(qemuCaps);
virStringListFree(features);
+ qemuMonitorCPUModelInfoFree(modelInfo);
+ qemuMonitorCPUModelInfoFree(expansion);
+ qemuProcessStopQmp(dom);
+ virDomainObjEndAPI(&dom);
return cpustr;
}
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 507710d241..7045186c4e 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -3801,6 +3801,47 @@ qemuMonitorCPUModelInfoCopy(const qemuMonitorCPUModelInfo *orig)
}
+/* Squash CPU Model Info property list
+ * removing props of type boolean matching value */
+void
+qemuMonitorCPUModelInfoRemovePropByBoolValue(qemuMonitorCPUModelInfoPtr model,
+ bool value)
+{
+ qemuMonitorCPUPropertyPtr src;
+ qemuMonitorCPUPropertyPtr dst;
+ size_t i;
+ size_t dst_nprops = 0;
+
+ for (i = 0; i < model->nprops; i++) {
+ src = &(model->props[i]);
+ dst = &(model->props[dst_nprops]);
+
+ if (src->type == QEMU_MONITOR_CPU_PROPERTY_BOOLEAN &&
+ src->value.boolean == value)
+ continue;
+
+ *dst = *src;
+
+ dst_nprops++;
+ }
+
+ model->nprops = dst_nprops;
+
+ ignore_value(VIR_REALLOC_N_QUIET(model->props, dst_nprops));
+}
+
+
+int
+qemuMonitorGetCPUModelBaseline(qemuMonitorPtr mon,
+ qemuMonitorCPUModelInfoPtr model_a,
+ qemuMonitorCPUModelInfoPtr model_b,
+ qemuMonitorCPUModelInfoPtr *model_baseline)
+{
+ QEMU_CHECK_MONITOR(mon);
+
+ return qemuMonitorJSONGetCPUModelBaseline(mon, model_a, model_b, model_baseline);
+}
+
int
qemuMonitorCPUModelInfoBoolPropAdd(qemuMonitorCPUModelInfoPtr model,
const char *prop_name,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index d3efd37099..fad39945d3 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -1053,6 +1053,16 @@ int qemuMonitorCPUModelInfoBoolPropAdd(qemuMonitorCPUModelInfoPtr
model,
bool prop_value)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+void qemuMonitorCPUModelInfoRemovePropByBoolValue(qemuMonitorCPUModelInfoPtr model,
+ bool value)
+ ATTRIBUTE_NONNULL(1);
+
+int qemuMonitorGetCPUModelBaseline(qemuMonitorPtr mon,
+ qemuMonitorCPUModelInfoPtr model_a,
+ qemuMonitorCPUModelInfoPtr model_b,
+ qemuMonitorCPUModelInfoPtr *model_baseline)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
+
int qemuMonitorGetCommands(qemuMonitorPtr mon,
char ***commands);
int qemuMonitorGetEvents(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 4e6e220a8c..1cac3d2964 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -5708,6 +5708,66 @@ qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon,
}
+int
+qemuMonitorJSONGetCPUModelBaseline(qemuMonitorPtr mon,
+ qemuMonitorCPUModelInfoPtr model_a,
+ qemuMonitorCPUModelInfoPtr model_b,
+ qemuMonitorCPUModelInfoPtr *model_baseline)
+{
+ int ret = -1;
+ virJSONValuePtr cmd = NULL;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr data = NULL;
+ virJSONValuePtr modela = NULL;
+ virJSONValuePtr modelb = NULL;
+ virJSONValuePtr cpu_model = NULL;
+
+ *model_baseline = NULL;
+
+ if (!(modela = qemuMonitorJSONBuildCPUModelInfoToJSON(model_a)) ||
+ !(modelb = qemuMonitorJSONBuildCPUModelInfoToJSON(model_b)))
+ goto cleanup;
+
+ if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-baseline",
+ "a:modela", &modela,
+ "a:modelb", &modelb,
+ NULL)))
+ goto cleanup;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("QEMU doesn't support baseline or recognize model %s or
%s"),
+ model_a->name,
+ model_b->name);
+ goto cleanup;
+ }
+
+ data = virJSONValueObjectGetObject(reply, "return");
+
+ if (!(cpu_model = virJSONValueObjectGetObject(data, "model"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("query-cpu-model-baseline reply data was missing
'model'"));
+ goto cleanup;
+ }
+
+ if (!(*model_baseline = qemuMonitorJSONBuildCPUModelInfoFromJSON(cpu_model)))
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ virJSONValueFree(modela);
+ virJSONValueFree(modelb);
+
+ return ret;
+}
+
+
int qemuMonitorJSONGetCommands(qemuMonitorPtr mon,
char ***commands)
{
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 2d5679094e..e57f2e3590 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -372,6 +372,13 @@ int qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon,
qemuMonitorCPUModelInfoPtr *expansion)
ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5);
+int qemuMonitorJSONGetCPUModelBaseline(qemuMonitorPtr mon,
+ qemuMonitorCPUModelInfoPtr model_a,
+ qemuMonitorCPUModelInfoPtr model_b,
+ qemuMonitorCPUModelInfoPtr *model_baseline)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
+
+
int qemuMonitorJSONGetCommands(qemuMonitorPtr mon,
char ***commands)
ATTRIBUTE_NONNULL(2);
--
2.17.1