Add support for cpu mode attribute

The mode can be either of "custom" (default), "host-model",
"host-passthrough". The semantics of each mode is described in the
following examples:

- guest CPU is a default model with specified topology:
    <cpu>
      <topology sockets='1' cores='2' threads='1'/>
    </cpu>

- guest CPU matches selected model:
    <cpu mode='custom' match='exact'>
      <model>core2duo</model>
    </cpu>

- guest CPU should be a copy of host CPU as advertised by capabilities
  XML (this is a short cut for manually copying host CPU specification
  from capabilities to domain XML):
    <cpu mode='host-model'/>

  In case a hypervisor does not support the exact host model, libvirt
  automatically falls back to a closest supported CPU model and
  removes/adds features to match host. This behavior can be disabled by
    <cpu mode='host-model'>
      <model fallback='forbid'/>
    </cpu>

- the same as previous returned by virDomainGetXMLDesc with
  VIR_DOMAIN_XML_UPDATE_CPU flag:
    <cpu mode='host-model' match='exact'>
      <model fallback='allow'>Penryn</model>       --+
      <vendor>Intel</vendor>                         |
      <topology sockets='2' cores='4' threads='1'/>  + copied from
      <feature policy='require' name='dca'/>         | capabilities XML
      <feature policy='require' name='xtpr'/>        |
      ...                                          --+
    </cpu>

- guest CPU should be exactly the same as host CPU even in the aspects
  libvirt doesn't model (such domain cannot be migrated unless both
  hosts contain exactly the same CPUs):
    <cpu mode='host-passthrough'/>

- the same as previous returned by virDomainGetXMLDesc with
  VIR_DOMAIN_XML_UPDATE_CPU flag:
    <cpu mode='host-passthrough' match='minimal'>
      <model>Penryn</model>                        --+ copied from caps
      <vendor>Intel</vendor>                         | XML but doesn't
      <topology sockets='2' cores='4' threads='1'/>  | describe all
      <feature policy='require' name='dca'/>         | aspects of the
      <feature policy='require' name='xtpr'/>        | actual guest CPU
      ...                                          --+
    </cpu>
This commit is contained in:
Jiri Denemark 2011-08-18 12:14:36 +02:00
parent a6f88cbd2d
commit f7dd3a4e62
28 changed files with 245 additions and 70 deletions

View File

@ -566,6 +566,17 @@
&lt;/cpu&gt;
...</pre>
<pre>
&lt;cpu mode='host-model'&gt;
&lt;model fallback='forbid'/&gt;
&lt;topology sockets='1' cores='2' threads='1'/&gt;
&lt;/cpu&gt;
...</pre>
<pre>
&lt;cpu mode='host-passthrough'/&gt;
...</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.
@ -603,6 +614,55 @@
<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>

View File

@ -2538,6 +2538,20 @@
</interleave>
</group>
<group>
<ref name="cpuMode"/>
<interleave>
<optional>
<ref name="cpuModel"/>
</optional>
<optional>
<ref name="cpuNuma"/>
</optional>
</interleave>
</group>
<group>
<optional>
<ref name="cpuMode"/>
</optional>
<ref name="cpuMatch"/>
<interleave>
<ref name="cpuModel"/>
@ -2559,6 +2573,16 @@
</element>
</define>
<define name="cpuMode">
<attribute name="mode">
<choice>
<value>custom</value>
<value>host-model</value>
<value>host-passthrough</value>
</choice>
</attribute>
</define>
<define name="cpuMatch">
<attribute name="match">
<choice>
@ -2579,7 +2603,10 @@
</choice>
</attribute>
</optional>
<text/>
<choice>
<text/>
<empty/>
</choice>
</element>
</define>

View File

@ -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, " </cpu>\n");

View File

@ -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, "<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);
@ -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, "<model");
if (def->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</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");
@ -504,29 +566,32 @@ virCPUDefFormatBuf(virBufferPtr buf,
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);
}
}
}
@ -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"),

View File

@ -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,

View File

@ -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);

View File

@ -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) {

View File

@ -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)) {

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>Conroe</model>
<vendor>Intel</vendor>
<feature policy='disable' name='lahf_lm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>core2duo</model>
<feature policy='disable' name='nx'/>
</cpu>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>Opteron_G2</model>
<feature policy='disable' name='svm'/>
<feature policy='disable' name='rdtscp'/>

View File

@ -1,3 +1,3 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>Opteron_G1</model>
</cpu>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>486</model>
<feature policy='require' name='svm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>Nehalem</model>
<feature policy='require' name='svm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>qemu64</model>
<feature policy='require' name='lahf_lm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>Nehalem</model>
<feature policy='require' name='svm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>Penryn</model>
<feature policy='require' name='svm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>Penryn</model>
<topology sockets='2' cores='4' threads='1'/>
<feature policy='require' name='dca'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>Penryn</model>
<feature policy='require' name='dca'/>
<feature policy='require' name='xtpr'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>Nehalem</model>
</cpu>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>pentium3</model>
<feature policy='require' name='lahf_lm'/>
<feature policy='require' name='lm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>Penryn</model>
<feature policy='require' name='3dnow'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>core2duo</model>
<feature policy='require' name='lahf_lm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>pentium3</model>
<feature policy='require' name='lahf_lm'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>Nehalem</model>
<feature policy='require' name='dca'/>

View File

@ -1,4 +1,4 @@
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<arch>x86_64</arch>
<model fallback='allow'>Penryn</model>
<feature policy='require' name='svm'/>

View File

@ -15,7 +15,7 @@
<apic/>
<pae/>
</features>
<cpu match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>core2duo</model>
<vendor>Intel</vendor>
<topology sockets='1' cores='2' threads='1'/>

View File

@ -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 */