[libvirt] [PATCH 00/12] qemu: Enforce guest CPU specification

When starting a domain with custom guest CPU specification QEMU may add or remove some CPU features. There are several reasons for this, e.g., QEMU/KVM does not support some requested features or the definition of the requested CPU model in libvirt's cpu_map.xml differs from the one QEMU is using. We can't really avoid this because CPU models are allowed to change with machine types and libvirt doesn't know (and probably doesn't even want to know) about such changes. Thus when we want to make sure guest ABI doesn't change when a domain gets migrated to another host, we need to update our live CPU definition according to the CPU QEMU created. Once updated, we will change CPU checking to VIR_CPU_CHECK_FULL to make sure the virtual CPU created after migration exactly matches the one on the source. https://bugzilla.redhat.com/show_bug.cgi?id=822148 https://bugzilla.redhat.com/show_bug.cgi?id=824989 Jiri Denemark (12): tests: Switch to sparse initialization of virCPUDef docs: Clarify /domain/cpu/@match description Introduce /domain/cpu/@check XML attribute qemu: Set default values for CPU check attribute qemu: Refactor Hyper-V features check qemu: Refactor KVM features check qemu: Refactor CPU features check qemu: Refactor qemuProcessVerifyGuestCPU qemu: Use ARCH_IS_X86 in qemuMonitorJSONGetGuestCPU qemu: Ask QEMU for filtered CPU features qemu: Update CPU definition according to QEMU qemu: Enforce guest CPU specification docs/formatdomain.html.in | 52 ++++- docs/schemas/cputypes.rng | 10 + docs/schemas/domaincommon.rng | 3 + src/conf/cpu_conf.c | 30 +++ src/conf/cpu_conf.h | 12 ++ src/conf/domain_conf.c | 21 ++ src/cpu/cpu.c | 45 +++++ src/cpu/cpu.h | 12 ++ src/cpu/cpu_x86.c | 105 ++++++++++ src/libvirt_private.syms | 1 + src/qemu/qemu_domain.c | 42 ++++ src/qemu/qemu_monitor.c | 11 +- src/qemu/qemu_monitor.h | 3 +- src/qemu/qemu_monitor_json.c | 38 +++- src/qemu/qemu_monitor_json.h | 3 +- src/qemu/qemu_process.c | 225 +++++++++++++-------- tests/domaincapstest.c | 38 ++-- tests/qemumonitorjsontest.c | 4 +- .../qemuxml2argv-aarch64-gic-host.xml | 2 +- .../qemuxml2argv-aarch64-gic-v2.xml | 2 +- .../qemuxml2argv-aarch64-gic-v3.xml | 2 +- .../qemuxml2argv-cpu-check-default-none.args | 21 ++ .../qemuxml2argv-cpu-check-default-none.xml | 19 ++ .../qemuxml2argv-cpu-check-default-none2.args | 21 ++ .../qemuxml2argv-cpu-check-default-none2.xml | 21 ++ .../qemuxml2argv-cpu-check-default-partial.args | 22 ++ .../qemuxml2argv-cpu-check-default-partial.xml | 19 ++ .../qemuxml2argv-cpu-check-default-partial2.args | 21 ++ .../qemuxml2argv-cpu-check-default-partial2.xml | 21 ++ .../qemuxml2argv-cpu-check-full.args | 1 + .../qemuxml2argv-cpu-check-full.xml | 21 ++ .../qemuxml2argv-cpu-check-none.args | 21 ++ .../qemuxml2argv-cpu-check-none.xml | 21 ++ .../qemuxml2argv-cpu-check-partial.args | 1 + .../qemuxml2argv-cpu-check-partial.xml | 21 ++ tests/qemuxml2argvtest.c | 8 + .../qemuxml2xmlout-aarch64-aavmf-virtio-mmio.xml | 2 +- ...qemuxml2xmlout-aarch64-video-virtio-gpu-pci.xml | 2 +- .../qemuxml2xmlout-aarch64-virtio-pci-default.xml | 2 +- ...2xmlout-aarch64-virtio-pci-manual-addresses.xml | 2 +- .../qemuxml2xmlout-cpu-check-default-none.xml | 28 +++ .../qemuxml2xmlout-cpu-check-default-none2.xml | 30 +++ .../qemuxml2xmlout-cpu-check-default-partial.xml | 30 +++ .../qemuxml2xmlout-cpu-check-default-partial2.xml | 30 +++ .../qemuxml2xmlout-cpu-check-full.xml | 30 +++ .../qemuxml2xmlout-cpu-check-none.xml | 30 +++ .../qemuxml2xmlout-cpu-check-partial.xml | 30 +++ .../qemuxml2xmlout-cpu-eoi-disabled.xml | 2 +- .../qemuxml2xmlout-cpu-eoi-enabled.xml | 2 +- .../qemuxml2xmlout-cpu-host-kvmclock.xml | 2 +- .../qemuxml2xmlout-cpu-host-model-features.xml | 2 +- ...emuxml2xmlout-cpu-host-passthrough-features.xml | 2 +- .../qemuxml2xmlout-cpu-kvmclock.xml | 2 +- .../qemuxml2xmlout-graphics-spice-timeout.xml | 2 +- tests/qemuxml2xmltest.c | 8 + tests/testutilsqemu.c | 48 ++--- 56 files changed, 1039 insertions(+), 167 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.xml create mode 120000 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.xml create mode 120000 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none2.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial2.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-full.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-none.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-partial.xml -- 2.12.0

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- tests/domaincapstest.c | 38 ++++++++++++++++++++++++-------------- tests/testutilsqemu.c | 48 ++++++++++++++++++++---------------------------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c index a4bc8d6d0..462f86801 100644 --- a/tests/domaincapstest.c +++ b/tests/domaincapstest.c @@ -66,10 +66,10 @@ fillAllCaps(virDomainCapsPtr domCaps) virDomainCapsDeviceVideoPtr video = &domCaps->video; virDomainCapsDeviceHostdevPtr hostdev = &domCaps->hostdev; virCPUDef host = { - VIR_CPU_TYPE_HOST, 0, 0, - VIR_ARCH_X86_64, (char *) "host", - NULL, 0, (char *) "CPU Vendorrr", - 0, 0, 0, 0, 0, NULL, + .type = VIR_CPU_TYPE_HOST, + .arch = VIR_ARCH_X86_64, + .model = (char *) "host", + .vendor = (char *) "CPU Vendorrr", }; domCaps->maxvcpus = 255; @@ -119,25 +119,35 @@ fillAllCaps(virDomainCapsPtr domCaps) # include "testutilsqemu.h" static virCPUDef aarch64Cpu = { - 0, 0, 0, 0, NULL, NULL, 0, NULL, 1, 1, 1, 0, 0, NULL, + .sockets = 1, + .cores = 1, + .threads = 1, }; static virCPUDef ppc64leCpu = { - VIR_CPU_TYPE_HOST, 0, 0, - VIR_ARCH_PPC64LE, (char *) "POWER8", - NULL, 0, NULL, 1, 1, 1, 0, 0, NULL, + .type = VIR_CPU_TYPE_HOST, + .arch = VIR_ARCH_PPC64LE, + .model = (char *) "POWER8", + .sockets = 1, + .cores = 1, + .threads = 1, }; static virCPUDef x86Cpu = { - VIR_CPU_TYPE_HOST, 0, 0, - VIR_ARCH_X86_64, (char *) "Broadwell", - NULL, 0, NULL, 1, 1, 1, 0, 0, NULL, + .type = VIR_CPU_TYPE_HOST, + .arch = VIR_ARCH_X86_64, + .model = (char *) "Broadwell", + .sockets = 1, + .cores = 1, + .threads = 1, }; static virCPUDef s390Cpu = { - VIR_CPU_TYPE_HOST, 0, 0, - VIR_ARCH_S390X, NULL, - NULL, 0, NULL, 1, 1, 1, 0, 0, NULL, + .type = VIR_CPU_TYPE_HOST, + .arch = VIR_ARCH_S390X, + .sockets = 1, + .cores = 1, + .threads = 1, }; static int diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 0726cd317..026b73e9b 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -34,20 +34,16 @@ static virCPUFeatureDef cpuDefaultFeatures[] = { { (char *) "lahf_lm", -1 }, }; static virCPUDef cpuDefaultData = { - VIR_CPU_TYPE_HOST, /* type */ - 0, /* mode */ - 0, /* match */ - VIR_ARCH_X86_64, /* arch */ - (char *) "core2duo", /* model */ - NULL, /* vendor_id */ - 0, /* fallback */ - (char *) "Intel", /* vendor */ - 1, /* sockets */ - 2, /* cores */ - 1, /* threads */ - ARRAY_CARDINALITY(cpuDefaultFeatures), /* nfeatures */ - ARRAY_CARDINALITY(cpuDefaultFeatures), /* nfeatures_max */ - cpuDefaultFeatures, /* features */ + .type = VIR_CPU_TYPE_HOST, + .arch = VIR_ARCH_X86_64, + .model = (char *) "core2duo", + .vendor = (char *) "Intel", + .sockets = 1, + .cores = 2, + .threads = 1, + .nfeatures = ARRAY_CARDINALITY(cpuDefaultFeatures), + .nfeatures_max = ARRAY_CARDINALITY(cpuDefaultFeatures), + .features = cpuDefaultFeatures, }; static virCPUFeatureDef cpuHaswellFeatures[] = { @@ -77,20 +73,16 @@ static virCPUFeatureDef cpuHaswellFeatures[] = { { (char *) "lahf_lm", -1 }, }; static virCPUDef cpuHaswellData = { - VIR_CPU_TYPE_HOST, /* type */ - 0, /* mode */ - 0, /* match */ - VIR_ARCH_X86_64, /* arch */ - (char *) "Haswell", /* model */ - NULL, /* vendor_id */ - 0, /* fallback */ - (char *) "Intel", /* vendor */ - 1, /* sockets */ - 2, /* cores */ - 2, /* threads */ - ARRAY_CARDINALITY(cpuHaswellFeatures), /* nfeatures */ - ARRAY_CARDINALITY(cpuHaswellFeatures), /* nfeatures_max */ - cpuHaswellFeatures, /* features */ + .type = VIR_CPU_TYPE_HOST, + .arch = VIR_ARCH_X86_64, + .model = (char *) "Haswell", + .vendor = (char *) "Intel", + .sockets = 1, + .cores = 2, + .threads = 2, + .nfeatures = ARRAY_CARDINALITY(cpuHaswellFeatures), + .nfeatures_max = ARRAY_CARDINALITY(cpuHaswellFeatures), + .features = cpuHaswellFeatures, }; static virCPUDef cpuPower8Data = { -- 2.12.0

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- docs/formatdomain.html.in | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 9a204f845..75367d6dd 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1217,8 +1217,8 @@ <dl> <dt><code>cpu</code></dt> <dd>The <code>cpu</code> element is the main container for describing - guest CPU requirements. Its <code>match</code> attribute specified how - strictly has the virtual CPU provided to the guest match these + guest CPU requirements. Its <code>match</code> attribute specifies how + strictly the virtual CPU provided to the guest matches these requirements. <span class="since">Since 0.7.6</span> the <code>match</code> attribute can be omitted if <code>topology</code> is the only element within <code>cpu</code>. Possible values for the @@ -1227,13 +1227,21 @@ <dl> <dt><code>minimum</code></dt> <dd>The specified CPU model and features describes the minimum - requested CPU.</dd> + requested CPU. A better CPU will be provided to the guest if it + is possible with the requested hypervisor on the current host. + This is a constrained <code>host-model</code> mode; the domain + will not be created if the provided virtual CPU does not meet + the requirements.</dd> + <dt><code>exact</code></dt> - <dd>The virtual CPU provided to the guest will exactly match the - specification</dd> + <dd>The virtual CPU provided to the guest should exactly match the + specification. If such CPU is not supported, libvirt will refuse + to start the domain.</dd> + <dt><code>strict</code></dt> - <dd>The guest will not be created unless the host CPU does exactly - match the specification.</dd> + <dd>The domain will not be created unless the host CPU exactly + matches the specification. This is not very useful in practice + and should only be used if there is a real reason.</dd> </dl> <span class="since">Since 0.8.5</span> the <code>match</code> -- 2.12.0

