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(a)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