diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index cb7532e2bd..6bb5721a74 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -566,6 +566,17 @@ </cpu> ... +
+  <cpu mode='host-model'>
+    <model fallback='forbid'/>
+    <topology sockets='1' cores='2' threads='1'/>
+  </cpu>
+  ...
+ +
+  <cpu mode='host-passthrough'/>
+  ...
+

In case no restrictions need to be put on CPU model and its features, a simpler cpu element can be used. @@ -603,6 +614,55 @@ Since 0.8.5 the match attribute can be omitted and will default to exact. + + 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 + mode attribute are: + +

+
custom
+
In this mode, the cpu element describes the CPU + that should be presented to the guest. This is the default when no + mode attribute is specified. This mode makes it so that + a persistent guest will see the same hardware no matter what host + the guest is booted on.
+
host-model
+
The host-model mode is essentially a shortcut to + copying host CPU definition from capabilities XML into domain XML. + Since the CPU definition is copied just before starting a domain, + exactly the same XML can be used on different hosts while still + providing the best guest CPU each host supports. Neither + match attribute nor any feature elements + can be used in this mode. Specifying CPU model is not supported + either, but model's fallback attribute may + still be used. Libvirt does not model every aspect of each CPU so + the guest CPU will not match the host CPU exactly. On the other + hand, the ABI provided to the guest is reproducible. During + migration, complete CPU model definition is transferred to the + destination host so the migrated guest will see exactly the same CPU + model even if the destination host contains more capable CPUs for + the running instance of the guest; but shutting down and restarting + the guest may present different hardware to the guest according to + the capabilities of the new host.
+
host-passthrough
+
With this mode, the CPU visible to the guest should be exactly + the same as the host CPU even in the aspects that libvirt does not + understand. Though the downside of this mode is that the guest + environment cannot be reproduced on different hardware. Thus, if you + hit any bugs, you are on your own. Neither model nor + feature elements are allowed in this mode.
+
+ + In both host-model and host-passthrough + mode, the real (approximate in host-passthrough mode) CPU + definition which would be used on current host can be determined by + specifying VIR_DOMAIN_XML_UPDATE_CPU flag when calling + virDomainGetXMLDesc API. When running a guest that might + be prone to operating system reactivation when presented with + different hardware, and which will be migrated between hosts with + different capabilities, you can use this output to rewrite XML to the + custom mode for more robust migration.
model
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 133b7f7a82..27b9a1f046 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2538,6 +2538,20 @@ + + + + + + + + + + + + + + @@ -2559,6 +2573,16 @@ + + + + custom + host-model + host-passthrough + + + + @@ -2579,7 +2603,10 @@ - + + + + diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index ac050df2d3..ed0ae99830 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -691,7 +691,7 @@ virCapabilitiesFormatXML(virCapsPtr caps) } virBufferAdjustIndent(&xml, 6); - virCPUDefFormatBuf(&xml, caps->host.cpu); + virCPUDefFormatBuf(&xml, caps->host.cpu, 0); virBufferAdjustIndent(&xml, -6); virBufferAddLit(&xml, " \n"); diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index c8e29e4cd8..31862e2bfe 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -39,6 +39,11 @@ VIR_ENUM_IMPL(virCPU, VIR_CPU_TYPE_LAST, "host", "guest", "auto") +VIR_ENUM_IMPL(virCPUMode, VIR_CPU_MODE_LAST, + "custom", + "host-model", + "host-passthrough") + VIR_ENUM_IMPL(virCPUMatch, VIR_CPU_MATCH_LAST, "minimum", "exact", @@ -100,6 +105,7 @@ virCPUDefCopy(const virCPUDefPtr cpu) copy->nfeatures_max = cpu->nfeatures; copy->type = cpu->type; + copy->mode = cpu->mode; copy->match = cpu->match; copy->fallback = cpu->fallback; copy->sockets = cpu->sockets; @@ -151,6 +157,7 @@ virCPUDefParseXML(const xmlNodePtr node, xmlNodePtr *nodes = NULL; int n; unsigned int i; + char *cpuMode; if (!xmlStrEqual(node->name, BAD_CAST "cpu")) { virCPUReportError(VIR_ERR_INTERNAL_ERROR, @@ -173,10 +180,35 @@ virCPUDefParseXML(const xmlNodePtr node, goto error; } def->type = VIR_CPU_TYPE_HOST; - } else + } else { def->type = VIR_CPU_TYPE_GUEST; - } else + } + } else { def->type = mode; + } + + if ((cpuMode = virXMLPropString(node, "mode"))) { + if (def->type == VIR_CPU_TYPE_HOST) { + VIR_FREE(cpuMode); + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Attribute mode is only allowed for guest CPU")); + goto error; + } else { + def->mode = virCPUModeTypeFromString(cpuMode); + VIR_FREE(cpuMode); + + if (def->mode < 0) { + virCPUReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Invalid mode attribute")); + goto error; + } + } + } else { + if (def->type == VIR_CPU_TYPE_HOST) + def->mode = -1; + else + def->mode = VIR_CPU_MODE_CUSTOM; + } if (def->type == VIR_CPU_TYPE_GUEST) { char *match = virXMLPropString(node, "match"); @@ -214,7 +246,9 @@ virCPUDefParseXML(const xmlNodePtr node, goto error; } - if (def->model && def->type == VIR_CPU_TYPE_GUEST) { + if (def->type == VIR_CPU_TYPE_GUEST && + def->mode != VIR_CPU_MODE_HOST_PASSTHROUGH && + virXPathBoolean("boolean(./model[1]/@fallback)", ctxt)) { const char *fallback; fallback = virXPathString("string(./model[1]/@fallback)", ctxt); @@ -311,9 +345,9 @@ virCPUDefParseXML(const xmlNodePtr node, "%s", _("Invalid CPU feature policy")); goto error; } - } - else + } else { policy = -1; + } if (!(name = virXMLPropString(nodes[i], "name")) || *name == 0) { VIR_FREE(name); @@ -407,11 +441,12 @@ error: char * -virCPUDefFormat(virCPUDefPtr def) +virCPUDefFormat(virCPUDefPtr def, + unsigned int flags) { virBuffer buf = VIR_BUFFER_INITIALIZER; - if (virCPUDefFormatBufFull(&buf, def) < 0) + if (virCPUDefFormatBufFull(&buf, def, flags) < 0) goto cleanup; if (virBufferError(&buf)) @@ -429,29 +464,44 @@ cleanup: int virCPUDefFormatBufFull(virBufferPtr buf, - virCPUDefPtr def) + virCPUDefPtr def, + unsigned int flags) { if (!def) return 0; - if (def->type == VIR_CPU_TYPE_GUEST && def->model) { - const char *match; - if (!(match = virCPUMatchTypeToString(def->match))) { - virCPUReportError(VIR_ERR_INTERNAL_ERROR, - _("Unexpected CPU match policy %d"), def->match); - return -1; + virBufferAddLit(buf, "type == VIR_CPU_TYPE_GUEST) { + const char *tmp; + + if (def->mode != VIR_CPU_MODE_CUSTOM || def->model) { + if (!(tmp = virCPUModeTypeToString(def->mode))) { + virCPUReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected CPU mode %d"), def->mode); + return -1; + } + virBufferAsprintf(buf, " mode='%s'", tmp); } - virBufferAsprintf(buf, "\n", match); - } else { - virBufferAddLit(buf, "\n"); + if (def->model && + (def->mode == VIR_CPU_MODE_CUSTOM || + (flags & VIR_DOMAIN_XML_UPDATE_CPU))) { + if (!(tmp = virCPUMatchTypeToString(def->match))) { + virCPUReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected CPU match policy %d"), + def->match); + return -1; + } + virBufferAsprintf(buf, " match='%s'", tmp); + } } + virBufferAddLit(buf, ">\n"); if (def->arch) virBufferAsprintf(buf, " %s\n", def->arch); virBufferAdjustIndent(buf, 2); - if (virCPUDefFormatBuf(buf, def) < 0) + if (virCPUDefFormatBuf(buf, def, flags) < 0) return -1; virBufferAdjustIndent(buf, -2); @@ -462,22 +512,31 @@ virCPUDefFormatBufFull(virBufferPtr buf, int virCPUDefFormatBuf(virBufferPtr buf, - virCPUDefPtr def) + virCPUDefPtr def, + unsigned int flags) { unsigned int i; + bool formatModel; + bool formatFallback; if (!def) return 0; + formatModel = (def->mode == VIR_CPU_MODE_CUSTOM || + (flags & VIR_DOMAIN_XML_UPDATE_CPU)); + formatFallback = (def->type == VIR_CPU_TYPE_GUEST && + (def->mode == VIR_CPU_MODE_HOST_MODEL || + (def->mode == VIR_CPU_MODE_CUSTOM && def->model))); + if (!def->model && def->nfeatures) { virCPUReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Non-empty feature list specified without CPU model")); return -1; } - if (def->model) { + if ((formatModel && def->model) || formatFallback) { virBufferAddLit(buf, "type == VIR_CPU_TYPE_GUEST) { + if (formatFallback) { const char *fallback; fallback = virCPUFallbackTypeToString(def->fallback); @@ -489,12 +548,15 @@ virCPUDefFormatBuf(virBufferPtr buf, } virBufferAsprintf(buf, " fallback='%s'", fallback); } - virBufferAsprintf(buf, ">%s\n", def->model); + if (formatModel && def->model) { + virBufferAsprintf(buf, ">%s\n", def->model); + } else { + virBufferAddLit(buf, "/>\n"); + } } - if (def->vendor) { + if (formatModel && def->vendor) virBufferAsprintf(buf, "%s\n", def->vendor); - } if (def->sockets && def->cores && def->threads) { virBufferAddLit(buf, "\n"); } - for (i = 0 ; i < def->nfeatures ; i++) { - virCPUFeatureDefPtr feature = def->features + i; + if (formatModel) { + for (i = 0 ; i < def->nfeatures ; i++) { + virCPUFeatureDefPtr feature = def->features + i; - if (!feature->name) { - virCPUReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Missing CPU feature name")); - return -1; - } - - if (def->type == VIR_CPU_TYPE_GUEST) { - const char *policy; - - policy = virCPUFeaturePolicyTypeToString(feature->policy); - if (!policy) { + if (!feature->name) { virCPUReportError(VIR_ERR_INTERNAL_ERROR, - _("Unexpected CPU feature policy %d"), feature->policy); + "%s", _("Missing CPU feature name")); return -1; } - virBufferAsprintf(buf, "\n", - policy, feature->name); - } else { - virBufferAsprintf(buf, "\n", - feature->name); + + if (def->type == VIR_CPU_TYPE_GUEST) { + const char *policy; + + policy = virCPUFeaturePolicyTypeToString(feature->policy); + if (!policy) { + virCPUReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected CPU feature policy %d"), + feature->policy); + return -1; + } + virBufferAsprintf(buf, "\n", + policy, feature->name); + } else { + virBufferAsprintf(buf, "\n", + feature->name); + } } } @@ -603,6 +668,14 @@ virCPUDefIsEqual(virCPUDefPtr src, goto cleanup; } + if (src->mode != dst->mode) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU mode %s does not match source %s"), + virCPUModeTypeToString(dst->mode), + virCPUModeTypeToString(src->mode)); + goto cleanup; + } + if (STRNEQ_NULLABLE(src->arch, dst->arch)) { virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target CPU arch %s does not match source %s"), diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index 0c50f90b15..4b0b35bda3 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -38,6 +38,16 @@ enum virCPUType { VIR_ENUM_DECL(virCPU) +enum virCPUMode { + VIR_CPU_MODE_CUSTOM, + VIR_CPU_MODE_HOST_MODEL, + VIR_CPU_MODE_HOST_PASSTHROUGH, + + VIR_CPU_MODE_LAST +}; + +VIR_ENUM_DECL(virCPUMode) + enum virCPUMatch { VIR_CPU_MATCH_MINIMUM, VIR_CPU_MATCH_EXACT, @@ -89,6 +99,7 @@ typedef struct _virCPUDef virCPUDef; typedef virCPUDef *virCPUDefPtr; struct _virCPUDef { int type; /* enum virCPUType */ + int mode; /* enum virCPUMode */ int match; /* enum virCPUMatch */ char *arch; char *model; @@ -123,14 +134,17 @@ virCPUDefIsEqual(virCPUDefPtr src, virCPUDefPtr dst); char * -virCPUDefFormat(virCPUDefPtr def); +virCPUDefFormat(virCPUDefPtr def, + unsigned int flags); int virCPUDefFormatBuf(virBufferPtr buf, - virCPUDefPtr def); + virCPUDefPtr def, + unsigned int flags); int virCPUDefFormatBufFull(virBufferPtr buf, - virCPUDefPtr def); + virCPUDefPtr def, + unsigned int flags); int virCPUDefAddFeature(virCPUDefPtr cpu, diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6c42f36f1e..dcc32465ac 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11639,7 +11639,7 @@ virDomainDefFormatInternal(virDomainDefPtr def, } virBufferAdjustIndent(buf, 2); - if (virCPUDefFormatBufFull(buf, def->cpu) < 0) + if (virCPUDefFormatBufFull(buf, def->cpu, flags) < 0) goto cleanup; virBufferAdjustIndent(buf, -2); diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index e4149e284a..1f3993429e 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -322,7 +322,7 @@ cpuBaselineXML(const char **xmlCPUs, if (!(cpu = cpuBaseline(cpus, ncpus, models, nmodels))) goto error; - cpustr = virCPUDefFormat(cpu); + cpustr = virCPUDefFormat(cpu, 0); cleanup: if (cpus) { diff --git a/tests/cputest.c b/tests/cputest.c index 2dd89f28be..d8c171398c 100644 --- a/tests/cputest.c +++ b/tests/cputest.c @@ -176,7 +176,7 @@ cpuTestCompareXML(const char *arch, if (virtTestLoadFile(xml, &expected) < 0) goto cleanup; - if (!(actual = virCPUDefFormat(cpu))) + if (!(actual = virCPUDefFormat(cpu, 0))) goto cleanup; if (STRNEQ(expected, actual)) { diff --git a/tests/cputestdata/x86-baseline-1-result.xml b/tests/cputestdata/x86-baseline-1-result.xml index 99e43d018f..96c4f43b3d 100644 --- a/tests/cputestdata/x86-baseline-1-result.xml +++ b/tests/cputestdata/x86-baseline-1-result.xml @@ -1,4 +1,4 @@ - + Conroe Intel diff --git a/tests/cputestdata/x86-baseline-2-result.xml b/tests/cputestdata/x86-baseline-2-result.xml index 76c13aa505..a11352d0b1 100644 --- a/tests/cputestdata/x86-baseline-2-result.xml +++ b/tests/cputestdata/x86-baseline-2-result.xml @@ -1,4 +1,4 @@ - + core2duo diff --git a/tests/cputestdata/x86-baseline-no-vendor-result.xml b/tests/cputestdata/x86-baseline-no-vendor-result.xml index 8b97d2c130..a14bb7e6af 100644 --- a/tests/cputestdata/x86-baseline-no-vendor-result.xml +++ b/tests/cputestdata/x86-baseline-no-vendor-result.xml @@ -1,4 +1,4 @@ - + Opteron_G2 diff --git a/tests/cputestdata/x86-baseline-some-vendors-result.xml b/tests/cputestdata/x86-baseline-some-vendors-result.xml index bac0e5d38f..2927611f7b 100644 --- a/tests/cputestdata/x86-baseline-some-vendors-result.xml +++ b/tests/cputestdata/x86-baseline-some-vendors-result.xml @@ -1,3 +1,3 @@ - + Opteron_G1 diff --git a/tests/cputestdata/x86-host+guest,model486-result.xml b/tests/cputestdata/x86-host+guest,model486-result.xml index 9fd67eb1a7..e21c8b81f4 100644 --- a/tests/cputestdata/x86-host+guest,model486-result.xml +++ b/tests/cputestdata/x86-host+guest,model486-result.xml @@ -1,4 +1,4 @@ - + x86_64 486 diff --git a/tests/cputestdata/x86-host+guest,models,Penryn-result.xml b/tests/cputestdata/x86-host+guest,models,Penryn-result.xml index 9ae11c9500..6a31dcd2aa 100644 --- a/tests/cputestdata/x86-host+guest,models,Penryn-result.xml +++ b/tests/cputestdata/x86-host+guest,models,Penryn-result.xml @@ -1,4 +1,4 @@ - + x86_64 Nehalem diff --git a/tests/cputestdata/x86-host+guest,models,qemu64-result.xml b/tests/cputestdata/x86-host+guest,models,qemu64-result.xml index 7582ddc8c5..1f15d6ec8b 100644 --- a/tests/cputestdata/x86-host+guest,models,qemu64-result.xml +++ b/tests/cputestdata/x86-host+guest,models,qemu64-result.xml @@ -1,4 +1,4 @@ - + x86_64 qemu64 diff --git a/tests/cputestdata/x86-host+guest,models-result.xml b/tests/cputestdata/x86-host+guest,models-result.xml index 9ae11c9500..6a31dcd2aa 100644 --- a/tests/cputestdata/x86-host+guest,models-result.xml +++ b/tests/cputestdata/x86-host+guest,models-result.xml @@ -1,4 +1,4 @@ - + x86_64 Nehalem diff --git a/tests/cputestdata/x86-host+guest-result.xml b/tests/cputestdata/x86-host+guest-result.xml index e596c43209..9d37dec9ca 100644 --- a/tests/cputestdata/x86-host+guest-result.xml +++ b/tests/cputestdata/x86-host+guest-result.xml @@ -1,4 +1,4 @@ - + x86_64 Penryn diff --git a/tests/cputestdata/x86-host+guest.xml b/tests/cputestdata/x86-host+guest.xml index 2a786fd3aa..137a3d6647 100644 --- a/tests/cputestdata/x86-host+guest.xml +++ b/tests/cputestdata/x86-host+guest.xml @@ -1,4 +1,4 @@ - + Penryn diff --git a/tests/cputestdata/x86-host+min.xml b/tests/cputestdata/x86-host+min.xml index fe55058992..81011517bb 100644 --- a/tests/cputestdata/x86-host+min.xml +++ b/tests/cputestdata/x86-host+min.xml @@ -1,4 +1,4 @@ - + Penryn diff --git a/tests/cputestdata/x86-host+nehalem-force-result.xml b/tests/cputestdata/x86-host+nehalem-force-result.xml index 41e7356796..000bc0d61b 100644 --- a/tests/cputestdata/x86-host+nehalem-force-result.xml +++ b/tests/cputestdata/x86-host+nehalem-force-result.xml @@ -1,4 +1,4 @@ - + x86_64 Nehalem diff --git a/tests/cputestdata/x86-host+pentium3.xml b/tests/cputestdata/x86-host+pentium3.xml index e122ba53d6..d46525ce59 100644 --- a/tests/cputestdata/x86-host+pentium3.xml +++ b/tests/cputestdata/x86-host+pentium3.xml @@ -1,4 +1,4 @@ - + pentium3 diff --git a/tests/cputestdata/x86-host+strict-force-extra-result.xml b/tests/cputestdata/x86-host+strict-force-extra-result.xml index f3d52a14b3..68db4129c2 100644 --- a/tests/cputestdata/x86-host+strict-force-extra-result.xml +++ b/tests/cputestdata/x86-host+strict-force-extra-result.xml @@ -1,4 +1,4 @@ - + x86_64 Penryn diff --git a/tests/cputestdata/x86-host-better+pentium3,core2duo-result.xml b/tests/cputestdata/x86-host-better+pentium3,core2duo-result.xml index 5d4528bd6d..0c436d9216 100644 --- a/tests/cputestdata/x86-host-better+pentium3,core2duo-result.xml +++ b/tests/cputestdata/x86-host-better+pentium3,core2duo-result.xml @@ -1,4 +1,4 @@ - + x86_64 core2duo diff --git a/tests/cputestdata/x86-host-better+pentium3,pentium3-result.xml b/tests/cputestdata/x86-host-better+pentium3,pentium3-result.xml index 1530a07e0a..1e4f488616 100644 --- a/tests/cputestdata/x86-host-better+pentium3,pentium3-result.xml +++ b/tests/cputestdata/x86-host-better+pentium3,pentium3-result.xml @@ -1,4 +1,4 @@ - + x86_64 pentium3 diff --git a/tests/cputestdata/x86-host-better+pentium3-result.xml b/tests/cputestdata/x86-host-better+pentium3-result.xml index 917d63f069..07be0f7d64 100644 --- a/tests/cputestdata/x86-host-better+pentium3-result.xml +++ b/tests/cputestdata/x86-host-better+pentium3-result.xml @@ -1,4 +1,4 @@ - + x86_64 Nehalem diff --git a/tests/cputestdata/x86-host-worse+guest-result.xml b/tests/cputestdata/x86-host-worse+guest-result.xml index 78e170adbc..441259fac8 100644 --- a/tests/cputestdata/x86-host-worse+guest-result.xml +++ b/tests/cputestdata/x86-host-worse+guest-result.xml @@ -1,4 +1,4 @@ - + x86_64 Penryn diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-graphics-spice-timeout.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-graphics-spice-timeout.xml index caa5f0a0c0..ac72822508 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-graphics-spice-timeout.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-graphics-spice-timeout.xml @@ -15,7 +15,7 @@ - + core2duo Intel diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index c6adec924c..8e621fe0d4 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -112,6 +112,7 @@ virCapsPtr testQemuCapsInit(void) { }; static virCPUDef host_cpu = { VIR_CPU_TYPE_HOST, /* type */ + 0, /* mode */ 0, /* match */ (char *) "x86_64", /* arch */ (char *) "core2duo", /* model */