[libvirt] [PATCH v1 0/4] CPU Model Comparsion via QEMU

[Overview] This patch series implements an interface to "query-cpu-model-comparison" (available QEMU ~2.8.0) via virsh cpu-compare. [Using This Feature] Run virsh cpu-compare (ensure you are running the virsh in your build dir) and pass it an xml file describing a cpu definition. You can copy the cpu xml from virsh capabilities (if you want to compare the host cpu to itself), or a cpu defined in any guest xml. Additionally, you can create a cpu xml as such (e.g. for s390x): <cpu> <arch>s390x</arch> <model fallback='forbid'>model_name</model> <feature policy='require|disable' name='feature_name'/> </cpu> NOTE: the presence of <arch> is optional and it will treat the cpu defined in the xml as a host cpu. This will disregard all feature policies (i.e. all features listed will behave with policy='require', even if disable is specified). NOTE: as s390x only supports feature policies 'require' and 'disable', I am uncertain how to handle the other policies when parsing CPUDef to JSON. [Example Output] On an s390x system running a z13.2, this is the expected output (where each file describes a CPU model corresponding to the name of the file): $ virsh cpu-compare zEC12.xml Host CPU is a superset of CPU described in zEC12.xml $ virsh cpu-compare z13.2.xml CPU described in z13.2.xml is identical to host CPU $ virsh cpu-compare z14.xml CPU described in z14.xml is incompatible with host CPU $ virsh cpu-compare z14.xml --error error: Failed to compare host CPU with z14.xml error: the CPU is incompatible with host CPU $ virsh cpu-compare z12345.xml error: Failed to compare host CPU with z12345cpu.xml error: internal error: unable to execute QEMU command 'query-cpu-model-comparison': The CPU definition 'z12345-base' is unknown. [Patch Rundown] The first patch copies the host CPU definition from qemuCaps to virCaps so we can easily access the host CPU model and features when we handle the CPU comparison in qemu_driver. Note that we take care to not clobber anything already stored in the host CPU definition until we have successfully constructed a new host CPU definition. The second patch refactors the xml -> CPUDef code. This is used in virCPUCompareXML, qemuCompareCPU (and potentially for baseline). The third patch creates the interface from libvirt to QEMU and handles parsing the CPUDef -> JSON and JSON -> CPUModelInfo. In order to respect the data returned from this command, we capture the "responsible_properties" field from the comparison command even though it is not entirely necessary for what we are trying to accomplish here (which is to simply report if the comparison result is "incompatible", "superset", or "identical"). The fourth patch creates the interface from virsh cpu-compare to the qemu driver. The qemu driver will handle parsing the xml passed to the virsh command and parsing the result of the QMP command. Note that the "incompatible" and "subset" results are grouped together (and report "incompatible"). TODO: - implement cpu-baseline (I've noted Chris Venteicher's current progress on this): https://www.redhat.com/archives/libvir-list/2018-April/msg01274.html - update qemucapabilitiestest files for qemu >= 2.8.0 (need to add comparison command) - consider adding a new test file for comparing cpu models via QEMU if necessary Collin Walling (4): qemu: place qemuCaps host cpu model in virCaps cpu_conf: xml to cpu definition parse helper qemu: implement query-cpu-model-comparison via qemu qemu: hook up cpu-comparison to qemu driver src/conf/cpu_conf.c | 30 ++++++++++ src/conf/cpu_conf.h | 6 ++ src/cpu/cpu.c | 14 +---- src/libvirt_private.syms | 1 + src/qemu/qemu_capabilities.c | 108 +++++++++++++++++++++++++++++++++++- src/qemu/qemu_capabilities.h | 9 +++ src/qemu/qemu_driver.c | 96 ++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 14 +++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 128 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 6 ++ 11 files changed, 404 insertions(+), 14 deletions(-) -- 2.7.4