The attribute can be used to request a specific way of checking whether the virtual CPU matches created by the hypervisor matches the specification in domain XML. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- docs/formatdomain.html.in | 30 ++++++++++++++++++++++++++++++ docs/schemas/cputypes.rng | 10 ++++++++++ docs/schemas/domaincommon.rng | 3 +++ src/conf/cpu_conf.c | 30 ++++++++++++++++++++++++++++++ src/conf/cpu_conf.h | 12 ++++++++++++ src/conf/domain_conf.c | 21 +++++++++++++++++++++ 6 files changed, 106 insertions(+) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 75367d6dd..3bea3c75a 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1247,6 +1247,36 @@ <span class="since">Since 0.8.5</span> the <code>match</code> attribute can be omitted and will default to <code>exact</code>. + Sometimes the hypervisor is not able to create a virtual CPU exactly + matching the specification passed by libvirt. + <span class="since">Since 3.2.0</span>, an optional <code>check</code> + attribute can be used to request a specific way of checking whether + the virtual CPU matches the specification. It is usually safe to omit + this attribute when starting a domain and stick with the default + value. Once the domain starts, libvirt will automatically change the + <code>check</code> attribute to the best supported value to ensure the + virtual CPU does not change when the domain is migrated to another + host. The following values can be used: + + <dl> + <dt><code>none</code></dt> + <dd>Libvirt does no checking and it is up to the hypervisor to + refuse to start the domain if it cannot provide the requested CPU. + With QEMU this means no checking is done at all since the default + behavior of QEMU is to emit warnings, but start the domain anyway. + </dd> + + <dt><code>partial</code></dt> + <dd>Libvirt will check the guest CPU specification before starting + a domain, but the rest is left on the hypervisor. It can still + provide a different virtual CPU.</dd> + + <dt><code>full</code></dt> + <dd>The virtual CPU created by the hypervisor will be checked + against the CPU specification and the domain will not be started + unless the two CPUs match.</dd> + </dl> + <span class="since">Since 0.9.10</span>, an optional <code>mode</code> attribute may be used to make it easier to configure a guest CPU to be as close to host CPU as possible. Possible values for the diff --git a/docs/schemas/cputypes.rng b/docs/schemas/cputypes.rng index 7cc9dd3d8..8189114e3 100644 --- a/docs/schemas/cputypes.rng +++ b/docs/schemas/cputypes.rng @@ -23,6 +23,16 @@ </attribute> </define> + <define name="cpuCheck"> + <attribute name="check"> + <choice> + <value>none</value> + <value>partial</value> + <value>full</value> + </choice> + </attribute> + </define> + <define name="cpuModel"> <element name="model"> <optional> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 5e593285e..767d6979c 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4503,6 +4503,9 @@ <optional> <ref name="cpuMatch"/> </optional> + <optional> + <ref name="cpuCheck"/> + </optional> <interleave> <optional> <ref name="cpuModel"/> diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 2724fa30a..90accaea7 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -45,6 +45,12 @@ VIR_ENUM_IMPL(virCPUMatch, VIR_CPU_MATCH_LAST, "exact", "strict") +VIR_ENUM_IMPL(virCPUCheck, VIR_CPU_CHECK_LAST, + "default", + "none", + "partial", + "full") + VIR_ENUM_IMPL(virCPUFallback, VIR_CPU_FALLBACK_LAST, "allow", "forbid") @@ -182,6 +188,7 @@ virCPUDefCopyWithoutModel(const virCPUDef *cpu) copy->type = cpu->type; copy->mode = cpu->mode; copy->match = cpu->match; + copy->check = cpu->check; copy->fallback = cpu->fallback; copy->sockets = cpu->sockets; copy->cores = cpu->cores; @@ -277,6 +284,7 @@ virCPUDefParseXML(xmlNodePtr node, if (def->type == VIR_CPU_TYPE_GUEST) { char *match = virXMLPropString(node, "match"); + char *check; if (!match) { if (virXPathBoolean("boolean(./model)", ctxt)) @@ -294,6 +302,18 @@ virCPUDefParseXML(xmlNodePtr node, goto error; } } + + if ((check = virXMLPropString(node, "check"))) { + def->check = virCPUCheckTypeFromString(check); + VIR_FREE(check); + + if (def->check < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Invalid check attribute for CPU " + "specification")); + goto error; + } + } } if (def->type == VIR_CPU_TYPE_HOST) { @@ -532,6 +552,16 @@ virCPUDefFormatBufFull(virBufferPtr buf, } virBufferAsprintf(&attributeBuf, " match='%s'", tmp); } + + if (def->check) { + if (!(tmp = virCPUCheckTypeToString(def->check))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected CPU check policy %d"), + def->check); + goto cleanup; + } + virBufferAsprintf(&attributeBuf, " check='%s'", tmp); + } } /* Format children */ diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index cc3fbf0a4..507f630dc 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -64,6 +64,17 @@ typedef enum { VIR_ENUM_DECL(virCPUMatch) typedef enum { + VIR_CPU_CHECK_DEFAULT, + VIR_CPU_CHECK_NONE, + VIR_CPU_CHECK_PARTIAL, + VIR_CPU_CHECK_FULL, + + VIR_CPU_CHECK_LAST +} virCPUCheck; + +VIR_ENUM_DECL(virCPUCheck) + +typedef enum { VIR_CPU_FALLBACK_ALLOW, VIR_CPU_FALLBACK_FORBID, @@ -98,6 +109,7 @@ struct _virCPUDef { int type; /* enum virCPUType */ int mode; /* enum virCPUMode */ int match; /* enum virCPUMatch */ + int check; /* virCPUCheck */ virArch arch; char *model; char *vendor_id; /* vendor id returned by CPUID in the guest */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 88d419e27..a2cdb260a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4587,6 +4587,24 @@ virDomainVcpuDefPostParse(virDomainDefPtr def) static int +virDomainDefPostParseCPU(virDomainDefPtr def) +{ + if (!def->cpu) + return 0; + + if (def->cpu->mode == VIR_CPU_MODE_CUSTOM && + !def->cpu->model && + def->cpu->check != VIR_CPU_CHECK_DEFAULT) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("check attribute specified for CPU with no model")); + return -1; + } + + return 0; +} + + +static int virDomainDefPostParseInternal(virDomainDefPtr def, struct virDomainDefPostParseDeviceIteratorData *data) { @@ -4636,6 +4654,9 @@ virDomainDefPostParseInternal(virDomainDefPtr def, virDomainDefPostParseGraphics(def); + if (virDomainDefPostParseCPU(def) < 0) + return -1; + return 0; } -- 2.12.0

On Tue, Mar 14, 2017 at 05:57:42PM +0100, Jiri Denemark wrote:
The attribute can be used to request a specific way of checking whether the virtual CPU matches created by the hypervisor matches the specification in domain XML.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- docs/formatdomain.html.in | 30 ++++++++++++++++++++++++++++++ docs/schemas/cputypes.rng | 10 ++++++++++ docs/schemas/domaincommon.rng | 3 +++ src/conf/cpu_conf.c | 30 ++++++++++++++++++++++++++++++ src/conf/cpu_conf.h | 12 ++++++++++++ src/conf/domain_conf.c | 21 +++++++++++++++++++++ 6 files changed, 106 insertions(+)
diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index cc3fbf0a4..507f630dc 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -98,6 +109,7 @@ struct _virCPUDef { int type; /* enum virCPUType */ int mode; /* enum virCPUMode */ int match; /* enum virCPUMatch */ + int check; /* virCPUCheck */
It would be nicer to declare this as virCPUCheck check...
virArch arch; char *model; char *vendor_id; /* vendor id returned by CPUID in the guest */ diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 2724fa30a..90accaea7 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -294,6 +302,18 @@ virCPUDefParseXML(xmlNodePtr node, goto error; } } + + if ((check = virXMLPropString(node, "check"))) { + def->check = virCPUCheckTypeFromString(check); + VIR_FREE(check); + + if (def->check < 0) {
... which would require a temporary int variable for this comparison.
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Invalid check attribute for CPU " + "specification")); + goto error; + } + } }
if (def->type == VIR_CPU_TYPE_HOST) { @@ -532,6 +552,16 @@ virCPUDefFormatBufFull(virBufferPtr buf, } virBufferAsprintf(&attributeBuf, " match='%s'", tmp); } + + if (def->check) { + if (!(tmp = virCPUCheckTypeToString(def->check))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected CPU check policy %d"),
We validated the value in the parser. I don't think we need to waste translators' time with translatable strings in dead code. Jan
+ def->check); + goto cleanup; + }

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_domain.c | 42 ++++++++++++++++++++++ src/qemu/qemu_process.c | 13 +++---- .../qemuxml2argv-aarch64-gic-host.xml | 2 +- .../qemuxml2argv-aarch64-gic-v2.xml | 2 +- .../qemuxml2argv-aarch64-gic-v3.xml | 2 +- .../qemuxml2argv-cpu-check-default-none.args | 21 +++++++++++ .../qemuxml2argv-cpu-check-default-none.xml | 19 ++++++++++ .../qemuxml2argv-cpu-check-default-none2.args | 21 +++++++++++ .../qemuxml2argv-cpu-check-default-none2.xml | 21 +++++++++++ .../qemuxml2argv-cpu-check-default-partial.args | 22 ++++++++++++ .../qemuxml2argv-cpu-check-default-partial.xml | 19 ++++++++++ .../qemuxml2argv-cpu-check-default-partial2.args | 21 +++++++++++ .../qemuxml2argv-cpu-check-default-partial2.xml | 21 +++++++++++ .../qemuxml2argv-cpu-check-full.args | 1 + .../qemuxml2argv-cpu-check-full.xml | 21 +++++++++++ .../qemuxml2argv-cpu-check-none.args | 21 +++++++++++ .../qemuxml2argv-cpu-check-none.xml | 21 +++++++++++ .../qemuxml2argv-cpu-check-partial.args | 1 + .../qemuxml2argv-cpu-check-partial.xml | 21 +++++++++++ tests/qemuxml2argvtest.c | 8 +++++ .../qemuxml2xmlout-aarch64-aavmf-virtio-mmio.xml | 2 +- ...qemuxml2xmlout-aarch64-video-virtio-gpu-pci.xml | 2 +- .../qemuxml2xmlout-aarch64-virtio-pci-default.xml | 2 +- ...2xmlout-aarch64-virtio-pci-manual-addresses.xml | 2 +- .../qemuxml2xmlout-cpu-check-default-none.xml | 28 +++++++++++++++ .../qemuxml2xmlout-cpu-check-default-none2.xml | 30 ++++++++++++++++ .../qemuxml2xmlout-cpu-check-default-partial.xml | 30 ++++++++++++++++ .../qemuxml2xmlout-cpu-check-default-partial2.xml | 30 ++++++++++++++++ .../qemuxml2xmlout-cpu-check-full.xml | 30 ++++++++++++++++ .../qemuxml2xmlout-cpu-check-none.xml | 30 ++++++++++++++++ .../qemuxml2xmlout-cpu-check-partial.xml | 30 ++++++++++++++++ .../qemuxml2xmlout-cpu-eoi-disabled.xml | 2 +- .../qemuxml2xmlout-cpu-eoi-enabled.xml | 2 +- .../qemuxml2xmlout-cpu-host-kvmclock.xml | 2 +- .../qemuxml2xmlout-cpu-host-model-features.xml | 2 +- ...emuxml2xmlout-cpu-host-passthrough-features.xml | 2 +- .../qemuxml2xmlout-cpu-kvmclock.xml | 2 +- .../qemuxml2xmlout-graphics-spice-timeout.xml | 2 +- tests/qemuxml2xmltest.c | 8 +++++ 39 files changed, 536 insertions(+), 22 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.xml create mode 120000 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.xml create mode 120000 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none2.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial2.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-full.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-none.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-partial.xml diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 07ce22417..8ebc92479 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2675,6 +2675,45 @@ qemuDomainDefVcpusPostParse(virDomainDefPtr def) static int +qemuDomainDefCPUPostParse(virDomainDefPtr def) +{ + if (!def->cpu) + return 0; + + /* Nothing to be done if only CPU topology is specified. */ + if (def->cpu->mode == VIR_CPU_MODE_CUSTOM && + !def->cpu->model) + return 0; + + if (def->cpu->check != VIR_CPU_CHECK_DEFAULT) + return 0; + + switch ((virCPUMode) def->cpu->mode) { + case VIR_CPU_MODE_HOST_PASSTHROUGH: + def->cpu->check = VIR_CPU_CHECK_NONE; + break; + + case VIR_CPU_MODE_HOST_MODEL: + def->cpu->check = VIR_CPU_CHECK_PARTIAL; + break; + + case VIR_CPU_MODE_CUSTOM: + /* Custom CPUs in TCG mode are not compared to host CPU by default. */ + if (def->virtType == VIR_DOMAIN_VIRT_QEMU) + def->cpu->check = VIR_CPU_CHECK_NONE; + else + def->cpu->check = VIR_CPU_CHECK_PARTIAL; + break; + + case VIR_CPU_MODE_LAST: + break; + } + + return 0; +} + + +static int qemuDomainDefPostParse(virDomainDefPtr def, virCapsPtr caps, unsigned int parseFlags, @@ -2738,6 +2777,9 @@ qemuDomainDefPostParse(virDomainDefPtr def, if (qemuDomainDefVcpusPostParse(def) < 0) goto cleanup; + if (qemuDomainDefCPUPostParse(def) < 0) + goto cleanup; + ret = 0; cleanup: virObjectUnref(qemuCaps); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index b9c1847bb..e9631c111 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -5184,14 +5184,11 @@ qemuProcessUpdateGuestCPU(virDomainDefPtr def, if (def->cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) return 0; - /* custom CPUs in TCG mode don't need to be compared to host CPU */ - if (def->virtType != VIR_DOMAIN_VIRT_QEMU || - def->cpu->mode != VIR_CPU_MODE_CUSTOM) { - if (virCPUCompare(caps->host.arch, - virQEMUCapsGetHostModel(qemuCaps, def->virtType), - def->cpu, true) < 0) - return -1; - } + if (def->cpu->check == VIR_CPU_CHECK_PARTIAL && + virCPUCompare(caps->host.arch, + virQEMUCapsGetHostModel(qemuCaps, def->virtType), + def->cpu, true) < 0) + return -1; if (virCPUUpdate(def->os.arch, def->cpu, virQEMUCapsGetHostModel(qemuCaps, def->virtType)) < 0) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-host.xml b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-host.xml index 445b35857..b14d14281 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-host.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-host.xml @@ -11,7 +11,7 @@ <features> <gic version='host'/> </features> - <cpu mode='host-passthrough'/> + <cpu mode='host-passthrough' check='none'/> <clock offset='utc'/> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-v2.xml b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-v2.xml index 9ccba9904..8b9983752 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-v2.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-v2.xml @@ -11,7 +11,7 @@ <features> <gic version='2'/> </features> - <cpu mode='host-passthrough'/> + <cpu mode='host-passthrough' check='none'/> <clock offset='utc'/> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-v3.xml b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-v3.xml index 7c9ee92b3..bde94e16c 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-v3.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-gic-v3.xml @@ -11,7 +11,7 @@ <features> <gic version='3'/> </features> - <cpu mode='host-passthrough'/> + <cpu mode='host-passthrough' check='none'/> <clock offset='utc'/> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.args new file mode 100644 index 000000000..24d9f5306 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.args @@ -0,0 +1,21 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-kvm \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-cpu host \ +-m 214 \ +-smp 6,sockets=6,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait \ +-no-acpi \ +-boot n \ +-usb \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.xml new file mode 100644 index 000000000..314cdf52a --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none.xml @@ -0,0 +1,19 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='host-passthrough'/> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.args new file mode 100644 index 000000000..b6a5d4dd4 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.args @@ -0,0 +1,21 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-cpu core2duo \ +-m 214 \ +-smp 6,sockets=6,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait \ +-no-acpi \ +-boot n \ +-usb \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.xml new file mode 100644 index 000000000..286d2d5d7 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-none2.xml @@ -0,0 +1,21 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.args new file mode 100644 index 000000000..decf7fdd4 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.args @@ -0,0 +1,22 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-kvm \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-cpu core2duo,+ds,+acpi,+ss,+ht,+tm,+pbe,+ds_cpl,+vmx,+est,+tm2,+cx16,+xtpr,\ ++lahf_lm \ +-m 214 \ +-smp 6,sockets=6,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait \ +-no-acpi \ +-boot n \ +-usb \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.xml new file mode 100644 index 000000000..3dd45a704 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial.xml @@ -0,0 +1,19 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='host-model'/> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.args new file mode 100644 index 000000000..bd4cefaeb --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.args @@ -0,0 +1,21 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-kvm \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-cpu core2duo \ +-m 214 \ +-smp 6,sockets=6,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait \ +-no-acpi \ +-boot n \ +-usb \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.xml new file mode 100644 index 000000000..6c7690a95 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-default-partial2.xml @@ -0,0 +1,21 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.args new file mode 120000 index 000000000..dd946106e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.args @@ -0,0 +1 @@ +qemuxml2argv-cpu-check-none.args \ No newline at end of file diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.xml new file mode 100644 index 000000000..653c2aaad --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-full.xml @@ -0,0 +1,21 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact' check='full'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.args new file mode 100644 index 000000000..bd4cefaeb --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.args @@ -0,0 +1,21 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-kvm \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-cpu core2duo \ +-m 214 \ +-smp 6,sockets=6,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait \ +-no-acpi \ +-boot n \ +-usb \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.xml new file mode 100644 index 000000000..632074626 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-none.xml @@ -0,0 +1,21 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.args new file mode 120000 index 000000000..dd946106e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.args @@ -0,0 +1 @@ +qemuxml2argv-cpu-check-none.args \ No newline at end of file diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.xml new file mode 100644 index 000000000..8e7850df8 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-check-partial.xml @@ -0,0 +1,21 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact' check='partial'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 6bd746599..db21edbbf 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -2468,6 +2468,14 @@ mymain(void) DO_TEST("fd-memory-no-numa-topology", QEMU_CAPS_MEM_PATH, QEMU_CAPS_OBJECT_MEMORY_FILE, QEMU_CAPS_KVM); + DO_TEST("cpu-check-none", QEMU_CAPS_KVM); + DO_TEST("cpu-check-partial", QEMU_CAPS_KVM); + DO_TEST("cpu-check-full", QEMU_CAPS_KVM); + DO_TEST("cpu-check-default-none", QEMU_CAPS_KVM); + DO_TEST("cpu-check-default-none2", NONE); + DO_TEST("cpu-check-default-partial", QEMU_CAPS_KVM); + DO_TEST("cpu-check-default-partial2", QEMU_CAPS_KVM); + qemuTestDriverFree(&driver); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-aavmf-virtio-mmio.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-aavmf-virtio-mmio.xml index 2f1f8dd3d..bea65990e 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-aavmf-virtio-mmio.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-aavmf-virtio-mmio.xml @@ -18,7 +18,7 @@ <pae/> <gic version='2'/> </features> - <cpu mode='custom' match='exact'> + <cpu mode='custom' match='exact' check='none'> <model fallback='allow'>cortex-a53</model> </cpu> <clock offset='utc'/> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-video-virtio-gpu-pci.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-video-virtio-gpu-pci.xml index 26f6a5162..2c765e7c3 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-video-virtio-gpu-pci.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-video-virtio-gpu-pci.xml @@ -11,7 +11,7 @@ <acpi/> <gic version='2'/> </features> - <cpu mode='custom' match='exact'> + <cpu mode='custom' match='exact' check='none'> <model fallback='allow'>cortex-a57</model> </cpu> <clock offset='utc'/> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-default.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-default.xml index f7fbdc7a9..88a6a6a0c 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-default.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-default.xml @@ -18,7 +18,7 @@ <pae/> <gic version='2'/> </features> - <cpu mode='custom' match='exact'> + <cpu mode='custom' match='exact' check='none'> <model fallback='allow'>cortex-a53</model> </cpu> <clock offset='utc'/> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml index 1b50f75f0..83cf0d1f5 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml @@ -18,7 +18,7 @@ <pae/> <gic version='2'/> </features> - <cpu mode='custom' match='exact'> + <cpu mode='custom' match='exact' check='none'> <model fallback='allow'>cortex-a53</model> </cpu> <clock offset='utc'/> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none.xml new file mode 100644 index 000000000..b25cdd599 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none.xml @@ -0,0 +1,28 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='host-passthrough' check='none'/> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none2.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none2.xml new file mode 100644 index 000000000..310b95e58 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-none2.xml @@ -0,0 +1,30 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial.xml new file mode 100644 index 000000000..f4bfe54ee --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial.xml @@ -0,0 +1,30 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='host-model' check='partial'> + <model fallback='allow'/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial2.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial2.xml new file mode 100644 index 000000000..8619f009c --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-default-partial2.xml @@ -0,0 +1,30 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact' check='partial'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-full.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-full.xml new file mode 100644 index 000000000..3fd13f27e --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-full.xml @@ -0,0 +1,30 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact' check='full'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-none.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-none.xml new file mode 100644 index 000000000..d71ad5fde --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-none.xml @@ -0,0 +1,30 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-partial.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-partial.xml new file mode 100644 index 000000000..8619f009c --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-check-partial.xml @@ -0,0 +1,30 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>6</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu mode='custom' match='exact' check='partial'> + <model fallback='forbid'>core2duo</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-eoi-disabled.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-eoi-disabled.xml index 7a51e3ddf..b29a24e52 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-eoi-disabled.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-eoi-disabled.xml @@ -13,7 +13,7 @@ <apic eoi='off'/> <pae/> </features> - <cpu mode='custom' match='exact'> + <cpu mode='custom' match='exact' check='none'> <model fallback='allow'>qemu32</model> </cpu> <clock offset='utc'/> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-eoi-enabled.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-eoi-enabled.xml index ae8ab6aee..efba9e5db 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-eoi-enabled.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-eoi-enabled.xml @@ -13,7 +13,7 @@ <apic eoi='on'/> <pae/> </features> - <cpu mode='custom' match='exact'> + <cpu mode='custom' match='exact' check='none'> <model fallback='allow'>qemu32</model> </cpu> <clock offset='utc'/> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-kvmclock.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-kvmclock.xml index c2bdad91f..df0aecb3e 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-kvmclock.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-kvmclock.xml @@ -8,7 +8,7 @@ <type arch='x86_64' machine='pc'>hvm</type> <boot dev='network'/> </os> - <cpu mode='host-passthrough'/> + <cpu mode='host-passthrough' check='none'/> <clock offset='utc'> <timer name='kvmclock' present='no'/> </clock> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-model-features.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-model-features.xml index 161fcfe39..b1e194910 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-model-features.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-model-features.xml @@ -13,7 +13,7 @@ <type arch='i686' machine='pc'>hvm</type> <boot dev='hd'/> </os> - <cpu mode='host-model'> + <cpu mode='host-model' check='partial'> <model fallback='allow'/> <feature policy='require' name='abm'/> <feature policy='force' name='ds'/> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-passthrough-features.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-passthrough-features.xml index 935f8cd39..98189c43f 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-passthrough-features.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-host-passthrough-features.xml @@ -13,7 +13,7 @@ <type arch='i686' machine='pc'>hvm</type> <boot dev='hd'/> </os> - <cpu mode='host-passthrough'> + <cpu mode='host-passthrough' check='none'> <feature policy='require' name='abm'/> <feature policy='force' name='ds'/> <feature policy='disable' name='invtsc'/> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-kvmclock.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-kvmclock.xml index 4d222de4a..a483025a0 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-kvmclock.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-kvmclock.xml @@ -8,7 +8,7 @@ <type arch='i686' machine='pc'>hvm</type> <boot dev='network'/> </os> - <cpu mode='custom' match='exact'> + <cpu mode='custom' match='exact' check='partial'> <model fallback='allow'>core2duo</model> </cpu> <clock offset='utc'> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-graphics-spice-timeout.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-graphics-spice-timeout.xml index 5f881f1fb..b807ac31b 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-graphics-spice-timeout.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-graphics-spice-timeout.xml @@ -15,7 +15,7 @@ <apic/> <pae/> </features> - <cpu mode='custom' match='exact'> + <cpu mode='custom' match='exact' check='partial'> <model fallback='allow'>core2duo</model> <vendor>Intel</vendor> <topology sockets='1' cores='2' threads='1'/> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 4353ad245..af56dd4e5 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -1108,6 +1108,14 @@ mymain(void) QEMU_CAPS_MACHINE_OPT, QEMU_CAPS_MACHINE_IOMMU); + DO_TEST("cpu-check-none", NONE); + DO_TEST("cpu-check-partial", NONE); + DO_TEST("cpu-check-full", NONE); + DO_TEST("cpu-check-default-none", NONE); + DO_TEST("cpu-check-default-none2", NONE); + DO_TEST("cpu-check-default-partial", NONE); + DO_TEST("cpu-check-default-partial2", NONE); + qemuTestDriverFree(&driver); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -- 2.12.0

The checks are now in a dedicated qemuProcessVerifyHypervFeatures function. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 88 ++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index e9631c111..48820a204 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3726,6 +3726,59 @@ qemuValidateCpuCount(virDomainDefPtr def, } +static int +qemuProcessVerifyHypervFeatures(virDomainDefPtr def, + virCPUDataPtr cpu) +{ + char *cpuFeature; + size_t i; + int rc; + + for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) { + if (def->hyperv_features[i] != VIR_TRISTATE_SWITCH_ON) + continue; + + if (virAsprintf(&cpuFeature, "__kvm_hv_%s", + virDomainHypervTypeToString(i)) < 0) + return -1; + + rc = virCPUDataCheckFeature(cpu, cpuFeature); + VIR_FREE(cpuFeature); + + if (rc < 0) + return -1; + else if (rc == 1) + continue; + + switch ((virDomainHyperv) i) { + case VIR_DOMAIN_HYPERV_RELAXED: + case VIR_DOMAIN_HYPERV_VAPIC: + case VIR_DOMAIN_HYPERV_SPINLOCKS: + VIR_WARN("host doesn't support hyperv '%s' feature", + virDomainHypervTypeToString(i)); + break; + + case VIR_DOMAIN_HYPERV_VPINDEX: + case VIR_DOMAIN_HYPERV_RUNTIME: + case VIR_DOMAIN_HYPERV_SYNIC: + case VIR_DOMAIN_HYPERV_STIMER: + case VIR_DOMAIN_HYPERV_RESET: + case VIR_DOMAIN_HYPERV_VENDOR_ID: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("host doesn't support hyperv '%s' feature"), + virDomainHypervTypeToString(i)); + return -1; + + /* coverity[dead_error_begin] */ + case VIR_DOMAIN_HYPERV_LAST: + break; + } + } + + return 0; +} + + static bool qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -3763,39 +3816,8 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, } } - for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) { - if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON) { - char *cpuFeature; - if (virAsprintf(&cpuFeature, "__kvm_hv_%s", - virDomainHypervTypeToString(i)) < 0) - goto cleanup; - if (!virCPUDataCheckFeature(guestcpu, cpuFeature)) { - switch ((virDomainHyperv) i) { - case VIR_DOMAIN_HYPERV_RELAXED: - case VIR_DOMAIN_HYPERV_VAPIC: - case VIR_DOMAIN_HYPERV_SPINLOCKS: - VIR_WARN("host doesn't support hyperv '%s' feature", - virDomainHypervTypeToString(i)); - break; - case VIR_DOMAIN_HYPERV_VPINDEX: - case VIR_DOMAIN_HYPERV_RUNTIME: - case VIR_DOMAIN_HYPERV_SYNIC: - case VIR_DOMAIN_HYPERV_STIMER: - case VIR_DOMAIN_HYPERV_RESET: - case VIR_DOMAIN_HYPERV_VENDOR_ID: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("host doesn't support hyperv '%s' feature"), - virDomainHypervTypeToString(i)); - goto cleanup; - break; - - /* coverity[dead_error_begin] */ - case VIR_DOMAIN_HYPERV_LAST: - break; - } - } - } - } + if (qemuProcessVerifyHypervFeatures(def, guestcpu) < 0) + goto cleanup; if (def->cpu) { for (i = 0; i < def->cpu->nfeatures; i++) { -- 2.12.0

On Tue, Mar 14, 2017 at 05:57:44PM +0100, Jiri Denemark wrote:
The checks are now in a dedicated qemuProcessVerifyHypervFeatures function.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 88 ++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 33 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index e9631c111..48820a204 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3763,39 +3816,8 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, } }
- for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) { - if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON) { - char *cpuFeature; - if (virAsprintf(&cpuFeature, "__kvm_hv_%s", - virDomainHypervTypeToString(i)) < 0) - goto cleanup; - if (!virCPUDataCheckFeature(guestcpu, cpuFeature)) {
Before, a failure of virCPUDataCheckFeature was ignored. These refactors add error checking, which should be mentioned in the commit messages. Jan

The checks are now in a dedicated qemuProcessVerifyKVMFeatures function. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 48820a204..df9489f3e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3779,6 +3779,28 @@ qemuProcessVerifyHypervFeatures(virDomainDefPtr def, } +static int +qemuProcessVerifyKVMFeatures(virDomainDefPtr def, + virCPUDataPtr cpu) +{ + int rc = 0; + + if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK] != VIR_TRISTATE_SWITCH_ON) + return 0; + + rc = virCPUDataCheckFeature(cpu, VIR_CPU_x86_KVM_PV_UNHALT); + + if (rc <= 0) { + if (rc == 0) + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support paravirtual spinlocks")); + return -1; + } + + return 0; +} + + static bool qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -3808,15 +3830,8 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, goto cleanup; } - if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK] == VIR_TRISTATE_SWITCH_ON) { - if (!virCPUDataCheckFeature(guestcpu, VIR_CPU_x86_KVM_PV_UNHALT)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("host doesn't support paravirtual spinlocks")); - goto cleanup; - } - } - - if (qemuProcessVerifyHypervFeatures(def, guestcpu) < 0) + if (qemuProcessVerifyKVMFeatures(def, guestcpu) < 0 || + qemuProcessVerifyHypervFeatures(def, guestcpu) < 0) goto cleanup; if (def->cpu) { -- 2.12.0

The checks are now in a dedicated qemuProcessVerifyCPUFeatures function. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index df9489f3e..25371b93d 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3801,6 +3801,36 @@ qemuProcessVerifyKVMFeatures(virDomainDefPtr def, } +static int +qemuProcessVerifyCPUFeatures(virDomainDefPtr def, + virCPUDataPtr cpu) +{ + int rc; + + if (!def->cpu || + (def->cpu->mode == VIR_CPU_MODE_CUSTOM && + !def->cpu->model)) + return 0; + + rc = virCPUCheckFeature(def->os.arch, def->cpu, "invtsc"); + + if (rc < 0) { + return -1; + } else if (rc == 1) { + rc = virCPUDataCheckFeature(cpu, "invtsc"); + if (rc <= 0) { + if (rc == 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support invariant TSC")); + } + return -1; + } + } + + return 0; +} + + static bool qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -3812,7 +3842,6 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; int rc; bool ret = false; - size_t i; switch (arch) { case VIR_ARCH_I686: @@ -3834,21 +3863,8 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, qemuProcessVerifyHypervFeatures(def, guestcpu) < 0) goto cleanup; - if (def->cpu) { - for (i = 0; i < def->cpu->nfeatures; i++) { - virCPUFeatureDefPtr feature = &def->cpu->features[i]; - - if (feature->policy != VIR_CPU_FEATURE_REQUIRE) - continue; - - if (STREQ(feature->name, "invtsc") && - !virCPUDataCheckFeature(guestcpu, feature->name)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("host doesn't support invariant TSC")); - goto cleanup; - } - } - } + if (qemuProcessVerifyCPUFeatures(def, guestcpu) < 0) + goto cleanup; break; default: -- 2.12.0

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 25371b93d..894679373 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3831,50 +3831,44 @@ qemuProcessVerifyCPUFeatures(virDomainDefPtr def, } -static bool +static int qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, virDomainObjPtr vm, - int asyncJob) + qemuDomainAsyncJob asyncJob) { virDomainDefPtr def = vm->def; - virArch arch = def->os.arch; - virCPUDataPtr guestcpu = NULL; + virCPUDataPtr cpu = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; int rc; - bool ret = false; + int ret = -1; - switch (arch) { - case VIR_ARCH_I686: - case VIR_ARCH_X86_64: + if (ARCH_IS_X86(def->os.arch)) { if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) - return false; - rc = qemuMonitorGetGuestCPU(priv->mon, arch, &guestcpu); + goto cleanup; + + rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu); + if (qemuDomainObjExitMonitor(driver, vm) < 0) - return false; + goto cleanup; if (rc < 0) { if (rc == -2) - break; - + ret = 0; goto cleanup; } - if (qemuProcessVerifyKVMFeatures(def, guestcpu) < 0 || - qemuProcessVerifyHypervFeatures(def, guestcpu) < 0) + if (qemuProcessVerifyKVMFeatures(def, cpu) < 0 || + qemuProcessVerifyHypervFeatures(def, cpu) < 0) goto cleanup; - if (qemuProcessVerifyCPUFeatures(def, guestcpu) < 0) + if (qemuProcessVerifyCPUFeatures(def, cpu) < 0) goto cleanup; - break; - - default: - break; } - ret = true; + ret = 0; cleanup: - virCPUDataFree(guestcpu); + virCPUDataFree(cpu); return ret; } @@ -5719,7 +5713,7 @@ qemuProcessLaunch(virConnectPtr conn, goto cleanup; VIR_DEBUG("Detecting if required emulator features are present"); - if (!qemuProcessVerifyGuestCPU(driver, vm, asyncJob)) + if (qemuProcessVerifyGuestCPU(driver, vm, asyncJob) < 0) goto cleanup; VIR_DEBUG("Setting up post-init cgroup restrictions"); -- 2.12.0

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_monitor_json.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 3a29f1aa7..733daf096 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -6741,22 +6741,19 @@ qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon, { int rc; - switch (arch) { - case VIR_ARCH_X86_64: - case VIR_ARCH_I686: + if (ARCH_IS_X86(arch)) { if ((rc = qemuMonitorJSONCheckCPUx86(mon)) < 0) return -1; else if (!rc) return -2; return qemuMonitorJSONGetCPUx86Data(mon, "feature-words", data); - - default: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("CPU definition retrieval isn't supported for '%s'"), - virArchToString(arch)); - return -1; } + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU definition retrieval isn't supported for '%s'"), + virArchToString(arch)); + return -1; } int -- 2.12.0

qemuMonitorGetGuestCPU can now optionally create CPU data from filtered-features in addition to feature-words. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_monitor.c | 11 ++++++++--- src/qemu/qemu_monitor.h | 3 ++- src/qemu/qemu_monitor_json.c | 25 +++++++++++++++++++++++-- src/qemu/qemu_monitor_json.h | 3 ++- src/qemu/qemu_process.c | 2 +- tests/qemumonitorjsontest.c | 4 ++-- 6 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index d71f84c80..70e9724c5 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4000,6 +4000,7 @@ qemuMonitorSetDomainLog(qemuMonitorPtr mon, * @mon: Pointer to the monitor * @arch: arch of the guest * @data: returns the cpu data + * @disabled: returns the CPU data for features which were disabled by QEMU * * Retrieve the definition of the guest CPU from a running qemu instance. * @@ -4009,15 +4010,19 @@ qemuMonitorSetDomainLog(qemuMonitorPtr mon, int qemuMonitorGetGuestCPU(qemuMonitorPtr mon, virArch arch, - virCPUDataPtr *data) + virCPUDataPtr *data, + virCPUDataPtr *disabled) { - VIR_DEBUG("arch='%s' data='%p'", virArchToString(arch), data); + VIR_DEBUG("arch=%s data=%p disabled=%p", + virArchToString(arch), data, disabled); QEMU_CHECK_MONITOR_JSON(mon); *data = NULL; + if (disabled) + *disabled = NULL; - return qemuMonitorJSONGetGuestCPU(mon, arch, data); + return qemuMonitorJSONGetGuestCPU(mon, arch, data, disabled); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 847e9458a..575d72edd 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1020,7 +1020,8 @@ void qemuMonitorSetDomainLog(qemuMonitorPtr mon, int qemuMonitorGetGuestCPU(qemuMonitorPtr mon, virArch arch, - virCPUDataPtr *data); + virCPUDataPtr *data, + virCPUDataPtr *disabled); int qemuMonitorRTCResetReinjection(qemuMonitorPtr mon); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 733daf096..553544aea 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -6728,6 +6728,7 @@ qemuMonitorJSONCheckCPUx86(qemuMonitorPtr mon) * @mon: Pointer to the monitor * @arch: arch of the guest * @data: returns the cpu data of the guest + * @disabled: returns the CPU data for features which were disabled by QEMU * * Retrieve the definition of the guest CPU from a running qemu instance. * @@ -6737,8 +6738,11 @@ qemuMonitorJSONCheckCPUx86(qemuMonitorPtr mon) int qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon, virArch arch, - virCPUDataPtr *data) + virCPUDataPtr *data, + virCPUDataPtr *disabled) { + virCPUDataPtr cpuEnabled = NULL; + virCPUDataPtr cpuDisabled = NULL; int rc; if (ARCH_IS_X86(arch)) { @@ -6747,13 +6751,30 @@ qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon, else if (!rc) return -2; - return qemuMonitorJSONGetCPUx86Data(mon, "feature-words", data); + if (qemuMonitorJSONGetCPUx86Data(mon, "feature-words", + &cpuEnabled) < 0) + goto error; + + if (disabled && + qemuMonitorJSONGetCPUx86Data(mon, "filtered-features", + &cpuDisabled) < 0) + goto error; + + *data = cpuEnabled; + if (disabled) + *disabled = cpuDisabled; + return 0; } virReportError(VIR_ERR_INTERNAL_ERROR, _("CPU definition retrieval isn't supported for '%s'"), virArchToString(arch)); return -1; + + error: + virCPUDataFree(cpuEnabled); + virCPUDataFree(cpuDisabled); + return -1; } int diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 59d9f098c..2bc2d6ea8 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -475,7 +475,8 @@ int qemuMonitorJSONGetCPUx86Data(qemuMonitorPtr mon, int qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon, virArch arch, - virCPUDataPtr *data); + virCPUDataPtr *data, + virCPUDataPtr *disabled); int qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 894679373..f2aa134d4 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3846,7 +3846,7 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) goto cleanup; - rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu); + rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu, NULL); if (qemuDomainObjExitMonitor(driver, vm) < 0) goto cleanup; diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 402c87d45..d0f9381b3 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -2395,7 +2395,7 @@ testQemuMonitorJSONGetCPUData(const void *opaque) if (qemuMonitorJSONGetGuestCPU(qemuMonitorTestGetMonitor(test), VIR_ARCH_X86_64, - &cpuData) < 0) + &cpuData, NULL) < 0) goto cleanup; if (!(actual = virCPUDataFormat(cpuData))) @@ -2438,7 +2438,7 @@ testQemuMonitorJSONGetNonExistingCPUData(const void *opaque) rv = qemuMonitorJSONGetGuestCPU(qemuMonitorTestGetMonitor(test), VIR_ARCH_X86_64, - &cpuData); + &cpuData, NULL); if (rv != -2) { virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected return value %d, expecting -2", rv); -- 2.12.0

When starting a domain with custom guest CPU specification QEMU may add or remove some CPU features. There are several reasons for this, e.g., QEMU/KVM does not support some requested features or the definition of the requested CPU model in libvirt's cpu_map.xml differs from the one QEMU is using. We can't really avoid this because CPU models are allowed to change with machine types and libvirt doesn't know (and probably doesn't even want to know) about such changes. Thus when we want to make sure guest ABI doesn't change when a domain gets migrated to another host, we need to update our live CPU definition according to the CPU QEMU created. Once updated, we will change CPU checking to VIR_CPU_CHECK_FULL to make sure the virtual CPU created after migration exactly matches the one on the source. https://bugzilla.redhat.com/show_bug.cgi?id=822148 https://bugzilla.redhat.com/show_bug.cgi?id=824989 Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/cpu/cpu.c | 44 ++++++++++++++++++++++++++++++++++++ src/cpu/cpu.h | 12 ++++++++++ src/cpu/cpu_x86.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 1 + src/qemu/qemu_process.c | 19 +++++++++++----- 5 files changed, 129 insertions(+), 6 deletions(-) diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 5b1940b47..992a0339c 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -713,6 +713,50 @@ virCPUUpdate(virArch arch, /** + * virCPUUpdateLive: + * + * @arch: CPU architecture + * @cpu: guest CPU definition to be updated + * @dataEnabled: CPU data of the virtual CPU + * @dataDisabled: CPU data with features requested by @cpu but disabled by the + * hypervisor + * + * Update custom mode CPU according to the virtual CPU created by the + * hypervisor. + * + * Returns -1 on error, + * 0 when the CPU was successfully updated, + * 1 when the operation does not make sense on the CPU or it is not + * supported for the given architecture. + */ +int +virCPUUpdateLive(virArch arch, + virCPUDefPtr cpu, + virCPUDataPtr dataEnabled, + virCPUDataPtr dataDisabled) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, cpu=%p, dataEnabled=%p, dataDisabled=%p", + virArchToString(arch), cpu, dataEnabled, dataDisabled); + + if (!(driver = cpuGetSubDriver(arch))) + return -1; + + if (!driver->updateLive) + return 1; + + if (cpu->mode != VIR_CPU_MODE_CUSTOM) + return 1; + + if (driver->updateLive(cpu, dataEnabled, dataDisabled) < 0) + return -1; + + return 0; +} + + +/** * virCPUCheckFeature: * * @arch: CPU architecture diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index c329eb134..7d6d3e921 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -87,6 +87,11 @@ typedef int const virCPUDef *host); typedef int +(*virCPUArchUpdateLive)(virCPUDefPtr cpu, + virCPUDataPtr dataEnabled, + virCPUDataPtr dataDisabled); + +typedef int (*virCPUArchCheckFeature)(const virCPUDef *cpu, const char *feature); @@ -122,6 +127,7 @@ struct cpuArchDriver { virCPUArchGetHost getHost; cpuArchBaseline baseline; virCPUArchUpdate update; + virCPUArchUpdateLive updateLive; virCPUArchCheckFeature checkFeature; virCPUArchDataCheckFeature dataCheckFeature; virCPUArchDataFormat dataFormat; @@ -198,6 +204,12 @@ virCPUUpdate(virArch arch, const virCPUDef *host) ATTRIBUTE_NONNULL(2); +int +virCPUUpdateLive(virArch arch, + virCPUDefPtr cpu, + virCPUDataPtr dataEnabled, + virCPUDataPtr dataDisabled) + ATTRIBUTE_NONNULL(2); int virCPUCheckFeature(virArch arch, diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index 6719acee2..a43bb2bdf 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -2678,6 +2678,64 @@ virCPUx86Update(virCPUDefPtr guest, static int +virCPUx86UpdateLive(virCPUDefPtr cpu, + virCPUDataPtr dataEnabled, + virCPUDataPtr dataDisabled) +{ + virCPUx86MapPtr map; + virCPUx86ModelPtr model = NULL; + virCPUx86Data enabled = VIR_CPU_X86_DATA_INIT; + virCPUx86Data disabled = VIR_CPU_X86_DATA_INIT; + size_t i; + int ret = -1; + + if (!(map = virCPUx86GetMap())) + return -1; + + if (!(model = x86ModelFromCPU(cpu, map, -1))) + goto cleanup; + + if (dataEnabled && + x86DataCopy(&enabled, &dataEnabled->data.x86) < 0) + goto cleanup; + + if (dataDisabled && + x86DataCopy(&disabled, &dataDisabled->data.x86) < 0) + goto cleanup; + + x86DataSubtract(&enabled, &model->data); + + for (i = 0; i < map->nfeatures; i++) { + virCPUx86FeaturePtr feature = map->features[i]; + + if (x86DataIsSubset(&enabled, &feature->data)) { + VIR_DEBUG("Adding feature '%s' enabled by the hypervisor", + feature->name); + if (virCPUDefUpdateFeature(cpu, feature->name, + VIR_CPU_FEATURE_REQUIRE) < 0) + goto cleanup; + } + + if (x86DataIsSubset(&disabled, &feature->data)) { + VIR_DEBUG("Removing feature '%s' disabled by the hypervisor", + feature->name); + if (virCPUDefUpdateFeature(cpu, feature->name, + VIR_CPU_FEATURE_DISABLE) < 0) + goto cleanup; + } + } + + ret = 0; + + cleanup: + x86ModelFree(model); + virCPUx86DataClear(&enabled); + virCPUx86DataClear(&disabled); + return ret; +} + + +static int virCPUx86CheckFeature(const virCPUDef *cpu, const char *name) { @@ -2854,6 +2912,7 @@ struct cpuArchDriver cpuDriverX86 = { #endif .baseline = x86Baseline, .update = virCPUx86Update, + .updateLive = virCPUx86UpdateLive, .checkFeature = virCPUx86CheckFeature, .dataCheckFeature = virCPUx86DataCheckFeature, .dataFormat = virCPUx86DataFormat, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4efea0098..0fbbe257f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1009,6 +1009,7 @@ virCPUGetHost; virCPUGetModels; virCPUTranslate; virCPUUpdate; +virCPUUpdateLive; # cpu/cpu_x86.h diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f2aa134d4..3d9ab0cdc 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3832,12 +3832,13 @@ qemuProcessVerifyCPUFeatures(virDomainDefPtr def, static int -qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob) +qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) { virDomainDefPtr def = vm->def; virCPUDataPtr cpu = NULL; + virCPUDataPtr disabled = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; int rc; int ret = -1; @@ -3846,7 +3847,7 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) goto cleanup; - rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu, NULL); + rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu, &disabled); if (qemuDomainObjExitMonitor(driver, vm) < 0) goto cleanup; @@ -3863,12 +3864,18 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver, if (qemuProcessVerifyCPUFeatures(def, cpu) < 0) goto cleanup; + + if ((rc = virCPUUpdateLive(def->os.arch, def->cpu, cpu, disabled)) < 0) + goto cleanup; + else if (rc == 0) + def->cpu->check = VIR_CPU_CHECK_FULL; } ret = 0; cleanup: virCPUDataFree(cpu); + virCPUDataFree(disabled); return ret; } @@ -5712,8 +5719,8 @@ qemuProcessLaunch(virConnectPtr conn, if (qemuConnectAgent(driver, vm) < 0) goto cleanup; - VIR_DEBUG("Detecting if required emulator features are present"); - if (qemuProcessVerifyGuestCPU(driver, vm, asyncJob) < 0) + VIR_DEBUG("Verifying and updating provided guest CPU"); + if (qemuProcessUpdateLiveGuestCPU(driver, vm, asyncJob) < 0) goto cleanup; VIR_DEBUG("Setting up post-init cgroup restrictions"); -- 2.12.0

