Interfaces with QEMU to compare CPU models. The command takes two CPU models, A and B, that are given a model name and an optional list of CPU features. Through the query-cpu-model-comparison command issued via QMP, a result is produced that contains the comparison evaluation string (identical, superset, subset [s390x specific], incompatible) as well as a list of properties (aka CPU features) responsible for the subset or superset result. Though the "responsible properties" are captured, they are not reported by libvirt. This command is hooked into the virsh hypervisor-cpu-compare command. As such, the CPU model XML provided to the command will be compared to the hypervisor CPU contained in the QEMU capabilities file for the appropriate QEMU binary (for s390x, this CPU definition can be observed via virsh domcapabilities). s390x can report that the first model (A) is a subset of the second (B). If A is the hypervisor CPU and a subset of B (the CPU contained in the XML), then B would not be able to run on this machine and thus we will report that CPU model B is incompatible. Signed-off-by: Collin Walling <walling@xxxxxxxxxxxxx> --- src/qemu/qemu_capabilities.c | 47 +++++++++++++++++++ src/qemu/qemu_capabilities.h | 9 ++++ src/qemu/qemu_driver.c | 10 ++++ src/qemu/qemu_monitor.c | 22 +++++++++ src/qemu/qemu_monitor.h | 10 ++++ src/qemu/qemu_monitor_json.c | 89 ++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 11 +++++ 7 files changed, 198 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 6163fd5399..1e4b05d535 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -5400,3 +5400,50 @@ virQEMUCapsStripMachineAliases(virQEMUCapsPtr qemuCaps) for (i = 0; i < qemuCaps->nmachineTypes; i++) VIR_FREE(qemuCaps->machineTypes[i].alias); } + +virCPUCompareResult +virQEMUCapsCPUModelComparison(virQEMUCapsPtr qemuCaps, + const char *libDir, + uid_t runUid, + gid_t runGid, + virCPUDefPtr cpu_a, + virCPUDefPtr cpu_b, + bool failIncompatible) +{ + qemuProcessQMPPtr proc = NULL; + qemuMonitorCPUModelInfoPtr result; + int ret = -1; + + if (!(proc = qemuProcessQMPNew(qemuCaps->binary, libDir, + runUid, runGid, false))) + goto cleanup; + + if (qemuProcessQMPStart(proc) < 0) + goto cleanup; + + if (qemuMonitorGetCPUModelComparison(proc->mon, cpu_a->model, + cpu_a->nfeatures, cpu_a->features, + cpu_b->model, cpu_b->nfeatures, + cpu_b->features, &result) < 0) + goto cleanup; + + if (STREQ(result->name, "incompatible") || + STREQ(result->name, "subset")) + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + else if (STREQ(result->name, "identical")) + ret = VIR_CPU_COMPARE_IDENTICAL; + else if (STREQ(result->name, "superset")) + ret = VIR_CPU_COMPARE_SUPERSET; + + if (failIncompatible && ret == VIR_CPU_COMPARE_INCOMPATIBLE) { + ret = VIR_CPU_COMPARE_ERROR; + virReportError(VIR_ERR_CPU_INCOMPATIBLE, NULL); + } + + cleanup: + if (ret < 0) + virQEMUCapsLogProbeFailure(qemuCaps->binary); + + qemuProcessQMPFree(proc); + return ret; +} diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 0cc3d4d512..aa27b18d32 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -647,4 +647,13 @@ virQEMUCapsGetSEVCapabilities(virQEMUCapsPtr qemuCaps); virArch virQEMUCapsArchFromString(const char *arch); const char *virQEMUCapsArchToString(virArch arch); +virCPUCompareResult +virQEMUCapsCPUModelComparison(virQEMUCapsPtr qemuCaps, + const char *libDir, + uid_t runUid, + gid_t runGid, + virCPUDefPtr cpu_a, + virCPUDefPtr cpu_b, + bool failIncompatible); + #endif /* LIBVIRT_QEMU_CAPABILITIES_H */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4510b0ce60..2084da1b9d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13463,9 +13463,11 @@ qemuConnectCompareHypervisorCPU(virConnectPtr conn, { int ret = VIR_CPU_COMPARE_ERROR; virQEMUDriverPtr driver = conn->privateData; + virQEMUDriverConfigPtr config = driver->config; virQEMUCapsPtr qemuCaps = NULL; bool failIncompatible; virCPUDefPtr hvCPU; + virCPUDefPtr cpu; virArch arch; virDomainVirtType virttype; @@ -13500,6 +13502,14 @@ qemuConnectCompareHypervisorCPU(virConnectPtr conn, if (ARCH_IS_X86(arch)) { ret = virCPUCompareXML(arch, hvCPU, xmlCPU, failIncompatible); + } else if (ARCH_IS_S390(arch) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_COMPARISON)) { + if (virCPUDefParseXMLHelper(xmlCPU, NULL, VIR_CPU_TYPE_AUTO, &cpu) < 0) + goto cleanup; + + ret = virQEMUCapsCPUModelComparison(qemuCaps, config->libDir, + config->user, config->group, + hvCPU, cpu, failIncompatible); } else { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("comparing with the hypervisor CPU is not supported " diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index babcbde878..5fd132a268 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3684,6 +3684,28 @@ qemuMonitorGetCPUModelExpansion(qemuMonitorPtr mon, } +int +qemuMonitorGetCPUModelComparison(qemuMonitorPtr mon, + const char *model_a_name, + int model_a_nprops, + virCPUFeatureDefPtr model_a_props, + const char *model_b_name, + int model_b_nprops, + virCPUFeatureDefPtr model_b_props, + qemuMonitorCPUModelInfoPtr *model_result) +{ + VIR_DEBUG("model_a_name=%s model_a_nprops=%d model_b_name=%s model_b_nprops=%d", + model_a_name, model_a_nprops, model_b_name, model_b_nprops); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONGetCPUModelComparison(mon, model_a_name, model_a_nprops, + model_a_props, model_b_name, + model_b_nprops, model_b_props, + model_result); +} + + void qemuMonitorCPUModelInfoFree(qemuMonitorCPUModelInfoPtr model_info) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index caf62af5e2..0a3ee493b1 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1066,6 +1066,16 @@ int qemuMonitorGetCPUModelExpansion(qemuMonitorPtr mon, void qemuMonitorCPUModelInfoFree(qemuMonitorCPUModelInfoPtr model_info); +int +qemuMonitorGetCPUModelComparison(qemuMonitorPtr mon, + const char *model_a_name, + int model_a_nprops, + virCPUFeatureDefPtr model_a_props, + const char *model_b_name, + int model_b_nprops, + virCPUFeatureDefPtr model_b_props, + qemuMonitorCPUModelInfoPtr *model_result); + qemuMonitorCPUModelInfoPtr qemuMonitorCPUModelInfoCopy(const qemuMonitorCPUModelInfo *orig); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 992cec9efb..97cb7191e1 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -5704,6 +5704,95 @@ qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon, } +static int +qemuMonitorJSONParseCPUModelPropName(size_t pos ATTRIBUTE_UNUSED, + virJSONValuePtr item, + void *opaque) +{ + return qemuMonitorJSONParseCPUModelProperty(virJSONValueGetString(item), + item, opaque); +} + + +int +qemuMonitorJSONGetCPUModelComparison(qemuMonitorPtr mon, + const char *model_a_name, + int model_a_nprops, + virCPUFeatureDefPtr model_a_props, + const char *model_b_name, + int model_b_nprops, + virCPUFeatureDefPtr model_b_props, + qemuMonitorCPUModelInfoPtr *model_result) +{ + int ret = -1; + virJSONValuePtr model_a; + virJSONValuePtr model_b = NULL; + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + virJSONValuePtr data; + const char *result_name; + virJSONValuePtr props; + qemuMonitorCPUModelInfoPtr compare = NULL; + + if (!(model_a = qemuMonitorJSONMakeCPUModel(model_a_name, model_a_nprops, model_a_props, true)) || + !(model_b = qemuMonitorJSONMakeCPUModel(model_b_name, model_b_nprops, model_b_props, true))) + goto cleanup; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-comparison", + "a:modela", &model_a, + "a:modelb", &model_b, + NULL))) + goto cleanup; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + goto cleanup; + + data = virJSONValueObjectGetObject(reply, "return"); + + if (!(result_name = virJSONValueObjectGetString(data, "result"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-cpu-model-comparison reply data was missing " + "'result'")); + goto cleanup; + } + + if (!(props = virJSONValueObjectGetArray(data, "responsible-properties"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-cpu-model-comparison reply data was missing " + "'responsible-properties'")); + goto cleanup; + } + + if (VIR_ALLOC(compare) < 0) + goto cleanup; + + if (VIR_STRDUP(compare->name, result_name) < 0) + goto cleanup; + + if (VIR_ALLOC_N(compare->props, virJSONValueArraySize(props)) < 0) + goto cleanup; + + if (virJSONValueArrayForeachSteal(props, + qemuMonitorJSONParseCPUModelPropName, + compare) < 0) + goto cleanup; + + VIR_STEAL_PTR(*model_result, compare); + ret = 0; + + cleanup: + qemuMonitorCPUModelInfoFree(compare); + virJSONValueFree(cmd); + virJSONValueFree(reply); + virJSONValueFree(model_a); + virJSONValueFree(model_b); + 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 c10513da15..e69be46dd5 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -367,6 +367,17 @@ int qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon, qemuMonitorCPUModelInfoPtr *model_info) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(5); +int +qemuMonitorJSONGetCPUModelComparison(qemuMonitorPtr mon, + const char *model_a_name, + int model_a_nprops, + virCPUFeatureDefPtr model_a_props, + const char *model_b_name, + int model_b_nprops, + virCPUFeatureDefPtr model_b_props, + qemuMonitorCPUModelInfoPtr *model_result) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) ATTRIBUTE_NONNULL(8); + int qemuMonitorJSONGetCommands(qemuMonitorPtr mon, char ***commands) ATTRIBUTE_NONNULL(2); -- 2.20.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list