diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 3d16d5fada1df336c88573c97ee987d19d8bc841..4a3123e989706c6e42ab03029e95fda1473fd197 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1247,6 +1247,36 @@
Since 0.8.5 the match
attribute can be omitted and will default to exact
.
+ Sometimes the hypervisor is not able to create a virtual CPU exactly
+ matching the specification passed by libvirt.
+ Since 3.2.0, an optional check
+ 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
+ check
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:
+
+
+ none
+ - 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.
+
+
+ partial
+ - 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.
+
+ full
+ - 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.
+
+
Since 0.9.10, an optional mode
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 7cc9dd3d8351b194b0b87a7c45f323fbaa06082f..8189114e3c3032711e53aec2e1628790e71b31a7 100644
--- a/docs/schemas/cputypes.rng
+++ b/docs/schemas/cputypes.rng
@@ -23,6 +23,16 @@
+
+
+
+ none
+ partial
+ full
+
+
+
+
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 24d1edbddd6edec5d3eee9068977b71ef14563fc..fbedc9b1f92d5b20d3ab24ff0a9dce3d5236d1b1 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -4504,6 +4504,9 @@
+
+
+
diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c
index 2724fa30a979927cef304793615bb0b91b630d3f..d7c8b8ff211eed7594ca57283c44924c4615db74 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,19 @@ virCPUDefParseXML(xmlNodePtr node,
goto error;
}
}
+
+ if ((check = virXMLPropString(node, "check"))) {
+ int value = virCPUCheckTypeFromString(check);
+ VIR_FREE(check);
+
+ if (value < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Invalid check attribute for CPU "
+ "specification"));
+ goto error;
+ }
+ def->check = value;
+ }
}
if (def->type == VIR_CPU_TYPE_HOST) {
@@ -532,6 +553,11 @@ virCPUDefFormatBufFull(virBufferPtr buf,
}
virBufferAsprintf(&attributeBuf, " match='%s'", tmp);
}
+
+ if (def->check) {
+ virBufferAsprintf(&attributeBuf, " check='%s'",
+ virCPUCheckTypeToString(def->check));
+ }
}
/* Format children */
diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h
index cc3fbf0a45b95d23f5923f404000807ceb77b04e..9118f037ed20dca48ebe55809f3be3a361329620 100644
--- a/src/conf/cpu_conf.h
+++ b/src/conf/cpu_conf.h
@@ -63,6 +63,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 */
+ virCPUCheck check;
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 eaf46b2a48412f3032c51d4c24a23a07b0ebc34a..6bbc6a2a7b4af2e03c5cfcddafbc682fc3502913 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -4592,6 +4592,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)
@@ -4642,6 +4660,9 @@ virDomainDefPostParseInternal(virDomainDefPtr def,
virDomainDefPostParseGraphics(def);
+ if (virDomainDefPostParseCPU(def) < 0)
+ return -1;
+
return 0;
}