When guest CPU definition uses VIR_CPU_CHECK_FULL checks, we need to make sure QEMU does not add or remove any features. https://bugzilla.redhat.com/show_bug.cgi?id=822148 https://bugzilla.redhat.com/show_bug.cgi?id=824989 Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/cpu/cpu.c | 3 ++- src/cpu/cpu_x86.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 992a0339c..1461190ba 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -722,7 +722,8 @@ virCPUUpdate(virArch arch, * hypervisor * * Update custom mode CPU according to the virtual CPU created by the - * hypervisor. + * hypervisor. The function refuses to update the CPU in case cpu->check is set + * to VIR_CPU_CHECK_FULL. * * Returns -1 on error, * 0 when the CPU was successfully updated, diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index a43bb2bdf..9e208b094 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -2686,6 +2686,10 @@ virCPUx86UpdateLive(virCPUDefPtr cpu, virCPUx86ModelPtr model = NULL; virCPUx86Data enabled = VIR_CPU_X86_DATA_INIT; virCPUx86Data disabled = VIR_CPU_X86_DATA_INIT; + virBuffer bufAdded = VIR_BUFFER_INITIALIZER; + virBuffer bufRemoved = VIR_BUFFER_INITIALIZER; + char *added = NULL; + char *removed = NULL; size_t i; int ret = -1; @@ -2709,28 +2713,70 @@ virCPUx86UpdateLive(virCPUDefPtr cpu, virCPUx86FeaturePtr feature = map->features[i]; if (x86DataIsSubset(&enabled, &feature->data)) { - VIR_DEBUG("Adding feature '%s' enabled by the hypervisor", - feature->name); - if (virCPUDefUpdateFeature(cpu, feature->name, - VIR_CPU_FEATURE_REQUIRE) < 0) + VIR_DEBUG("Feature '%s' enabled by the hypervisor", feature->name); + if (cpu->check == VIR_CPU_CHECK_FULL) + virBufferAsprintf(&bufAdded, "%s,", feature->name); + else if (virCPUDefUpdateFeature(cpu, feature->name, + VIR_CPU_FEATURE_REQUIRE) < 0) goto cleanup; } if (x86DataIsSubset(&disabled, &feature->data)) { - VIR_DEBUG("Removing feature '%s' disabled by the hypervisor", - feature->name); - if (virCPUDefUpdateFeature(cpu, feature->name, - VIR_CPU_FEATURE_DISABLE) < 0) + VIR_DEBUG("Feature '%s' disabled by the hypervisor", feature->name); + if (cpu->check == VIR_CPU_CHECK_FULL) + virBufferAsprintf(&bufRemoved, "%s,", feature->name); + else if (virCPUDefUpdateFeature(cpu, feature->name, + VIR_CPU_FEATURE_DISABLE) < 0) goto cleanup; } } + virBufferTrim(&bufAdded, ",", -1); + virBufferTrim(&bufRemoved, ",", -1); + + if (virBufferCheckError(&bufAdded) < 0 || + virBufferCheckError(&bufRemoved) < 0) + goto cleanup; + + added = virBufferContentAndReset(&bufAdded); + removed = virBufferContentAndReset(&bufRemoved); + + if (added || removed) { + if (added && removed) + virReportError(VIR_ERR_OPERATION_FAILED, + _("guest CPU doesn't match specification: " + "extra features: %s, missing features: %s"), + added, removed); + else if (added) + virReportError(VIR_ERR_OPERATION_FAILED, + _("guest CPU doesn't match specification: " + "extra features: %s"), + added); + else + virReportError(VIR_ERR_OPERATION_FAILED, + _("guest CPU doesn't match specification: " + "missing features: %s"), + removed); + goto cleanup; + } + + if (cpu->check == VIR_CPU_CHECK_FULL && + !x86DataIsEmpty(&disabled)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("guest CPU doesn't match specification")); + goto cleanup; + } + ret = 0; cleanup: x86ModelFree(model); virCPUx86DataClear(&enabled); virCPUx86DataClear(&disabled); + VIR_FREE(added); + VIR_FREE(removed); + virBufferFreeAndReset(&bufAdded); + virBufferFreeAndReset(&bufRemoved); return ret; } -- 2.12.0

