</cpu>
...</pre>
+<pre>
+ <cpu mode='host-model'>
+ <model fallback='forbid'/>
+ <topology sockets='1' cores='2' threads='1'/>
+ </cpu>
+ ...</pre>
+
+<pre>
+ <cpu mode='host-passthrough'/>
+ ...</pre>
+
<p>
In case no restrictions need to be put on CPU model and its features, a
simpler <code>cpu</code> element can be used.
<span class="since">Since 0.8.5</span> the <code>match</code>
attribute can be omitted and will default to <code>exact</code>.
+
+ <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
+ <code>mode</code> attribute are:
+
+ <dl>
+ <dt><code>custom</code></dt>
+ <dd>In this mode, the <code>cpu</code> element describes the CPU
+ that should be presented to the guest. This is the default when no
+ <code>mode</code> 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.</dd>
+ <dt><code>host-model</code></dt>
+ <dd>The <code>host-model</code> 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
+ <code>match</code> attribute nor any <code>feature</code> elements
+ can be used in this mode. Specifying CPU model is not supported
+ either, but <code>model</code>'s <code>fallback</code> 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.</dd>
+ <dt><code>host-passthrough</code></dt>
+ <dd>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 <code>model</code> nor
+ <code>feature</code> elements are allowed in this mode.</dd>
+ </dl>
+
+ In both <code>host-model</code> and <code>host-passthrough</code>
+ mode, the real (approximate in <code>host-passthrough</code> mode) CPU
+ definition which would be used on current host can be determined by
+ specifying <code>VIR_DOMAIN_XML_UPDATE_CPU</code> flag when calling
+ <code>virDomainGetXMLDesc</code> 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
+ <code>custom</code> mode for more robust migration.
</dd>
<dt><code>model</code></dt>
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",
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;
xmlNodePtr *nodes = NULL;
int n;
unsigned int i;
+ char *cpuMode;
if (!xmlStrEqual(node->name, BAD_CAST "cpu")) {
virCPUReportError(VIR_ERR_INTERNAL_ERROR,
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");
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);
"%s", _("Invalid CPU feature policy"));
goto error;
}
- }
- else
+ } else {
policy = -1;
+ }
if (!(name = virXMLPropString(nodes[i], "name")) || *name == 0) {
VIR_FREE(name);
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))
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, "<cpu");
+ if (def->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, "<cpu match='%s'>\n", match);
- } else {
- virBufferAddLit(buf, "<cpu>\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, " <arch>%s</arch>\n", def->arch);
virBufferAdjustIndent(buf, 2);
- if (virCPUDefFormatBuf(buf, def) < 0)
+ if (virCPUDefFormatBuf(buf, def, flags) < 0)
return -1;
virBufferAdjustIndent(buf, -2);
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, "<model");
- if (def->type == VIR_CPU_TYPE_GUEST) {
+ if (formatFallback) {
const char *fallback;
fallback = virCPUFallbackTypeToString(def->fallback);
}
virBufferAsprintf(buf, " fallback='%s'", fallback);
}
- virBufferAsprintf(buf, ">%s</model>\n", def->model);
+ if (formatModel && def->model) {
+ virBufferAsprintf(buf, ">%s</model>\n", def->model);
+ } else {
+ virBufferAddLit(buf, "/>\n");
+ }
}
- if (def->vendor) {
+ if (formatModel && def->vendor)
virBufferAsprintf(buf, "<vendor>%s</vendor>\n", def->vendor);
- }
if (def->sockets && def->cores && def->threads) {
virBufferAddLit(buf, "<topology");
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, "<feature policy='%s' name='%s'/>\n",
- policy, feature->name);
- } else {
- virBufferAsprintf(buf, "<feature name='%s'/>\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, "<feature policy='%s' name='%s'/>\n",
+ policy, feature->name);
+ } else {
+ virBufferAsprintf(buf, "<feature name='%s'/>\n",
+ feature->name);
+ }
}
}
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"),