[libvirt] [PATCH 0/2] CPU flags capabilities and domain configuration

Hi, These are two initial patches for CPU flags/masks support for libvirt. The first patch contains changes to schemas and describes semantics of new elements and attributes. The second patch introduces internal API functions for XML parsing and formating. Jiri Denemark (2): XML schema for CPU flags XML parsing/formating code for CPU flags docs/schemas/capability.rng | 30 ++++ docs/schemas/domain.rng | 49 ++++++ include/libvirt/virterror.h | 1 + src/Makefile.am | 9 +- src/conf/capabilities.c | 31 ++++- src/conf/capabilities.h | 6 + src/conf/cpu_conf.c | 345 +++++++++++++++++++++++++++++++++++++++++++ src/conf/cpu_conf.h | 110 ++++++++++++++ src/conf/domain_conf.c | 15 ++ src/conf/domain_conf.h | 2 + src/libvirt_private.syms | 8 + src/util/virterror.c | 3 + 12 files changed, 606 insertions(+), 3 deletions(-) create mode 100644 src/conf/cpu_conf.c create mode 100644 src/conf/cpu_conf.h

Firstly, CPU topology and model with optional features have to be advertised in host capabilities: <host> <cpu> <arch>ARCHITECTURE</arch> <features> <!-- old-style features are here --> </features> <model>NAME</model> <topology sockets="S" cores="C" threads="T"/> <feature>NAME</feature> </cpu> ... </host> Secondly, drivers which support detailed CPU specification have to advertise it in guest capabilities: <guest> ... <features> <cpu/> </features> </guest> And finally, CPU may be configured in domain XML configuration: <domain> ... <cpu match="MATCH"> <model>NAME</model> <topology sockets="S" cores="C" threads="T"/> <feature policy="POLICY">NAME</feature> </cpu> </domain> Where MATCH can be one of: - 'minimum' specified CPU is the minimum requested CPU - 'exact' disable all additional features provided by host CPU - 'strict' fail if host CPU doesn't exactly match POLICY can be one of: - 'force' turn on the feature, even if host doesn't have it - 'require' fail if host doesn't have the feature - 'optional' match host - 'disable' turn off the feature, even if host has it - 'forbid' fail if host has the feature 'force' and 'disable' policies turn on/off the feature regardless of its availability on host. 'force' is unlikely to be used but its there for completeness since Xen and VMWare allow it. 'require' and 'forbid' policies prevent a guest from being started on a host which doesn't/does have the feature. 'forbid' is for cases where you disable the feature but a guest may still try to access it anyway and you don't want it to succeed. 'optional' policy sets the feature according to its availability on host. When a guest is booted on a host that has the feature and then migrated to another host, the policy changes to 'require' as we can't take the feature away from a running guest. Default policy for features provided by host CPU but not specified in domain configuration is set using match attribute of cpu tag. If 'minimum' match is requested, additional features will be treated as if they were specified with 'optional' policy. 'exact' match implies 'disable' policy and 'strict' match stands for 'forbid' policy. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- docs/schemas/capability.rng | 30 ++++++++++++++++++++++++++ docs/schemas/domain.rng | 49 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 0 deletions(-) diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index 3e8944c..faeff4d 100644 --- a/docs/schemas/capability.rng +++ b/docs/schemas/capability.rng @@ -25,6 +25,9 @@ <optional> <ref name='cpufeatures'/> </optional> + <optional> + <ref name='cpuspec'/> + </optional> </element> <optional> <ref name='migration'/> @@ -67,6 +70,28 @@ </element> </define> + <define name='cpuspec'> + <element name='model'> + <text/> + </element> + <element name='topology'> + <attribute name='sockets'> + <ref name='uint'/> + </attribute> + <attribute name='cores'> + <ref name='uint'/> + </attribute> + <attribute name='threads'> + <ref name='uint'/> + </attribute> + </element> + <zeroOrMore> + <element name='feature'> + <text/> + </element> + </zeroOrMore> + </define> + <define name='migration'> <element name='migration_features'> <optional> @@ -259,6 +284,11 @@ <empty/> </element> </optional> + <optional> + <element name='cpu'> + <empty/> + </element> + </optional> </element> </define> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 0a6ab61..eb91572 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -38,6 +38,9 @@ <optional> <ref name="seclabel"/> </optional> + <optional> + <ref name="cpu"/> + </optional> </interleave> </element> </define> @@ -1193,6 +1196,52 @@ </optional> </define> <!-- + CPU specification + --> + <define name="cpu"> + <element name="cpu"> + <attribute name="match"> + <choice> + <value>minimum</value> + <value>exact</value> + <value>strict</value> + </choice> + </attribute> + <interleave> + <element name="model"> + <text/> + </element> + <optional> + <element name="topology"> + <attribute name="sockets"> + <ref name="uint"/> + </attribute> + <attribute name="cores"> + <ref name="uint"/> + </attribute> + <attribute name="threads"> + <ref name="uint"/> + </attribute> + </element> + </optional> + <zeroOrMore> + <element name="feature"> + <attribute name="policy"> + <choice> + <value>force</value> + <value>require</value> + <value>optional</value> + <value>disable</value> + <value>forbid</value> + </choice> + </attribute> + <text/> + </element> + </zeroOrMore> + </interleave> + </element> + </define> + <!-- Type library Our unsignedInt doesn't allow a leading '+' in its lexical form -- 1.6.5.2