On Tue, Mar 14, 2017 at 05:57:39PM +0100, Jiri Denemark wrote:
When starting a domain with custom guest CPU specification QEMU may add or remove some CPU features. There are several reasons for this, e.g., QEMU/KVM does not support some requested features or the definition of the requested CPU model in libvirt's cpu_map.xml differs from the one QEMU is using. We can't really avoid this because CPU models are allowed to change with machine types and libvirt doesn't know (and probably doesn't even want to know) about such changes.
Thus when we want to make sure guest ABI doesn't change when a domain gets migrated to another host, we need to update our live CPU definition according to the CPU QEMU created. Once updated, we will change CPU checking to VIR_CPU_CHECK_FULL to make sure the virtual CPU created after migration exactly matches the one on the source.
https://bugzilla.redhat.com/show_bug.cgi?id=822148 https://bugzilla.redhat.com/show_bug.cgi?id=824989
Jiri Denemark (12): tests: Switch to sparse initialization of virCPUDef docs: Clarify /domain/cpu/@match description Introduce /domain/cpu/@check XML attribute qemu: Set default values for CPU check attribute qemu: Refactor Hyper-V features check qemu: Refactor KVM features check qemu: Refactor CPU features check qemu: Refactor qemuProcessVerifyGuestCPU qemu: Use ARCH_IS_X86 in qemuMonitorJSONGetGuestCPU qemu: Ask QEMU for filtered CPU features qemu: Update CPU definition according to QEMU qemu: Enforce guest CPU specification
ACK series. Jan