Some architectures retrieve their host CPU model data from QEMU via qmp-query-cpu-model-expansion and stores it in the QEMU cache files. Let's take this data and also store it in virCaps so we can easily retrieve the host CPU model for later use. We should also take care to ensure that we always query the same cache file consistently between libvirtd executions, so let's query the qemuCaps from the first qemu-system-ARCH found in $PATH (via virQEMUCapsCacheLookup). Signed-off-by: Collin Walling <walling@linux.ibm.com> --- src/qemu/qemu_capabilities.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index cb716ff..d2eb813 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -896,6 +896,54 @@ virQEMUCapsProbeHostCPUForEmulator(virArch hostArch, } +static int +virQEMUProbeHostCPUModelFromBinary(virFileCachePtr cache, + virArch hostarch, + virCPUDefPtr *hostCPU) +{ + char *binary; + virQEMUCapsPtr qemuCaps = NULL; + virCPUDefPtr qemuCpu; + virCPUDefPtr tmp = NULL; + int ret = -1; + + if (!(binary = virQEMUCapsFindBinaryForArch(hostarch, hostarch))) + goto cleanup; + + if (!(qemuCaps = virQEMUCapsCacheLookup(cache, binary))) + goto cleanup; + + /* If QEMU does not report the host's cpu model, then fail gracefully */ + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION)) { + ret = 0; + goto cleanup; + } + + if (!(qemuCpu = virQEMUCapsGetHostModel(qemuCaps, VIR_DOMAIN_VIRT_KVM, + VIR_QEMU_CAPS_HOST_CPU_REPORTED))) + goto cleanup; + + if (VIR_ALLOC(tmp) < 0) + goto cleanup; + + if (!(tmp = virCPUDefCopyWithoutModel(*hostCPU))) + goto cleanup; + + if (virCPUDefCopyModel(tmp, qemuCpu, false) < 0) + goto cleanup; + + virCPUDefFree(*hostCPU); + VIR_STEAL_PTR(*hostCPU, tmp); + ret = 0; + + cleanup: + VIR_FREE(binary); + virCPUDefFree(tmp); + virObjectUnref(qemuCaps); + return ret; +} + + virCapsPtr virQEMUCapsInit(virFileCachePtr cache) { @@ -922,6 +970,11 @@ virQEMUCapsInit(virFileCachePtr cache) if (!(caps->host.cpu = virCPUProbeHost(caps->host.arch))) VIR_WARN("Failed to get host CPU"); + /* Some archs get host cpu information via QEMU */ + if (caps->host.cpu && !caps->host.cpu->model && + virQEMUProbeHostCPUModelFromBinary(cache, hostarch, &caps->host.cpu) < 0) + VIR_WARN("Failed to get host CPU model from QEMU"); + /* Add the power management features of the host */ if (virNodeSuspendGetTargetMask(&caps->host.powerMgmt) < 0) VIR_WARN("Failed to get host power management capabilities"); -- 2.7.4

Implement an xml to virCPUDefPtr helper that handles the ctxt prerequisite for virCPUDefParseXML. Signed-off-by: Collin Walling <walling@linux.ibm.com> --- src/conf/cpu_conf.c | 30 ++++++++++++++++++++++++++++++ src/conf/cpu_conf.h | 6 ++++++ src/cpu/cpu.c | 14 +------------- src/libvirt_private.syms | 1 + 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 43a3ab5..b2e726a 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -250,6 +250,36 @@ virCPUDefCopy(const virCPUDef *cpu) } +int +virCPUDefParseXMLHelper(const char *xml, + const char *xpath, + virCPUType type, + virCPUDefPtr *cpu) +{ + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + int ret = -1; + + if (!xml) { + virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing CPU definition")); + goto cleanup; + } + + if (!(doc = virXMLParseStringCtxt(xml, _("(CPU_definition)"), &ctxt))) + goto cleanup; + + if (virCPUDefParseXML(ctxt, xpath, type, cpu) < 0) + goto cleanup; + + ret = 0; + + cleanup: + xmlFreeDoc(doc); + xmlXPathFreeContext(ctxt); + return ret; +} + + /* * Parses CPU definition XML from a node pointed to by @xpath. If @xpath is * NULL, the current node of @ctxt is used (i.e., it is a shortcut to "."). diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index 9f2e7ee..f8f57fa 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -184,6 +184,12 @@ virCPUDefPtr virCPUDefCopyWithoutModel(const virCPUDef *cpu); int +virCPUDefParseXMLHelper(const char *xml, + const char *xpath, + virCPUType type, + virCPUDefPtr *cpu); + +int virCPUDefParseXML(xmlXPathContextPtr ctxt, const char *xpath, virCPUType mode, diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 047e3b1..8b829a3 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -114,31 +114,19 @@ virCPUCompareXML(virArch arch, const char *xml, bool failIncompatible) { - xmlDocPtr doc = NULL; - xmlXPathContextPtr ctxt = NULL; virCPUDefPtr cpu = NULL; virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR; VIR_DEBUG("arch=%s, host=%p, xml=%s", virArchToString(arch), host, NULLSTR(xml)); - if (!xml) { - virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing CPU definition")); - goto cleanup; - } - - if (!(doc = virXMLParseStringCtxt(xml, _("(CPU_definition)"), &ctxt))) - goto cleanup; - - if (virCPUDefParseXML(ctxt, NULL, VIR_CPU_TYPE_AUTO, &cpu) < 0) + if (virCPUDefParseXMLHelper(xml, NULL, VIR_CPU_TYPE_AUTO, &cpu) < 0) goto cleanup; ret = virCPUCompare(arch, host, cpu, failIncompatible); cleanup: virCPUDefFree(cpu); - xmlXPathFreeContext(ctxt); - xmlFreeDoc(doc); return ret; } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index cab324c..502fe48 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -86,6 +86,7 @@ virCPUDefIsEqual; virCPUDefListFree; virCPUDefListParse; virCPUDefParseXML; +virCPUDefParseXMLHelper; virCPUDefStealModel; virCPUDefUpdateFeature; virCPUModeTypeToString; -- 2.7.4