On Tue, Nov 03, 2009 at 04:40:00PM +0100, Jiri Denemark wrote:
Firstly, CPU topology and model with optional features have to be advertised in host capabilities:
<host> <cpu> <arch>ARCHITECTURE</arch> <features> <!-- old-style features are here --> </features> <model>NAME</model> <topology sockets="S" cores="C" threads="T"/> <feature>NAME</feature> </cpu> ... </host>
Secondly, drivers which support detailed CPU specification have to advertise it in guest capabilities:
<guest> ... <features> <cpu/> </features> </guest>
maybe <cpu/> is a bit too generic. <cpuselection/> or something similar might be a bit better, for example if we want to extend that later to give there a list of the supported CPU names for example <cpu> would be handy.
And finally, CPU may be configured in domain XML configuration:
<domain> ... <cpu match="MATCH"> <model>NAME</model> <topology sockets="S" cores="C" threads="T"/> <feature policy="POLICY">NAME</feature> </cpu> </domain>
I wonder if I don't prefer to have the name in an attribute since we're going with a single feature at a time.
Where MATCH can be one of: - 'minimum' specified CPU is the minimum requested CPU - 'exact' disable all additional features provided by host CPU - 'strict' fail if host CPU doesn't exactly match
POLICY can be one of: - 'force' turn on the feature, even if host doesn't have it - 'require' fail if host doesn't have the feature - 'optional' match host - 'disable' turn off the feature, even if host has it - 'forbid' fail if host has the feature
'force' and 'disable' policies turn on/off the feature regardless of its availability on host. 'force' is unlikely to be used but its there for completeness since Xen and VMWare allow it.
'require' and 'forbid' policies prevent a guest from being started on a host which doesn't/does have the feature. 'forbid' is for cases where you disable the feature but a guest may still try to access it anyway and you don't want it to succeed.
'optional' policy sets the feature according to its availability on host. When a guest is booted on a host that has the feature and then migrated to another host, the policy changes to 'require' as we can't take the feature away from a running guest.
Default policy for features provided by host CPU but not specified in domain configuration is set using match attribute of cpu tag. If 'minimum' match is requested, additional features will be treated as if they were specified with 'optional' policy. 'exact' match implies 'disable' policy and 'strict' match stands for 'forbid' policy.
Okay, I think this reflects the consensus from previous discussions about this. IMHO the nasty part is the set of feature names which is undefined and unbounded at the moment.
diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index 3e8944c..faeff4d 100644 --- a/docs/schemas/capability.rng +++ b/docs/schemas/capability.rng @@ -25,6 +25,9 @@ <optional> <ref name='cpufeatures'/> </optional> + <optional> + <ref name='cpuspec'/> + </optional> </element> <optional> <ref name='migration'/>
One thing to note is that the order of the elements here is fixed they must be present under <cpu> in the exact order given by the schemas (fine by me).
@@ -67,6 +70,28 @@ </element> </define>
+ <define name='cpuspec'> + <element name='model'> + <text/> + </element> + <element name='topology'> + <attribute name='sockets'> + <ref name='uint'/> + </attribute> + <attribute name='cores'> + <ref name='uint'/> + </attribute> + <attribute name='threads'> + <ref name='uint'/> + </attribute>
Maybe we want provide a new type excluding 0 here. by adding at the end <define name='positiveInteger'> <data type='positiveInteger'/> </define> referencing http://www.w3.org/TR/xmlschema-2/#positiveInteger since the grammar references datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" similary: <define name='uint'> <data type='unsignedInt'/> </define> would be nicer than the current definition.
+ </element> + <zeroOrMore> + <element name='feature'> + <text/> + </element> + </zeroOrMore> + </define> + <define name='migration'> <element name='migration_features'> <optional> @@ -259,6 +284,11 @@ <empty/> </element> </optional> + <optional> + <element name='cpu'>
I would go for <element name='cpuselection'> or similar here
+ <empty/> + </element> + </optional> </element> </define>
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 0a6ab61..eb91572 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -38,6 +38,9 @@ <optional> <ref name="seclabel"/> </optional> + <optional> + <ref name="cpu"/> + </optional> </interleave> </element> </define>
even though we are in an interleave, i.e. the order of elements under <domain> is not constrained, I would add the optional reference higher, it's more sensible to see those constraints after description or before <os>, so I would move that block a little up, even if this doesn't change the semantic... <note>Damn I'm pedantic this morning ...</note>
@@ -1193,6 +1196,52 @@ </optional> </define> <!-- + CPU specification + --> + <define name="cpu"> + <element name="cpu"> + <attribute name="match"> + <choice> + <value>minimum</value> + <value>exact</value> + <value>strict</value> + </choice> + </attribute> + <interleave> + <element name="model"> + <text/> + </element> + <optional> + <element name="topology"> + <attribute name="sockets"> + <ref name="uint"/> + </attribute> + <attribute name="cores"> + <ref name="uint"/> + </attribute> + <attribute name="threads"> + <ref name="uint"/>
similar we may want a positiveInteger here with a similar definition as 0 makes no sense for any of those values.
+ </attribute> + </element> + </optional> + <zeroOrMore> + <element name="feature"> + <attribute name="policy"> + <choice> + <value>force</value> + <value>require</value> + <value>optional</value> + <value>disable</value> + <value>forbid</value> + </choice> + </attribute>
+ <text/>
sight text is completely unconstrainted... And somehow I think I would prefer to have <attribute name="name"> <text/> </attribute> and keep feature content empty for the moment, which would allow more easilly to evolve the format for something more complex if the needs arise.
+ </element> + </zeroOrMore> + </interleave> + </element> + </define> + <!-- Type library
Our unsignedInt doesn't allow a leading '+' in its lexical form
Sounds good to me, a couple of suggestions on the format, naming and stucture, plus optional improvements on the types used. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- include/libvirt/virterror.h | 1 + src/Makefile.am | 9 +- src/conf/capabilities.c | 31 ++++- src/conf/capabilities.h | 6 + src/conf/cpu_conf.c | 345 +++++++++++++++++++++++++++++++++++++++++++ src/conf/cpu_conf.h | 110 ++++++++++++++ src/conf/domain_conf.c | 15 ++ src/conf/domain_conf.h | 2 + src/libvirt_private.syms | 8 + src/util/virterror.c | 3 + 10 files changed, 527 insertions(+), 3 deletions(-) create mode 100644 src/conf/cpu_conf.c create mode 100644 src/conf/cpu_conf.h diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index fa5cac4..4f819b2 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -68,6 +68,7 @@ typedef enum { VIR_FROM_ESX, /* Error from ESX driver */ VIR_FROM_PHYP, /* Error from IBM power hypervisor */ VIR_FROM_SECRET, /* Error from secret storage */ + VIR_FROM_CPU, /* Error from CPU driver */ } virErrorDomain; diff --git a/src/Makefile.am b/src/Makefile.am index 8e27ea7..17ed03f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -110,6 +110,9 @@ NODE_DEVICE_CONF_SOURCES = \ ENCRYPTION_CONF_SOURCES = \ conf/storage_encryption_conf.c conf/storage_encryption_conf.h +CPU_CONF_SOURCES = \ + conf/cpu_conf.c conf/cpu_conf.h + CONF_SOURCES = \ $(DOMAIN_CONF_SOURCES) \ $(DOMAIN_EVENT_SOURCES) \ @@ -118,7 +121,8 @@ CONF_SOURCES = \ $(STORAGE_CONF_SOURCES) \ $(ENCRYPTION_CONF_SOURCES) \ $(INTERFACE_CONF_SOURCES) \ - $(SECRET_CONF_SOURCES) + $(SECRET_CONF_SOURCES) \ + $(CPU_CONF_SOURCES) # The remote RPC driver, covering domains, storage, networks, etc REMOTE_DRIVER_SOURCES = \ @@ -819,7 +823,8 @@ libvirt_lxc_SOURCES = \ $(UTIL_SOURCES) \ $(NODE_INFO_SOURCES) \ $(ENCRYPTION_CONF_SOURCES) \ - $(DOMAIN_CONF_SOURCES) + $(DOMAIN_CONF_SOURCES) \ + $(CPU_CONF_SOURCES) libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) libvirt_lxc_LDADD = $(LIBXML_LIBS) $(NUMACTL_LIBS) ../gnulib/lib/libgnu.la libvirt_lxc_CFLAGS = \ diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 6ebddf5..98d7b0e 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -27,6 +27,7 @@ #include "buf.h" #include "memory.h" #include "util.h" +#include "cpu_conf.h" /** * virCapabilitiesNew: @@ -171,6 +172,8 @@ virCapabilitiesFree(virCapsPtr caps) { VIR_FREE(caps->host.arch); VIR_FREE(caps->host.secModel.model); VIR_FREE(caps->host.secModel.doi); + virCPUDefFree(caps->host.cpu); + VIR_FREE(caps); } @@ -263,6 +266,27 @@ virCapabilitiesAddHostNUMACell(virCapsPtr caps, return 0; } + +/** + * virCapabilitiesSetHostCPU: + * @caps: capabilities to extend + * @cpu: CPU definition + * + * Sets host CPU specification + */ +int +virCapabilitiesSetHostCPU(virCapsPtr caps, + virCPUDefPtr cpu) +{ + if (cpu == NULL) + return -1; + + caps->host.cpu = cpu; + + return 0; +} + + /** * virCapabilitiesAllocMachines: * @machines: machine variants for emulator ('pc', or 'isapc', etc) @@ -653,6 +677,10 @@ virCapabilitiesFormatXML(virCapsPtr caps) } virBufferAddLit(&xml, " </features>\n"); } + + virCPUDefFormatBuf(NULL, &xml, caps->host.cpu, " ", + VIR_CPU_FORMAT_EMBEDED); + virBufferAddLit(&xml, " </cpu>\n"); if (caps->host.offlineMigrate) { @@ -750,7 +778,8 @@ virCapabilitiesFormatXML(virCapsPtr caps) for (j = 0 ; j < caps->guests[i]->nfeatures ; j++) { if (STREQ(caps->guests[i]->features[j]->name, "pae") || STREQ(caps->guests[i]->features[j]->name, "nonpae") || - STREQ(caps->guests[i]->features[j]->name, "ia64_be")) { + STREQ(caps->guests[i]->features[j]->name, "ia64_be") || + STREQ(caps->guests[i]->features[j]->name, "cpu")) { virBufferVSprintf(&xml, " <%s/>\n", caps->guests[i]->features[j]->name); } else { diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 2f24605..48e293c 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -26,6 +26,7 @@ #include "internal.h" #include "util.h" +#include "cpu_conf.h" typedef struct _virCapsGuestFeature virCapsGuestFeature; typedef virCapsGuestFeature *virCapsGuestFeaturePtr; @@ -105,6 +106,7 @@ struct _virCapsHost { int nnumaCell; virCapsHostNUMACellPtr *numaCell; virCapsHostSecModel secModel; + virCPUDefPtr cpu; }; typedef struct _virCaps virCaps; @@ -159,6 +161,10 @@ virCapabilitiesAddHostNUMACell(virCapsPtr caps, const int *cpus); +extern int +virCapabilitiesSetHostCPU(virCapsPtr caps, + virCPUDefPtr cpu); + extern virCapsGuestMachinePtr * virCapabilitiesAllocMachines(const char *const *names, diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c new file mode 100644 index 0000000..3c100ee --- /dev/null +++ b/src/conf/cpu_conf.c @@ -0,0 +1,345 @@ +/* + * cpu_conf.h: CPU XML handling + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Jiri Denemark <jdenemar@redhat.com> + */ + +#include <config.h> + +#include "c-ctype.h" +#include "virterror_internal.h" +#include "memory.h" +#include "util.h" +#include "buf.h" +#include "cpu_conf.h" + +#define VIR_FROM_THIS VIR_FROM_CPU + +#define virCPUReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_CPU, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +VIR_ENUM_IMPL(virCPUMatch, VIR_CPU_MATCH_LAST, + "minimum", + "exact", + "strict") + +VIR_ENUM_IMPL(virCPUFeaturePolicy, VIR_CPU_FEATURE_LAST, + "force", + "require", + "optional", + "disable", + "forbid") + + +void +virCPUDefFree(virCPUDefPtr def) +{ + unsigned int i; + + if (!def) + return; + + VIR_FREE(def->model); + VIR_FREE(def->arch); + + for (i = 0 ; i < def->nfeatures ; i++) + VIR_FREE(def->features[i].name); + VIR_FREE(def->features); + + VIR_FREE(def); +} + + +virCPUDefPtr +virCPUDefParseXML(virConnectPtr conn, + const xmlNodePtr node, + xmlXPathContextPtr ctxt, + enum virCPUType mode) +{ + virCPUDefPtr def; + xmlNodePtr *nodes = NULL; + char *match; + int n; + unsigned int i; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(conn); + return NULL; + } + + match = virXMLPropString(node, "match"); + + if (mode == VIR_CPU_TYPE_AUTO) + def->type = (match == NULL) ? VIR_CPU_TYPE_HOST : VIR_CPU_TYPE_GUEST; + else + def->type = mode; + + if (def->type == VIR_CPU_TYPE_GUEST) { + if ((def->match = virCPUMatchTypeFromString(match)) < 0) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid match attribute for CPU specification")); + goto error; + } + } + + if (def->type == VIR_CPU_TYPE_HOST) { + def->arch = virXPathString(conn, "string(./arch[1])", ctxt); + if (!def->arch) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing CPU architecture")); + goto error; + } + } + + if (!(def->model = virXPathString(conn, "string(./model[1])", ctxt))) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing CPU model name")); + goto error; + } + + if (virXPathNode(conn, "./topology[1]", ctxt)) { + int ret; + unsigned long ul; + int all_zero; + + ret = virXPathULong(conn, "string(./topology[1]/@sockets)", + ctxt, &ul); + if (ret < 0) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing 'sockets' attribute in CPU topology")); + goto error; + } + def->sockets = (unsigned int) ul; + all_zero = !def->sockets; + + ret = virXPathULong(conn, "string(./topology[1]/@cores)", + ctxt, &ul); + if (ret < 0) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing 'cores' attribute in CPU topology")); + goto error; + } + def->cores = (unsigned int) ul; + all_zero &= !def->cores; + + ret = virXPathULong(conn, "string(./topology[1]/@threads)", + ctxt, &ul); + if (ret < 0) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing 'threads' attribute in CPU topology")); + goto error; + } + def->threads = (unsigned int) ul; + all_zero &= !def->threads; + + /* + * sockets, cores, and threads must all be either zero or nonzero; + * if all of them are zero, we don't care about topology + */ + if ((!def->sockets || !def->cores || !def->threads) && !all_zero) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid CPU topology")); + goto error; + } + } + + n = virXPathNodeSet(conn, "./feature", ctxt, &nodes); + if (n < 0) + goto error; + + if (n > 0) { + if (VIR_ALLOC_N(def->features, n) < 0) + goto no_memory; + def->nfeatures = n; + } + + for (i = 0 ; i < n ; i++) { + char *name; + int policy; /* enum virDomainCPUFeaturePolicy */ + unsigned int j; + unsigned int len; + + if (def->type == VIR_CPU_TYPE_GUEST) { + char *strpolicy; + + strpolicy = virXMLPropString(nodes[i], "policy"); + policy = virCPUFeaturePolicyTypeFromString(strpolicy); + VIR_FREE(strpolicy); + + if (policy < 0) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid CPU feature policy")); + goto error; + } + } + else + policy = -1; + + if (!(name = (char *) xmlNodeGetContent(nodes[i])) + || *name == 0) { + VIR_FREE(name); + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid CPU feature name")); + goto error; + } + + len = strlen(name); + for (j = 0 ; j < len ; j++) + name[j] = c_tolower(name[j]); + + for (j = 0 ; j < i ; j++) { + if (STREQ(name, def->features[j].name)) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("CPU feature `%s' specified more than once"), + name); + VIR_FREE(name); + goto error; + } + } + + def->features[i].name = name; + def->features[i].policy = policy; + } + +cleanup: + VIR_FREE(match); + VIR_FREE(nodes); + + return def; + +no_memory: + virReportOOMError(conn); + +error: + virCPUDefFree(def); + def = NULL; + goto cleanup; +} + + +char * +virCPUDefFormat(virConnectPtr conn, + virCPUDefPtr def, + const char *indent, + int flags) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *tmp; + + if (virCPUDefFormatBuf(conn, &buf, def, indent, flags) < 0) + goto cleanup; + + if (virBufferError(&buf)) + goto no_memory; + + return virBufferContentAndReset(&buf); + +no_memory: + virReportOOMError(conn); +cleanup: + tmp = virBufferContentAndReset(&buf); + VIR_FREE(tmp); + return NULL; +} + + +int +virCPUDefFormatBuf(virConnectPtr conn, + virBufferPtr buf, + virCPUDefPtr def, + const char *indent, + int flags) +{ + unsigned int i; + + if (!def) + return 0; + + if (indent == NULL) + indent = ""; + + if (!def->model) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing CPU model")); + return -1; + } + + if (!(flags & VIR_CPU_FORMAT_EMBEDED)) { + if (def->type == VIR_CPU_TYPE_GUEST) { + const char *match; + if (!(match = virCPUMatchTypeToString(def->match))) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Unexpected CPU match policy %d"), def->match); + return -1; + } + + virBufferVSprintf(buf, "%s<cpu match='%s'>\n", indent, match); + } + else + virBufferVSprintf(buf, "%s<cpu>\n", indent); + + if (def->arch) + virBufferVSprintf(buf, "%s <arch>%s</arch>\n", indent, def->arch); + } + + virBufferVSprintf(buf, "%s <model>%s</model>\n", indent, def->model); + + if (def->sockets && def->cores && def->threads) { + virBufferVSprintf(buf, "%s <topology", indent); + virBufferVSprintf(buf, " sockets='%u'", def->sockets); + virBufferVSprintf(buf, " cores='%u'", def->cores); + virBufferVSprintf(buf, " threads='%u'", def->threads); + virBufferAddLit(buf, "/>\n"); + } + + for (i = 0 ; i < def->nfeatures ; i++) { + virCPUFeatureDefPtr feature = def->features + i; + + if (!feature->name) { + virCPUReportError(conn, 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) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Unexpected CPU feature policy %d"), feature->policy); + return -1; + } + virBufferVSprintf(buf, "%s <feature policy='%s'>%s</feature>\n", + indent, policy, feature->name); + } + else { + virBufferVSprintf(buf, "%s <feature>%s</feature>\n", + indent, feature->name); + } + } + + if (!(flags & VIR_CPU_FORMAT_EMBEDED)) + virBufferVSprintf(buf, "%s</cpu>\n", indent); + + return 0; +} + diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h new file mode 100644 index 0000000..3629d90 --- /dev/null +++ b/src/conf/cpu_conf.h @@ -0,0 +1,110 @@ +/* + * cpu_conf.h: CPU XML handling + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Jiri Denemark <jdenemar@redhat.com> + */ + +#ifndef __VIR_CPU_CONF_H__ +#define __VIR_CPU_CONF_H__ + +#include "util.h" +#include "buf.h" +#include "xml.h" + + +enum virCPUType { + VIR_CPU_TYPE_HOST, + VIR_CPU_TYPE_GUEST, + VIR_CPU_TYPE_AUTO +}; + +enum virCPUMatch { + VIR_CPU_MATCH_MINIMUM, + VIR_CPU_MATCH_EXACT, + VIR_CPU_MATCH_STRICT, + + VIR_CPU_MATCH_LAST +}; + +VIR_ENUM_DECL(virCPUMatch) + +enum virCPUFeaturePolicy { + VIR_CPU_FEATURE_FORCE, + VIR_CPU_FEATURE_REQUIRE, + VIR_CPU_FEATURE_OPTIONAL, + VIR_CPU_FEATURE_DISABLE, + VIR_CPU_FEATURE_FORBID, + + VIR_CPU_FEATURE_LAST +}; + +VIR_ENUM_DECL(virCPUFeaturePolicy) + +typedef struct _virCPUFeatureDef virCPUFeatureDef; +typedef virCPUFeatureDef *virCPUFeatureDefPtr; +struct _virCPUFeatureDef { + char *name; + int policy; /* enum virCPUFeaturePolicy */ +}; + +typedef struct _virCPUDef virCPUDef; +typedef virCPUDef *virCPUDefPtr; +struct _virCPUDef { + int type; /* enum virCPUType */ + int match; /* enum virCPUMatch */ + char *arch; + char *model; + unsigned int sockets; + unsigned int cores; + unsigned int threads; + unsigned int nfeatures; + virCPUFeatureDefPtr features; +}; + + +void +virCPUDefFree(virCPUDefPtr def); + +virCPUDefPtr +virCPUDefParseXML(virConnectPtr conn, + const xmlNodePtr node, + xmlXPathContextPtr ctxt, + enum virCPUType mode); + +enum virCPUFormatFlags { + VIR_CPU_FORMAT_EMBEDED = (1 << 0) /* embed into existing <cpu/> element + * in host capabilities */ +}; + + +char * +virCPUDefFormat(virConnectPtr conn, + virCPUDefPtr def, + const char *indent, + int flags); + +int +virCPUDefFormatBuf(virConnectPtr conn, + virBufferPtr buf, + virCPUDefPtr def, + const char *indent, + int flags); + +#endif /* __VIR_CPU_CONF_H__ */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index de07e13..5a62712 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -577,6 +577,8 @@ void virDomainDefFree(virDomainDefPtr def) virSecurityLabelDefFree(def); + virCPUDefFree(def->cpu); + VIR_FREE(def); } @@ -3160,6 +3162,16 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, if (virSecurityLabelDefParseXML(conn, def, ctxt, flags) == -1) goto error; + if ((node = virXPathNode(conn, "./cpu[1]", ctxt)) != NULL) { + xmlNodePtr oldnode = ctxt->node; + ctxt->node = node; + def->cpu = virCPUDefParseXML(conn, node, ctxt, VIR_CPU_TYPE_GUEST); + ctxt->node = oldnode; + + if (def->cpu == NULL) + goto error; + } + return def; no_memory: @@ -4452,6 +4464,9 @@ char *virDomainDefFormat(virConnectPtr conn, virBufferAddLit(&buf, " </features>\n"); } + if (virCPUDefFormatBuf(conn, &buf, def->cpu, " ", 0) < 0) + goto cleanup; + virBufferVSprintf(&buf, " <clock offset='%s'/>\n", def->localtime ? "localtime" : "utc"); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 389e259..5ff4330 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -31,6 +31,7 @@ #include "internal.h" #include "capabilities.h" #include "storage_encryption_conf.h" +#include "cpu_conf.h" #include "util.h" #include "threads.h" #include "hash.h" @@ -614,6 +615,7 @@ struct _virDomainDef { virDomainChrDefPtr console; virSecurityLabelDef seclabel; virDomainWatchdogDefPtr watchdog; + virCPUDefPtr cpu; }; /* Guest VM runtime state */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3997704..e668d4b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -33,6 +33,7 @@ virCapabilitiesSetEmulatorRequired; virCapabilitiesIsEmulatorRequired; virCapabilitiesAllocMachines; virCapabilitiesFreeMachines; +virCapabilitiesSetHostCPU; # conf.h @@ -84,6 +85,13 @@ virGetStream; virUnrefStream; +# cpu_conf.h +virCPUDefFree; +virCPUDefParseXML; +virCPUDefFormat; +virCPUDefFormatBuf; + + # domain_conf.h virDiskNameToBusDeviceIndex; virDiskNameToIndex; diff --git a/src/util/virterror.c b/src/util/virterror.c index 10f979c..ff863ea 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -172,6 +172,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_SECRET: dom = "Secret Storage "; break; + case VIR_FROM_CPU: + dom = "CPU "; + break; } return(dom); } -- 1.6.5.2