On Wed, Mar 15, 2017 at 17:39:34 +0100, Ján Tomko wrote:
On Tue, Mar 14, 2017 at 05:57:39PM +0100, Jiri Denemark wrote:
When starting a domain with custom guest CPU specification QEMU may add or remove some CPU features. There are several reasons for this, e.g., QEMU/KVM does not support some requested features or the definition of the requested CPU model in libvirt's cpu_map.xml differs from the one QEMU is using. We can't really avoid this because CPU models are allowed to change with machine types and libvirt doesn't know (and probably doesn't even want to know) about such changes.
Thus when we want to make sure guest ABI doesn't change when a domain gets migrated to another host, we need to update our live CPU definition according to the CPU QEMU created. Once updated, we will change CPU checking to VIR_CPU_CHECK_FULL to make sure the virtual CPU created after migration exactly matches the one on the source.
https://bugzilla.redhat.com/show_bug.cgi?id=822148 https://bugzilla.redhat.com/show_bug.cgi?id=824989
Jiri Denemark (12): tests: Switch to sparse initialization of virCPUDef docs: Clarify /domain/cpu/@match description Introduce /domain/cpu/@check XML attribute qemu: Set default values for CPU check attribute qemu: Refactor Hyper-V features check qemu: Refactor KVM features check qemu: Refactor CPU features check qemu: Refactor qemuProcessVerifyGuestCPU qemu: Use ARCH_IS_X86 in qemuMonitorJSONGetGuestCPU qemu: Ask QEMU for filtered CPU features qemu: Update CPU definition according to QEMU qemu: Enforce guest CPU specification
ACK series.
Thanks, I implemented a test for the new virCPUUpdateLive API to confirm the API works as expected (I'll send it to the list later today) and pushed this series. Jirka
participants (2)
-
Jiri Denemark
-
Ján Tomko