Interfaces with QEMU to compare two 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 sent to QEMU via QMP, a third CPU model, C, is returned that contains the comparison result (identical, superset, subset, incompatible) as its model name as well as a list of properties (aka CPU features) responsible for this result. Signed-off-by: Collin Walling <walling@linux.ibm.com> --- src/qemu/qemu_capabilities.c | 53 ++++++++++++++++++ src/qemu/qemu_capabilities.h | 6 ++ src/qemu/qemu_monitor.c | 14 +++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 128 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 6 ++ 6 files changed, 213 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index d2eb813..d385ad8 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -468,6 +468,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "virtio-tablet-ccw", "qcow2-luks", "pcie-pci-bridge", + "query-cpu-model-comparison", ); @@ -1030,6 +1031,7 @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = { { "query-hotpluggable-cpus", QEMU_CAPS_QUERY_HOTPLUGGABLE_CPUS }, { "query-qmp-schema", QEMU_CAPS_QUERY_QMP_SCHEMA }, { "query-cpu-model-expansion", QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION }, + { "query-cpu-model-comparison", QEMU_CAPS_QUERY_CPU_MODEL_COMPARISON }, { "query-cpu-definitions", QEMU_CAPS_QUERY_CPU_DEFINITIONS }, { "query-named-block-nodes", QEMU_CAPS_QUERY_NAMED_BLOCK_NODES }, }; @@ -4930,3 +4932,54 @@ virQEMUCapsSetMicrocodeVersion(virQEMUCapsPtr qemuCaps, { qemuCaps->microcodeVersion = microcodeVersion; } + + +static virQEMUCapsInitQMPCommandPtr +virQEMUCapsSetupBinary(char *binary) +{ + virQEMUCapsInitQMPCommandPtr cmd; + char *qmperr = NULL; + + if (!(cmd = virQEMUCapsInitQMPCommandNew(binary, "/tmp", -1, -1, &qmperr))) + goto cleanup; + + if (virQEMUCapsInitQMPCommandRun(cmd, false) != 0) + goto cleanup; + + if (qemuMonitorSetCapabilities(cmd->mon) < 0) { + VIR_DEBUG("Failed to set monitor capabilities %s", + virGetLastErrorMessage()); + goto cleanup; + } + + return cmd; + + cleanup: + virQEMUCapsInitQMPCommandFree(cmd); + return NULL; +} + + +qemuMonitorCPUModelInfoPtr +virQEMUCapsProbeQMPCPUModelComparison(char *binary, + virCPUDefPtr cpuA, + virCPUDefPtr cpuB) +{ + virQEMUCapsInitQMPCommandPtr cmd; + qemuMonitorCPUModelInfoPtr cpuC = NULL; + qemuMonitorCPUModelInfoPtr ret = NULL; + + if (!(cmd = virQEMUCapsSetupBinary(binary))) + goto cleanup; + + if (qemuMonitorGetCPUModelComparison(cmd->mon, cpuA, cpuB, &cpuC) < 0) + goto cleanup; + + ret = cpuC; + cpuC = NULL; + + cleanup: + virQEMUCapsInitQMPCommandFree(cmd); + qemuMonitorCPUModelInfoFree(cpuC); + return ret; +} diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index a959931..f27a359 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -452,6 +452,7 @@ typedef enum { QEMU_CAPS_DEVICE_VIRTIO_TABLET_CCW, /* -device virtio-tablet-ccw */ QEMU_CAPS_QCOW2_LUKS, /* qcow2 format support LUKS encryption */ QEMU_CAPS_DEVICE_PCIE_PCI_BRIDGE, /* -device pcie-pci-bridge */ + QEMU_CAPS_QUERY_CPU_MODEL_COMPARISON, /* qmp query-cpu-model-comparison */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; @@ -578,4 +579,9 @@ bool virQEMUCapsGuestIsNative(virArch host, bool virQEMUCapsCPUFilterFeatures(const char *name, void *opaque); +qemuMonitorCPUModelInfoPtr +virQEMUCapsProbeQMPCPUModelComparison(char *binary, + virCPUDefPtr cpuA, + virCPUDefPtr cpuB); + #endif /* __QEMU_CAPABILITIES_H__*/ diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 22f0522..1981b62 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3792,6 +3792,20 @@ qemuMonitorCPUModelInfoFree(qemuMonitorCPUModelInfoPtr model_info) } +int +qemuMonitorGetCPUModelComparison(qemuMonitorPtr mon, + virCPUDefPtr cpuA, + virCPUDefPtr cpuB, + qemuMonitorCPUModelInfoPtr *cpuC) +{ + VIR_DEBUG("cpuA=%p cpuB=%p", cpuA, cpuB); + + QEMU_CHECK_MONITOR_JSON(mon); + + return qemuMonitorJSONGetCPUModelComparison(mon, cpuA, cpuB, cpuC); +} + + qemuMonitorCPUModelInfoPtr qemuMonitorCPUModelInfoCopy(const qemuMonitorCPUModelInfo *orig) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 9556a51..cc30184 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1072,6 +1072,12 @@ int qemuMonitorGetCPUModelExpansion(qemuMonitorPtr mon, void qemuMonitorCPUModelInfoFree(qemuMonitorCPUModelInfoPtr model_info); +int +qemuMonitorGetCPUModelComparison(qemuMonitorPtr mon, + virCPUDefPtr modelA, + virCPUDefPtr modelB, + qemuMonitorCPUModelInfoPtr *cpuC); + qemuMonitorCPUModelInfoPtr qemuMonitorCPUModelInfoCopy(const qemuMonitorCPUModelInfo *orig); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 57c2c4d..c1759ec 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -5466,6 +5466,134 @@ qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon, } +static virJSONValuePtr +qemuMonitorJSONConvertCPUDefToJSON(virCPUDefPtr cpu) +{ + virJSONValuePtr value; + virJSONValuePtr feats = NULL; + size_t i; + + if (!(value = virJSONValueNewObject()) || + !(feats = virJSONValueNewObject())) + goto cleanup; + + if (virJSONValueObjectAppendString(value, "name", cpu->model) < 0) + goto cleanup; + + for (i = 0; i < cpu->nfeatures; i++) { + char *name = cpu->features[i].name; + bool enabled = false; + + if (cpu->type == VIR_CPU_TYPE_HOST || + cpu->features[i].policy == VIR_CPU_FEATURE_REQUIRE) + enabled = true; + + if (virJSONValueObjectAppendBoolean(feats, name, enabled) < 0) + goto cleanup; + } + + if (virJSONValueObjectAppend(value, "props", feats) < 0) + goto cleanup; + + return value; + + cleanup: + virJSONValueFree(value); + virJSONValueFree(feats); + return NULL; +} + + +static int +qemuMonitorJSONParseCPUModelPropName(size_t pos ATTRIBUTE_UNUSED, + virJSONValuePtr item, + void *opaque) +{ + return qemuMonitorJSONParseCPUModelProperty(virJSONValueGetString(item), + item, opaque); +} + + +int +qemuMonitorJSONGetCPUModelComparison(qemuMonitorPtr mon, + virCPUDefPtr modelA, + virCPUDefPtr modelB, + qemuMonitorCPUModelInfoPtr *cpuC) +{ + int ret = -1; + virJSONValuePtr modela; + virJSONValuePtr modelb = NULL; + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + virJSONValuePtr data; + const char *result; + virJSONValuePtr props; + qemuMonitorCPUModelInfoPtr modelc = NULL; + + if (!(modela = qemuMonitorJSONConvertCPUDefToJSON(modelA)) || + !(modelb = qemuMonitorJSONConvertCPUDefToJSON(modelB))) + goto cleanup; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-comparison", + "a:modela", &modela, + "a:modelb", &modelb, + NULL))) + goto cleanup; + + /* Clean up of cmd will free the below virJSONValuePtrs */ + modela = NULL; + modelb = NULL; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + goto cleanup; + + data = virJSONValueObjectGetObject(reply, "return"); + + if (!(result = virJSONValueObjectGetString(data, "result"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-cpu-model-expansion reply data was missing " + "'result'")); + goto cleanup; + } + + if (!(props = virJSONValueObjectGetArray(data, "responsible-properties"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-cpu-model-expansion reply data was missing " + "'responsible-properties'")); + goto cleanup; + } + + if (VIR_ALLOC(modelc) < 0) + goto cleanup; + + if (VIR_STRDUP(modelc->name, result) < 0) + goto cleanup; + + if (VIR_ALLOC_N(modelc->props, virJSONValueArraySize(props)) < 0) + goto cleanup; + + if (virJSONValueArrayForeachSteal(props, + qemuMonitorJSONParseCPUModelPropName, + modelc) < 0) + goto cleanup; + + ret = 0; + *cpuC = modelc; + modelc = NULL; + + cleanup: + qemuMonitorCPUModelInfoFree(modelc); + 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 045df49..ad9ae73 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -361,6 +361,12 @@ int qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon, qemuMonitorCPUModelInfoPtr *model_info) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(5); +int qemuMonitorJSONGetCPUModelComparison(qemuMonitorPtr mon, + virCPUDefPtr modelA, + virCPUDefPtr modelB, + qemuMonitorCPUModelInfoPtr *cpuC) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); + int qemuMonitorJSONGetCommands(qemuMonitorPtr mon, char ***commands) ATTRIBUTE_NONNULL(2); -- 2.7.4

The virsh cpu-compare command accepts an xml file that describes a cpu definition and compares it to a master xml file containing the host CPU. Not all architectures follow this procedure, and instead compare CPU's via QEMU. Let's hook up this capability to the QEMU driver and, if the capabilitiy is available, compare the host CPU with the CPU defined in the xml file. Signed-off-by: Collin Walling <walling@linux.ibm.com> --- src/qemu/qemu_capabilities.c | 2 +- src/qemu/qemu_capabilities.h | 3 ++ src/qemu/qemu_driver.c | 96 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index d385ad8..e4ce086 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -657,7 +657,7 @@ virQEMUCapsFindBinary(const char *format, return ret; } -static char * +char * virQEMUCapsFindBinaryForArch(virArch hostarch, virArch guestarch) { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index f27a359..01770f9 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -579,6 +579,9 @@ bool virQEMUCapsGuestIsNative(virArch host, bool virQEMUCapsCPUFilterFeatures(const char *name, void *opaque); +char *virQEMUCapsFindBinaryForArch(virArch hostarch, + virArch guestarch); + qemuMonitorCPUModelInfoPtr virQEMUCapsProbeQMPCPUModelComparison(char *binary, virCPUDefPtr cpuA, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 92f5fe6..c87792b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13100,6 +13100,85 @@ qemuNodeDeviceReset(virNodeDevicePtr dev) return ret; } + +/** + * qemuCompareCPU: + * @binary: QEMU binary to issue the comparison command to + * @host: host CPU definition + * @xml: XML description of either guest or host CPU to be compared with @host + * @failIncompatible: return an error instead of VIR_CPU_COMPARE_INCOMPATIBLE + * + * Compares the target CPU described by @xml with @host CPU to produce a third + * model containing the comparison result and the list of features responsible + * format the result. This function discards the responsible features. + * + * Compared model results: + * + * "incompatible": target cpu cannot run on @host. + * + * "subset": @host is an older cpu model than target, or @host does not + * support all features enabled on target. + * + * This result is considered incompatible. + * + * "identical": @host and target are identical; target can run on @host. + * + * "superset": @host is a newer cpu model than target, or @host supports some + * features not supported by target; target can run on @host. + * + * Returns: virCPUCompareResult based on the produced "compared" model's name, + * or VIR_CPU_COMPARE_ERROR upon error. + */ +static virCPUCompareResult +qemuCompareCPU(char *binary, + virCPUDefPtr hostCPU, + const char *xml, + bool failIncompatible) +{ + virCPUDefPtr targetCPU = NULL; + qemuMonitorCPUModelInfoPtr result = NULL; + virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR; + + VIR_DEBUG("binary=%s, hostCPU=%p, xml=%s", binary, hostCPU, NULLSTR(xml)); + + if (!hostCPU || !hostCPU->model) { + if (failIncompatible) { + virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", + _("cannot get host CPU capabilities")); + } else { + VIR_WARN("cannot get host CPU capabilities"); + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + } + goto cleanup; + } + + if (virCPUDefParseXMLHelper(xml, NULL, VIR_CPU_TYPE_AUTO, &targetCPU) < 0) + goto cleanup; + + if (!(result = virQEMUCapsProbeQMPCPUModelComparison(binary, hostCPU, + targetCPU))) + 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: + virCPUDefFree(targetCPU); + qemuMonitorCPUModelInfoFree(result); + return ret; +} + + static int qemuConnectCompareCPU(virConnectPtr conn, const char *xmlDesc, @@ -13109,6 +13188,8 @@ qemuConnectCompareCPU(virConnectPtr conn, int ret = VIR_CPU_COMPARE_ERROR; virCapsPtr caps = NULL; bool failIncompatible; + char *binary = NULL; + virQEMUCapsPtr qemuCaps = NULL; virCheckFlags(VIR_CONNECT_COMPARE_CPU_FAIL_INCOMPATIBLE, VIR_CPU_COMPARE_ERROR); @@ -13121,11 +13202,26 @@ qemuConnectCompareCPU(virConnectPtr conn, if (!(caps = virQEMUDriverGetCapabilities(driver, false))) goto cleanup; + binary = virQEMUCapsFindBinaryForArch(caps->host.arch, caps->host.arch); + + if (binary) { + qemuCaps = virQEMUCapsCacheLookup(driver->qemuCapsCache, binary); + + if (qemuCaps && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_COMPARISON)) { + ret = qemuCompareCPU(binary, caps->host.cpu, xmlDesc, + failIncompatible); + goto cleanup; + } + } + ret = virCPUCompareXML(caps->host.arch, caps->host.cpu, xmlDesc, failIncompatible); cleanup: + VIR_FREE(binary); virObjectUnref(caps); + virObjectUnref(qemuCaps); return ret; } -- 2.7.4

On Mon, Apr 16, 2018 at 19:16:05 -0400, Collin Walling wrote:
[Overview]
This patch series implements an interface to "query-cpu-model-comparison" (available QEMU ~2.8.0) via virsh cpu-compare.
[Using This Feature]
Run virsh cpu-compare (ensure you are running the virsh in your build dir) and pass it an xml file describing a cpu definition. You can copy the cpu xml from virsh capabilities (if you want to compare the host cpu to itself), or a cpu defined in any guest xml. Additionally, you can create a cpu xml as such (e.g. for s390x):
<cpu> <arch>s390x</arch> <model fallback='forbid'>model_name</model> <feature policy='require|disable' name='feature_name'/> </cpu>
NOTE: the presence of <arch> is optional and it will treat the cpu defined in the xml as a host cpu. This will disregard all feature policies (i.e. all features listed will behave with policy='require', even if disable is specified).
NOTE: as s390x only supports feature policies 'require' and 'disable', I am uncertain how to handle the other policies when parsing CPUDef to JSON.
[Example Output]
On an s390x system running a z13.2, this is the expected output (where each file describes a CPU model corresponding to the name of the file):
$ virsh cpu-compare zEC12.xml Host CPU is a superset of CPU described in zEC12.xml
$ virsh cpu-compare z13.2.xml CPU described in z13.2.xml is identical to host CPU
$ virsh cpu-compare z14.xml CPU described in z14.xml is incompatible with host CPU
$ virsh cpu-compare z14.xml --error error: Failed to compare host CPU with z14.xml error: the CPU is incompatible with host CPU
$ virsh cpu-compare z12345.xml error: Failed to compare host CPU with z12345cpu.xml error: internal error: unable to execute QEMU command 'query-cpu-model-comparison': The CPU definition 'z12345-base' is unknown.
[Patch Rundown]
The first patch copies the host CPU definition from qemuCaps to virCaps so we can easily access the host CPU model and features when we handle the CPU comparison in qemu_driver. Note that we take care to not clobber anything already stored in the host CPU definition until we have successfully constructed a new host CPU definition.
I think this is a wrong approach. You'd be basically giving random answers depending on which QEMU binary is probed first. The reason for storing the CPU model in qemuCaps is that it is tight to a particular QEMU binary rather than the host itself. The model reported by one binary may not be usable with other binaries and this applies to any comparisons or other operations done with this CPU model. In other words, we need to introduce a new set of CPU related APIs which will take more arguments so that the caller may specify what binary, virt type, and machine type they want to use. In other words, the APIs should support parameters similar to virConnectGetDomainCapabilities(). I'm currently starting to work on these new APIs. Jirka

On 04/17/2018 04:18 AM, Jiri Denemark wrote:
On Mon, Apr 16, 2018 at 19:16:05 -0400, Collin Walling wrote:
[Overview]
This patch series implements an interface to "query-cpu-model-comparison" (available QEMU ~2.8.0) via virsh cpu-compare.
[Using This Feature]
Run virsh cpu-compare (ensure you are running the virsh in your build dir) and pass it an xml file describing a cpu definition. You can copy the cpu xml from virsh capabilities (if you want to compare the host cpu to itself), or a cpu defined in any guest xml. Additionally, you can create a cpu xml as such (e.g. for s390x):
<cpu> <arch>s390x</arch> <model fallback='forbid'>model_name</model> <feature policy='require|disable' name='feature_name'/> </cpu>
NOTE: the presence of <arch> is optional and it will treat the cpu defined in the xml as a host cpu. This will disregard all feature policies (i.e. all features listed will behave with policy='require', even if disable is specified).
NOTE: as s390x only supports feature policies 'require' and 'disable', I am uncertain how to handle the other policies when parsing CPUDef to JSON.
[Example Output]
On an s390x system running a z13.2, this is the expected output (where each file describes a CPU model corresponding to the name of the file):
$ virsh cpu-compare zEC12.xml Host CPU is a superset of CPU described in zEC12.xml
$ virsh cpu-compare z13.2.xml CPU described in z13.2.xml is identical to host CPU
$ virsh cpu-compare z14.xml CPU described in z14.xml is incompatible with host CPU
$ virsh cpu-compare z14.xml --error error: Failed to compare host CPU with z14.xml error: the CPU is incompatible with host CPU
$ virsh cpu-compare z12345.xml error: Failed to compare host CPU with z12345cpu.xml error: internal error: unable to execute QEMU command 'query-cpu-model-comparison': The CPU definition 'z12345-base' is unknown.
[Patch Rundown]
The first patch copies the host CPU definition from qemuCaps to virCaps so we can easily access the host CPU model and features when we handle the CPU comparison in qemu_driver. Note that we take care to not clobber anything already stored in the host CPU definition until we have successfully constructed a new host CPU definition.
I think this is a wrong approach. You'd be basically giving random answers depending on which QEMU binary is probed first. The reason for storing the CPU model in qemuCaps is that it is tight to a particular QEMU binary rather than the host itself. The model reported by one binary may not be usable with other binaries and this applies to any comparisons or other operations done with this CPU model.
In other words, we need to introduce a new set of CPU related APIs which will take more arguments so that the caller may specify what binary, virt type, and machine type they want to use. In other words, the APIs should support parameters similar to virConnectGetDomainCapabilities().
I'm currently starting to work on these new APIs.
Jirka
I see your concern. I understand your points behind having multiple arguments to finely control which qemu we probe, but what do you think of the current code within "virQEMUCapsInitGuest"? If I understand it correctly, then it has a way of querying the "native qemu binary" capabilities (e.g. qemu-kvm). We could refactor this code to get these "kvmbinCaps" when we need it, and from that we can retrieve the host CPU model. We would not need to specify a binary for this, as we already have a list of "native binaries" that we can test. As for virt type, we can still specify this via "virQEMUCapsGetHostModel". I think that would suffice, at least enough for what this patch series needs. I could spin up a patch for this if you'd like and we can see if it makes sense? Just some of my thoughts, and thanks for your response.
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- Respectfully, - Collin Walling

On Tue, Apr 17, 2018 at 11:32:17 -0400, Collin Walling wrote:
On 04/17/2018 04:18 AM, Jiri Denemark wrote:
On Mon, Apr 16, 2018 at 19:16:05 -0400, Collin Walling wrote:
[Overview]
This patch series implements an interface to "query-cpu-model-comparison" (available QEMU ~2.8.0) via virsh cpu-compare.
[Using This Feature]
Run virsh cpu-compare (ensure you are running the virsh in your build dir) and pass it an xml file describing a cpu definition. You can copy the cpu xml from virsh capabilities (if you want to compare the host cpu to itself), or a cpu defined in any guest xml. Additionally, you can create a cpu xml as such (e.g. for s390x):
<cpu> <arch>s390x</arch> <model fallback='forbid'>model_name</model> <feature policy='require|disable' name='feature_name'/> </cpu>
NOTE: the presence of <arch> is optional and it will treat the cpu defined in the xml as a host cpu. This will disregard all feature policies (i.e. all features listed will behave with policy='require', even if disable is specified).
NOTE: as s390x only supports feature policies 'require' and 'disable', I am uncertain how to handle the other policies when parsing CPUDef to JSON.
[Example Output]
On an s390x system running a z13.2, this is the expected output (where each file describes a CPU model corresponding to the name of the file):
$ virsh cpu-compare zEC12.xml Host CPU is a superset of CPU described in zEC12.xml
$ virsh cpu-compare z13.2.xml CPU described in z13.2.xml is identical to host CPU
$ virsh cpu-compare z14.xml CPU described in z14.xml is incompatible with host CPU
$ virsh cpu-compare z14.xml --error error: Failed to compare host CPU with z14.xml error: the CPU is incompatible with host CPU
$ virsh cpu-compare z12345.xml error: Failed to compare host CPU with z12345cpu.xml error: internal error: unable to execute QEMU command 'query-cpu-model-comparison': The CPU definition 'z12345-base' is unknown.
[Patch Rundown]
The first patch copies the host CPU definition from qemuCaps to virCaps so we can easily access the host CPU model and features when we handle the CPU comparison in qemu_driver. Note that we take care to not clobber anything already stored in the host CPU definition until we have successfully constructed a new host CPU definition.
I think this is a wrong approach. You'd be basically giving random answers depending on which QEMU binary is probed first. The reason for storing the CPU model in qemuCaps is that it is tight to a particular QEMU binary rather than the host itself. The model reported by one binary may not be usable with other binaries and this applies to any comparisons or other operations done with this CPU model.
In other words, we need to introduce a new set of CPU related APIs which will take more arguments so that the caller may specify what binary, virt type, and machine type they want to use. In other words, the APIs should support parameters similar to virConnectGetDomainCapabilities().
I'm currently starting to work on these new APIs.
Jirka
I see your concern.
I understand your points behind having multiple arguments to finely control which qemu we probe, but what do you think of the current code within "virQEMUCapsInitGuest"? If I understand it correctly, then it has a way of querying the "native qemu binary" capabilities (e.g. qemu-kvm).
We could refactor this code to get these "kvmbinCaps" when we need it, and from that we can retrieve the host CPU model. We would not need to specify a binary for this, as we already have a list of "native binaries" that we can test. As for virt type, we can still specify this via "virQEMUCapsGetHostModel".
Why would we need to refactor anything? We definitely don't want to advertise any CPU model we get from QEMU in host capabilities and the API implementations in qemu_driver have access to the internal cache of QEMU capabilities. Any code which needs to know the host CPU model for a specific QEMU binary should get it from the cache via virQEMUCapsGetHostModel.
I think that would suffice, at least enough for what this patch series needs. I could spin up a patch for this if you'd like and we can see if it makes sense?
Since libvirt doesn't know how to detect and compare s390 CPU by itself, we can't really implement the existing CPU comparison API. See also Daniel's email for further explanation. Jirka

On 04/18/2018 02:46 AM, Jiri Denemark wrote:
On Tue, Apr 17, 2018 at 11:32:17 -0400, Collin Walling wrote:
On 04/17/2018 04:18 AM, Jiri Denemark wrote:
On Mon, Apr 16, 2018 at 19:16:05 -0400, Collin Walling wrote:
[Overview]
This patch series implements an interface to "query-cpu-model-comparison" (available QEMU ~2.8.0) via virsh cpu-compare.
[Using This Feature]
Run virsh cpu-compare (ensure you are running the virsh in your build dir) and pass it an xml file describing a cpu definition. You can copy the cpu xml from virsh capabilities (if you want to compare the host cpu to itself), or a cpu defined in any guest xml. Additionally, you can create a cpu xml as such (e.g. for s390x):
<cpu> <arch>s390x</arch> <model fallback='forbid'>model_name</model> <feature policy='require|disable' name='feature_name'/> </cpu>
NOTE: the presence of <arch> is optional and it will treat the cpu defined in the xml as a host cpu. This will disregard all feature policies (i.e. all features listed will behave with policy='require', even if disable is specified).
NOTE: as s390x only supports feature policies 'require' and 'disable', I am uncertain how to handle the other policies when parsing CPUDef to JSON.
[Example Output]
On an s390x system running a z13.2, this is the expected output (where each file describes a CPU model corresponding to the name of the file):
$ virsh cpu-compare zEC12.xml Host CPU is a superset of CPU described in zEC12.xml
$ virsh cpu-compare z13.2.xml CPU described in z13.2.xml is identical to host CPU
$ virsh cpu-compare z14.xml CPU described in z14.xml is incompatible with host CPU
$ virsh cpu-compare z14.xml --error error: Failed to compare host CPU with z14.xml error: the CPU is incompatible with host CPU
$ virsh cpu-compare z12345.xml error: Failed to compare host CPU with z12345cpu.xml error: internal error: unable to execute QEMU command 'query-cpu-model-comparison': The CPU definition 'z12345-base' is unknown.
[Patch Rundown]
The first patch copies the host CPU definition from qemuCaps to virCaps so we can easily access the host CPU model and features when we handle the CPU comparison in qemu_driver. Note that we take care to not clobber anything already stored in the host CPU definition until we have successfully constructed a new host CPU definition.
I think this is a wrong approach. You'd be basically giving random answers depending on which QEMU binary is probed first. The reason for storing the CPU model in qemuCaps is that it is tight to a particular QEMU binary rather than the host itself. The model reported by one binary may not be usable with other binaries and this applies to any comparisons or other operations done with this CPU model.
In other words, we need to introduce a new set of CPU related APIs which will take more arguments so that the caller may specify what binary, virt type, and machine type they want to use. In other words, the APIs should support parameters similar to virConnectGetDomainCapabilities().
I'm currently starting to work on these new APIs.
Jirka
I see your concern.
I understand your points behind having multiple arguments to finely control which qemu we probe, but what do you think of the current code within "virQEMUCapsInitGuest"? If I understand it correctly, then it has a way of querying the "native qemu binary" capabilities (e.g. qemu-kvm).
We could refactor this code to get these "kvmbinCaps" when we need it, and from that we can retrieve the host CPU model. We would not need to specify a binary for this, as we already have a list of "native binaries" that we can test. As for virt type, we can still specify this via "virQEMUCapsGetHostModel".
Why would we need to refactor anything? We definitely don't want to advertise any CPU model we get from QEMU in host capabilities and the API implementations in qemu_driver have access to the internal cache of QEMU capabilities. Any code which needs to know the host CPU model for a specific QEMU binary should get it from the cache via virQEMUCapsGetHostModel.
I think that would suffice, at least enough for what this patch series needs. I could spin up a patch for this if you'd like and we can see if it makes sense?
Since libvirt doesn't know how to detect and compare s390 CPU by itself, we can't really implement the existing CPU comparison API. See also Daniel's email for further explanation.
Jirka
Indeed. I wanted to reject my response after reading Daniel's explanation yesterday :) I'll keep my eye out for these patches that introduce this new API. With any luck, perhaps some of my proposed work may be salvaged to work with the new code. We'll see. Thanks for your time. -- Respectfully, - Collin Walling

On Tue, Apr 17, 2018 at 10:18:58AM +0200, Jiri Denemark wrote:
On Mon, Apr 16, 2018 at 19:16:05 -0400, Collin Walling wrote:
[Overview]
This patch series implements an interface to "query-cpu-model-comparison" (available QEMU ~2.8.0) via virsh cpu-compare.
[Using This Feature]
Run virsh cpu-compare (ensure you are running the virsh in your build dir) and pass it an xml file describing a cpu definition. You can copy the cpu xml from virsh capabilities (if you want to compare the host cpu to itself), or a cpu defined in any guest xml. Additionally, you can create a cpu xml as such (e.g. for s390x):
<cpu> <arch>s390x</arch> <model fallback='forbid'>model_name</model> <feature policy='require|disable' name='feature_name'/> </cpu>
NOTE: the presence of <arch> is optional and it will treat the cpu defined in the xml as a host cpu. This will disregard all feature policies (i.e. all features listed will behave with policy='require', even if disable is specified).
NOTE: as s390x only supports feature policies 'require' and 'disable', I am uncertain how to handle the other policies when parsing CPUDef to JSON.
[Example Output]
On an s390x system running a z13.2, this is the expected output (where each file describes a CPU model corresponding to the name of the file):
$ virsh cpu-compare zEC12.xml Host CPU is a superset of CPU described in zEC12.xml
$ virsh cpu-compare z13.2.xml CPU described in z13.2.xml is identical to host CPU
$ virsh cpu-compare z14.xml CPU described in z14.xml is incompatible with host CPU
$ virsh cpu-compare z14.xml --error error: Failed to compare host CPU with z14.xml error: the CPU is incompatible with host CPU
$ virsh cpu-compare z12345.xml error: Failed to compare host CPU with z12345cpu.xml error: internal error: unable to execute QEMU command 'query-cpu-model-comparison': The CPU definition 'z12345-base' is unknown.
[Patch Rundown]
The first patch copies the host CPU definition from qemuCaps to virCaps so we can easily access the host CPU model and features when we handle the CPU comparison in qemu_driver. Note that we take care to not clobber anything already stored in the host CPU definition until we have successfully constructed a new host CPU definition.
I think this is a wrong approach. You'd be basically giving random answers depending on which QEMU binary is probed first. The reason for storing the CPU model in qemuCaps is that it is tight to a particular QEMU binary rather than the host itself. The model reported by one binary may not be usable with other binaries and this applies to any comparisons or other operations done with this CPU model.
In other words, we need to introduce a new set of CPU related APIs which will take more arguments so that the caller may specify what binary, virt type, and machine type they want to use. In other words, the APIs should support parameters similar to virConnectGetDomainCapabilities().
FYI, in addition to all this, the current 'virsh cpu-compare' command is in fact 100% accurate in what it is reporting today and we can not change it. The 'virsh cpu-compare' command is answering the question "Is this CPU model compatible with the host CPU model" The problem is that when doing live migration, we don't care about this question. We were asking the wrong question. We instead need to ask "Is this CPU model compatible with the hypervisor CPU model" We can't simply change the semantics of the existing API to answer a different question, because the original question is still relevant and useful for some usage scenarios. So yes, we must have a new API that lets us ask the right question for live migration compat. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
participants (3)
-
Collin Walling
-
Daniel P. Berrangé
-
Jiri Denemark