On Tue, Nov 03, 2009 at 04:40:01PM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- include/libvirt/virterror.h | 1 + src/Makefile.am | 9 +- src/conf/capabilities.c | 31 ++++- src/conf/capabilities.h | 6 + src/conf/cpu_conf.c | 345 +++++++++++++++++++++++++++++++++++++++++++ src/conf/cpu_conf.h | 110 ++++++++++++++ src/conf/domain_conf.c | 15 ++ src/conf/domain_conf.h | 2 + src/libvirt_private.syms | 8 + src/util/virterror.c | 3 + 10 files changed, 527 insertions(+), 3 deletions(-) create mode 100644 src/conf/cpu_conf.c create mode 100644 src/conf/cpu_conf.h
[...]
diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c [...] + /* + * sockets, cores, and threads must all be either zero or nonzero; + * if all of them are zero, we don't care about topology + */ + if ((!def->sockets || !def->cores || !def->threads) && !all_zero) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid CPU topology")); + goto error; + } + }
Hum, I would just ban all being 0 and drop the element in that case which would match my suggestion at the schemas level.
+ n = virXPathNodeSet(conn, "./feature", ctxt, &nodes); + if (n < 0) + goto error; + + if (n > 0) { + if (VIR_ALLOC_N(def->features, n) < 0) + goto no_memory; + def->nfeatures = n; + } + + for (i = 0 ; i < n ; i++) { + char *name; [...] + + if (!(name = (char *) xmlNodeGetContent(nodes[i])) + || *name == 0) { + VIR_FREE(name); + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid CPU feature name")); + goto error; + }
let's avoid xmlNodeGetContent() and just use the name in an attribute it's better and simpler at this point, plus better if we want this construct to evolve in the future.
+ len = strlen(name); + for (j = 0 ; j < len ; j++) + name[j] = c_tolower(name[j]);
hum ... are we really normalizing features names ? On one hand we don't define the list, but on the other hand we lower-case them. Sounds a bit weird, either we control them or not. Patch looks fine overall, just those could of suggestions especially about matching the proposed changes in the schemas and avoiding tolower() Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (2)
-
Daniel Veillard
-
Jiri Denemark