[libvirt] [PATCH v3 00/13] CPU selection patches

Hi, This is a third version of CPU selection patchset. Changes in version 3: - fix build and segfault on i386 Changes in version 2: - virConnectGetHostCPU() API call was completely removed - 'CPU driver implementation' (11/14) patch was dropped - virConnectCompareCPU() API call is implemented directly by hypervisor drivers - new cpuCompareXML() internal function to make virConnectCompareCPU() simpler Jirka Jiri Denemark (13): XML schema for CPU flags XML parsing/formating code for CPU flags Public API Internal driver API Public API implementation Wire protocol format Remote driver Server side dispatcher CPU selection infrastructure CPU map for models and features Set cpuCompare handler in drivers Implement CPU selection in QEMU driver Implement virsh commands configure.in | 20 + daemon/remote.c | 22 + docs/schemas/capability.rng | 46 ++- docs/schemas/domain.rng | 62 +++ include/libvirt/libvirt.h.in | 13 + include/libvirt/virterror.h | 1 + src/Makefile.am | 23 +- src/conf/capabilities.c | 31 ++- src/conf/capabilities.h | 6 + src/conf/cpu_conf.c | 366 +++++++++++++ src/conf/cpu_conf.h | 116 +++++ src/conf/domain_conf.c | 15 + src/conf/domain_conf.h | 2 + src/cpu/cpu.c | 242 +++++++++ src/cpu/cpu.h | 129 +++++ src/cpu/cpu_generic.c | 123 +++++ src/cpu/cpu_generic.h | 32 ++ src/cpu/cpu_map.c | 130 +++++ src/cpu/cpu_map.h | 41 ++ src/cpu/cpu_map.xml | 304 +++++++++++ src/cpu/cpu_x86.c | 1159 ++++++++++++++++++++++++++++++++++++++++++ src/cpu/cpu_x86.h | 31 ++ src/cpu/cpu_x86_data.h | 45 ++ src/driver.h | 5 + src/esx/esx_driver.c | 1 + src/libvirt.c | 41 ++ src/libvirt_private.syms | 19 + src/libvirt_public.syms | 5 + src/lxc/lxc_driver.c | 1 + src/openvz/openvz_driver.c | 1 + src/qemu/qemu_conf.c | 401 ++++++++++++++- src/qemu/qemu_conf.h | 7 + src/qemu/qemu_driver.c | 43 ++- src/remote/remote_driver.c | 27 + src/remote/remote_protocol.x | 12 +- src/test/test_driver.c | 1 + src/uml/uml_driver.c | 1 + src/util/virterror.c | 3 + src/vbox/vbox_tmpl.c | 1 + tools/virsh.c | 65 +++ 40 files changed, 3558 insertions(+), 35 deletions(-) create mode 100644 src/conf/cpu_conf.c create mode 100644 src/conf/cpu_conf.h create mode 100644 src/cpu/cpu.c create mode 100644 src/cpu/cpu.h create mode 100644 src/cpu/cpu_generic.c create mode 100644 src/cpu/cpu_generic.h create mode 100644 src/cpu/cpu_map.c create mode 100644 src/cpu/cpu_map.h create mode 100644 src/cpu/cpu_map.xml create mode 100644 src/cpu/cpu_x86.c create mode 100644 src/cpu/cpu_x86.h create mode 100644 src/cpu/cpu_x86_data.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="NAME"/> </cpu> ... </host> Secondly, drivers which support detailed CPU specification have to advertise it in guest capabilities: <guest> ... <features> <cpuselection/> </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="NAME"/> </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 | 46 +++++++++++++++++++++++++++++++- docs/schemas/domain.rng | 62 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletions(-) diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index 3e8944c..378652e 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,31 @@ </element> </define> + <define name='cpuspec'> + <element name='model'> + <text/> + </element> + <element name='topology'> + <attribute name='sockets'> + <ref name='positiveInteger'/> + </attribute> + <attribute name='cores'> + <ref name='positiveInteger'/> + </attribute> + <attribute name='threads'> + <ref name='positiveInteger'/> + </attribute> + </element> + <zeroOrMore> + <element name='feature'> + <attribute name='name'> + <ref name='featureName'/> + </attribute> + <empty/> + </element> + </zeroOrMore> + </define> + <define name='migration'> <element name='migration_features'> <optional> @@ -259,6 +287,11 @@ <empty/> </element> </optional> + <optional> + <element name='cpuselection'> + <empty/> + </element> + </optional> </element> </define> @@ -293,8 +326,14 @@ </define> + <define name='positiveInteger'> + <data type='positiveInteger'> + <param name="pattern">[0-9]+</param> + </data> + </define> + <define name='uint'> - <data type='string'> + <data type='unsignedInt'> <param name="pattern">[0-9]+</param> </data> </define> @@ -305,4 +344,9 @@ </data> </define> + <define name='featureName'> + <data type='string'> + <param name='pattern'>[a-zA-Z0-9\-_]+</param> + </data> + </define> </grammar> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 784f9b1..566b117 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -27,6 +27,9 @@ <optional> <ref name="description"/> </optional> + <optional> + <ref name="cpu"/> + </optional> <ref name="os"/> <ref name="clock"/> <ref name="resources"/> @@ -1235,6 +1238,55 @@ </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="positiveInteger"/> + </attribute> + <attribute name="cores"> + <ref name="positiveInteger"/> + </attribute> + <attribute name="threads"> + <ref name="positiveInteger"/> + </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> + <attribute name="name"> + <ref name="featureName"/> + </attribute> + <empty/> + </element> + </zeroOrMore> + </interleave> + </element> + </define> + <!-- Type library Our unsignedInt doesn't allow a leading '+' in its lexical form @@ -1247,6 +1299,11 @@ <param name="pattern">[0-9]+</param> </data> </define> + <define name='positiveInteger'> + <data type='positiveInteger'> + <param name="pattern">[0-9]+</param> + </data> + </define> <define name="countCPU"> <data type="unsignedShort"> <param name="pattern">[0-9]+</param> @@ -1354,4 +1411,9 @@ <param name="pattern">(0x)?[0-7]</param> </data> </define> + <define name="featureName"> + <data type="string"> + <param name='pattern'>[a-zA-Z0-9\-_]+</param> + </data> + </define> </grammar> -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:03:58AM +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="NAME"/> </cpu> ... </host>
Secondly, drivers which support detailed CPU specification have to advertise it in guest capabilities:
<guest> ... <features> <cpuselection/> </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="NAME"/> </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 | 46 +++++++++++++++++++++++++++++++- docs/schemas/domain.rng | 62 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletions(-)
diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index 3e8944c..378652e 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,31 @@ </element> </define>
+ <define name='cpuspec'> + <element name='model'> + <text/> + </element> + <element name='topology'> + <attribute name='sockets'> + <ref name='positiveInteger'/> + </attribute> + <attribute name='cores'> + <ref name='positiveInteger'/> + </attribute> + <attribute name='threads'> + <ref name='positiveInteger'/> + </attribute> + </element> + <zeroOrMore> + <element name='feature'> + <attribute name='name'> + <ref name='featureName'/> + </attribute> + <empty/> + </element> + </zeroOrMore> + </define> + <define name='migration'> <element name='migration_features'> <optional> @@ -259,6 +287,11 @@ <empty/> </element> </optional> + <optional> + <element name='cpuselection'> + <empty/> + </element> + </optional> </element> </define>
@@ -293,8 +326,14 @@ </define>
+ <define name='positiveInteger'> + <data type='positiveInteger'> + <param name="pattern">[0-9]+</param> + </data> + </define> + <define name='uint'> - <data type='string'> + <data type='unsignedInt'> <param name="pattern">[0-9]+</param> </data> </define>
Hum, why do you change this ? But basically if you use http://www.w3.org/TR/xmlschema-2/#unsignedInt as the base type then the pattern restriction is superfluous.
@@ -305,4 +344,9 @@ </data> </define>
+ <define name='featureName'> + <data type='string'> + <param name='pattern'>[a-zA-Z0-9\-_]+</param> + </data> + </define> </grammar> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng [...] + <define name='positiveInteger'> + <data type='positiveInteger'> + <param name="pattern">[0-9]+</param> + </data> + </define>
Same here, but it's nitpick, it should work as is, ACK, 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/

<define name='uint'> - <data type='string'> + <data type='unsignedInt'> <param name="pattern">[0-9]+</param> </data> </define>
Hum, why do you change this ?
Because you wanted me to do so last time you reviewed this patch :-)
But basically if you use http://www.w3.org/TR/xmlschema-2/#unsignedInt as the base type then the pattern restriction is superfluous.
It's derived (transitively) from nonNegativeInteger by setting maxInclusive. Since, nonNegativeInteger allows '+' sign at the beginning and I wanted to disallow that. Perhaps I'm reading that specification in a wrong way...
[...]
+ <define name='positiveInteger'> + <data type='positiveInteger'> + <param name="pattern">[0-9]+</param> + </data> + </define>
Same here, but it's nitpick, it should work as is, ACK,
Thanks. Jirka

On Wed, Dec 16, 2009 at 04:24:52PM +0100, Jiri Denemark wrote:
<define name='uint'> - <data type='string'> + <data type='unsignedInt'> <param name="pattern">[0-9]+</param> </data> </define>
Hum, why do you change this ?
Because you wanted me to do so last time you reviewed this patch :-)
But basically if you use http://www.w3.org/TR/xmlschema-2/#unsignedInt as the base type then the pattern restriction is superfluous.
It's derived (transitively) from nonNegativeInteger by setting maxInclusive. Since, nonNegativeInteger allows '+' sign at the beginning and I wanted to disallow that. Perhaps I'm reading that specification in a wrong way...
Ah, right, my mistake :-)
[...]
+ <define name='positiveInteger'> + <data type='positiveInteger'> + <param name="pattern">[0-9]+</param> + </data> + </define>
Same here, but it's nitpick, it should work as is, ACK,
Thanks.
Sorry :-) ACK ! 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 | 366 +++++++++++++++++++++++++++++++++++++++++++ src/conf/cpu_conf.h | 116 ++++++++++++++ src/conf/domain_conf.c | 15 ++ src/conf/domain_conf.h | 2 + src/libvirt_private.syms | 9 + src/util/virterror.c | 3 + 10 files changed, 555 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 8548a8d..30f88e8 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 b639915..432a66e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,6 +114,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) \ @@ -122,7 +125,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 = \ @@ -831,7 +835,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) $(YAJL_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 476bba6..47976af 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, "cpuselection")) { virBufferVSprintf(&xml, " <%s/>\n", caps->guests[i]->features[j]->name); } else { diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index f8cbae6..8c20cd4 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -27,6 +27,7 @@ #include "internal.h" #include "util.h" #include "buf.h" +#include "cpu_conf.h" #include <libxml/xpath.h> @@ -108,6 +109,7 @@ struct _virCapsHost { int nnumaCell; virCapsHostNUMACellPtr *numaCell; virCapsHostSecModel secModel; + virCPUDefPtr cpu; }; typedef struct _virCaps virCaps; @@ -166,6 +168,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..8286eb6 --- /dev/null +++ b/src/conf/cpu_conf.c @@ -0,0 +1,366 @@ +/* + * 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; + + 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; + + 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; + + 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; + + if (!def->sockets || !def->cores || !def->threads) { + 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; + + 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 = virXMLPropString(nodes[i], "name")) || *name == 0) { + VIR_FREE(name); + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid CPU feature name")); + goto error; + } + + 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' name='%s'/>\n", + indent, policy, feature->name); + } + else { + virBufferVSprintf(buf, "%s <feature name='%s'/>\n", + indent, feature->name); + } + } + + if (!(flags & VIR_CPU_FORMAT_EMBEDED)) + virBufferVSprintf(buf, "%s</cpu>\n", indent); + + return 0; +} + + +int +virCPUDefAddFeature(virConnectPtr conn, + virCPUDefPtr def, + const char *name, + int policy) +{ + int i; + + for (i = 0 ; i < def->nfeatures ; i++) { + if (STREQ(name, def->features[i].name)) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("CPU feature `%s' specified more than once"), name); + return -1; + } + } + + if (VIR_REALLOC_N(def->features, def->nfeatures + 1) < 0) + goto no_memory; + + if (def->type == VIR_CPU_TYPE_HOST) + policy = -1; + + if (!(def->features[def->nfeatures].name = strdup(name))) + goto no_memory; + + def->features[def->nfeatures].policy = policy; + def->nfeatures++; + + return 0; + +no_memory: + virReportOOMError(conn); + return -1; +} diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h new file mode 100644 index 0000000..c71fbbb --- /dev/null +++ b/src/conf/cpu_conf.h @@ -0,0 +1,116 @@ +/* + * 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); + +int +virCPUDefAddFeature(virConnectPtr conn, + virCPUDefPtr cpu, + const char *name, + int policy); + +#endif /* __VIR_CPU_CONF_H__ */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5e94edf..0b4fe8b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -601,6 +601,8 @@ void virDomainDefFree(virDomainDefPtr def) virSecurityLabelDefFree(def); + virCPUDefFree(def->cpu); + VIR_FREE(def); } @@ -3360,6 +3362,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: @@ -4660,6 +4672,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 ac39dcd..a807e9d 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" @@ -635,6 +636,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 e5ba365..9acd062 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -35,6 +35,7 @@ virCapabilitiesSetEmulatorRequired; virCapabilitiesIsEmulatorRequired; virCapabilitiesAllocMachines; virCapabilitiesFreeMachines; +virCapabilitiesSetHostCPU; # conf.h @@ -70,6 +71,14 @@ virCgroupGetFreezerState; virCgroupSetFreezerState; +# cpu_conf.h +virCPUDefFree; +virCPUDefParseXML; +virCPUDefFormat; +virCPUDefFormatBuf; +virCPUDefAddFeature; + + # datatypes.h virGetDomain; virGetInterface; diff --git a/src/util/virterror.c b/src/util/virterror.c index 3ff02ba..b1a96f8 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.6

On Wed, Dec 16, 2009 at 12:03:59AM +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 | 366 +++++++++++++++++++++++++++++++++++++++++++ src/conf/cpu_conf.h | 116 ++++++++++++++ src/conf/domain_conf.c | 15 ++ src/conf/domain_conf.h | 2 + src/libvirt_private.syms | 9 + src/util/virterror.c | 3 + 10 files changed, 555 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 8548a8d..30f88e8 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 b639915..432a66e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,6 +114,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) \ @@ -122,7 +125,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 = \ @@ -831,7 +835,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) $(YAJL_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 476bba6..47976af 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, "cpuselection")) { virBufferVSprintf(&xml, " <%s/>\n", caps->guests[i]->features[j]->name); } else { diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index f8cbae6..8c20cd4 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -27,6 +27,7 @@ #include "internal.h" #include "util.h" #include "buf.h" +#include "cpu_conf.h"
#include <libxml/xpath.h>
@@ -108,6 +109,7 @@ struct _virCapsHost { int nnumaCell; virCapsHostNUMACellPtr *numaCell; virCapsHostSecModel secModel; + virCPUDefPtr cpu; };
typedef struct _virCaps virCaps; @@ -166,6 +168,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..8286eb6 --- /dev/null +++ b/src/conf/cpu_conf.c @@ -0,0 +1,366 @@ +/* + * 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; + + 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; + + 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; + + 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; + + if (!def->sockets || !def->cores || !def->threads) { + 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; + + 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 = virXMLPropString(nodes[i], "name")) || *name == 0) { + VIR_FREE(name); + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid CPU feature name")); + goto error; + } + + 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' name='%s'/>\n", + indent, policy, feature->name); + } + else { + virBufferVSprintf(buf, "%s <feature name='%s'/>\n", + indent, feature->name); + } + } + + if (!(flags & VIR_CPU_FORMAT_EMBEDED)) + virBufferVSprintf(buf, "%s</cpu>\n", indent); + + return 0; +} + + +int +virCPUDefAddFeature(virConnectPtr conn, + virCPUDefPtr def, + const char *name, + int policy) +{ + int i; + + for (i = 0 ; i < def->nfeatures ; i++) { + if (STREQ(name, def->features[i].name)) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("CPU feature `%s' specified more than once"), name); + return -1; + } + } + + if (VIR_REALLOC_N(def->features, def->nfeatures + 1) < 0) + goto no_memory; + + if (def->type == VIR_CPU_TYPE_HOST) + policy = -1; + + if (!(def->features[def->nfeatures].name = strdup(name))) + goto no_memory; + + def->features[def->nfeatures].policy = policy; + def->nfeatures++; + + return 0; + +no_memory: + virReportOOMError(conn); + return -1; +} diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h new file mode 100644 index 0000000..c71fbbb --- /dev/null +++ b/src/conf/cpu_conf.h @@ -0,0 +1,116 @@ +/* + * 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); + +int +virCPUDefAddFeature(virConnectPtr conn, + virCPUDefPtr cpu, + const char *name, + int policy); + +#endif /* __VIR_CPU_CONF_H__ */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5e94edf..0b4fe8b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -601,6 +601,8 @@ void virDomainDefFree(virDomainDefPtr def)
virSecurityLabelDefFree(def);
+ virCPUDefFree(def->cpu); + VIR_FREE(def); }
@@ -3360,6 +3362,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: @@ -4660,6 +4672,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 ac39dcd..a807e9d 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" @@ -635,6 +636,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 e5ba365..9acd062 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -35,6 +35,7 @@ virCapabilitiesSetEmulatorRequired; virCapabilitiesIsEmulatorRequired; virCapabilitiesAllocMachines; virCapabilitiesFreeMachines; +virCapabilitiesSetHostCPU;
# conf.h @@ -70,6 +71,14 @@ virCgroupGetFreezerState; virCgroupSetFreezerState;
+# cpu_conf.h +virCPUDefFree; +virCPUDefParseXML; +virCPUDefFormat; +virCPUDefFormatBuf; +virCPUDefAddFeature; + + # datatypes.h virGetDomain; virGetInterface; diff --git a/src/util/virterror.c b/src/util/virterror.c index 3ff02ba..b1a96f8 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); }
ACK, I was wondering for a bit about the need for a new set of files, but it's a good idea to keep modules smaller, 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/libvirt.h.in | 13 +++++++++++++ src/libvirt_public.syms | 5 +++++ 2 files changed, 18 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6c3aded..473d2a2 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1676,6 +1676,19 @@ int virInterfaceIsActive(virInterfacePtr iface); int virConnectIsEncrypted(virConnectPtr conn); int virConnectIsSecure(virConnectPtr conn); +/* + * CPU specification API + */ + +typedef enum { + VIR_CPU_COMPARE_ERROR = -1, + VIR_CPU_COMPARE_INCOMPATIBLE = 0, + VIR_CPU_COMPARE_IDENTICAL = 1, + VIR_CPU_COMPARE_SUPERSET = 2 +} virCPUCompareResult; + +int virConnectCompareCPU(virConnectPtr conn, + const char *xmlDesc); #ifdef __cplusplus diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b4f57e7..538b4d4 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -343,4 +343,9 @@ LIBVIRT_0.7.3 { virInterfaceIsActive; } LIBVIRT_0.7.2; +LIBVIRT_0.7.5 { + global: + virConnectCompareCPU; +} LIBVIRT_0.7.3; + # .... define new API here using predicted next version number .... -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:04:00AM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- include/libvirt/libvirt.h.in | 13 +++++++++++++ src/libvirt_public.syms | 5 +++++ 2 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6c3aded..473d2a2 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1676,6 +1676,19 @@ int virInterfaceIsActive(virInterfacePtr iface); int virConnectIsEncrypted(virConnectPtr conn); int virConnectIsSecure(virConnectPtr conn);
+/* + * CPU specification API + */ + +typedef enum { + VIR_CPU_COMPARE_ERROR = -1, + VIR_CPU_COMPARE_INCOMPATIBLE = 0, + VIR_CPU_COMPARE_IDENTICAL = 1, + VIR_CPU_COMPARE_SUPERSET = 2 +} virCPUCompareResult; + +int virConnectCompareCPU(virConnectPtr conn, + const char *xmlDesc);
Hum, I would add an unsigned int flags there for potential evolution of the semantic. It can be maked as unused in the libvirt.c front-end if you don't want to pass it down to drivers yet minimizing the changes.
#ifdef __cplusplus diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b4f57e7..538b4d4 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -343,4 +343,9 @@ LIBVIRT_0.7.3 { virInterfaceIsActive; } LIBVIRT_0.7.2;
+LIBVIRT_0.7.5 { + global: + virConnectCompareCPU; +} LIBVIRT_0.7.3; + # .... define new API here using predicted next version number ....
ACK with the flags or with a good justification against. -- 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/

On Wed, Dec 16, 2009 at 12:04:00AM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- include/libvirt/libvirt.h.in | 13 +++++++++++++ src/libvirt_public.syms | 5 +++++ 2 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6c3aded..473d2a2 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1676,6 +1676,19 @@ int virInterfaceIsActive(virInterfacePtr iface); int virConnectIsEncrypted(virConnectPtr conn); int virConnectIsSecure(virConnectPtr conn);
+/* + * CPU specification API + */ + +typedef enum { + VIR_CPU_COMPARE_ERROR = -1, + VIR_CPU_COMPARE_INCOMPATIBLE = 0, + VIR_CPU_COMPARE_IDENTICAL = 1, + VIR_CPU_COMPARE_SUPERSET = 2 +} virCPUCompareResult; + +int virConnectCompareCPU(virConnectPtr conn, + const char *xmlDesc);
Which is the XML format expected here ? Is this allowing the guest XML data, or just the <cpu> part of it ? Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

+/* + * CPU specification API + */ + +typedef enum { + VIR_CPU_COMPARE_ERROR = -1, + VIR_CPU_COMPARE_INCOMPATIBLE = 0, + VIR_CPU_COMPARE_IDENTICAL = 1, + VIR_CPU_COMPARE_SUPERSET = 2 +} virCPUCompareResult; + +int virConnectCompareCPU(virConnectPtr conn, + const char *xmlDesc);
Which is the XML format expected here ? Is this allowing the guest XML data, or just the <cpu> part of it ?
Just the <cpu> part of either guest or host data. Jirka

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/driver.h | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 09ce8e2..c9177fa 100644 --- a/src/driver.h +++ b/src/driver.h @@ -349,6 +349,10 @@ typedef int typedef int (*virDrvDomainIsPersistent)(virDomainPtr dom); +typedef int + (*virDrvCPUCompare)(virConnectPtr conn, + const char *cpu); + /** * _virDriver: * @@ -435,6 +439,7 @@ struct _virDriver { virDrvConnectIsSecure isSecure; virDrvDomainIsActive domainIsActive; virDrvDomainIsPersistent domainIsPersistent; + virDrvCPUCompare cpuCompare; }; typedef int -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:04:01AM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/driver.h | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/src/driver.h b/src/driver.h index 09ce8e2..c9177fa 100644 --- a/src/driver.h +++ b/src/driver.h @@ -349,6 +349,10 @@ typedef int typedef int (*virDrvDomainIsPersistent)(virDomainPtr dom);
+typedef int + (*virDrvCPUCompare)(virConnectPtr conn, + const char *cpu); + /** * _virDriver: * @@ -435,6 +439,7 @@ struct _virDriver { virDrvConnectIsSecure isSecure; virDrvDomainIsActive domainIsActive; virDrvDomainIsPersistent domainIsPersistent; + virDrvCPUCompare cpuCompare; };
typedef int
ACK, unless we push the flags there too at this point, 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> --- src/libvirt.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 41 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 103b331..4d9cb33 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -10706,3 +10706,44 @@ error: virSetConnError(conn); return -1; } + + +/** + * virConnectCompareCPU: + * @conn: virConnect connection + * @xml: XML describing the CPU to compare with host CPU + * + * Returns comparison result according to enum virCPUCompareResult + */ +int +virConnectCompareCPU(virConnectPtr conn, const char *xmlDesc) +{ + VIR_DEBUG("conn=%p, xmlDesc=%s", conn, xmlDesc); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return VIR_CPU_COMPARE_ERROR; + } + if (xmlDesc == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->driver->cpuCompare) { + int ret; + + ret = conn->driver->cpuCompare(conn, xmlDesc); + if (ret == VIR_CPU_COMPARE_ERROR) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatibility */ + virSetConnError(conn); + return VIR_CPU_COMPARE_ERROR; +} -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:04:02AM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/libvirt.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 41 insertions(+), 0 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index 103b331..4d9cb33 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -10706,3 +10706,44 @@ error: virSetConnError(conn); return -1; } + + +/** + * virConnectCompareCPU: + * @conn: virConnect connection + * @xml: XML describing the CPU to compare with host CPU + * + * Returns comparison result according to enum virCPUCompareResult + */ +int +virConnectCompareCPU(virConnectPtr conn, const char *xmlDesc) +{ + VIR_DEBUG("conn=%p, xmlDesc=%s", conn, xmlDesc); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return VIR_CPU_COMPARE_ERROR; + } + if (xmlDesc == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->driver->cpuCompare) { + int ret; + + ret = conn->driver->cpuCompare(conn, xmlDesc); + if (ret == VIR_CPU_COMPARE_ERROR) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatibility */ + virSetConnError(conn); + return VIR_CPU_COMPARE_ERROR; +}
As of patch 3 I would prefer a flags there. ACK once done or justified against 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> --- src/remote/remote_protocol.x | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 92f7010..3d7ac45 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1433,6 +1433,15 @@ struct remote_interface_is_active_ret { }; +struct remote_cpu_compare_args { + remote_nonnull_string xml; +}; + +struct remote_cpu_compare_ret { + int result; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1611,7 +1620,8 @@ enum remote_procedure { REMOTE_PROC_STORAGE_POOL_IS_ACTIVE = 154, REMOTE_PROC_STORAGE_POOL_IS_PERSISTENT = 155, REMOTE_PROC_INTERFACE_IS_ACTIVE = 156, - REMOTE_PROC_GET_LIB_VERSION = 157 + REMOTE_PROC_GET_LIB_VERSION = 157, + REMOTE_PROC_CPU_COMPARE = 158 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:04:03AM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/remote/remote_protocol.x | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 92f7010..3d7ac45 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1433,6 +1433,15 @@ struct remote_interface_is_active_ret { };
+struct remote_cpu_compare_args { + remote_nonnull_string xml; +}; + +struct remote_cpu_compare_ret { + int result; +}; + + /*----- Protocol. -----*/
/* Define the program number, protocol version and procedure numbers here. */ @@ -1611,7 +1620,8 @@ enum remote_procedure { REMOTE_PROC_STORAGE_POOL_IS_ACTIVE = 154, REMOTE_PROC_STORAGE_POOL_IS_PERSISTENT = 155, REMOTE_PROC_INTERFACE_IS_ACTIVE = 156, - REMOTE_PROC_GET_LIB_VERSION = 157 + REMOTE_PROC_GET_LIB_VERSION = 157, + REMOTE_PROC_CPU_COMPARE = 158
/* * Notice how the entries are grouped in sets of 10 ?
ACK, 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> --- src/remote/remote_driver.c | 27 +++++++++++++++++++++++++++ 1 files changed, 27 insertions(+), 0 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 77962fe..db481dd 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7465,6 +7465,32 @@ done: return rv; } + +static int +remoteCPUCompare(virConnectPtr conn, const char *xmlDesc) +{ + struct private_data *priv = conn->privateData; + remote_cpu_compare_args args; + remote_cpu_compare_ret ret; + int rv = VIR_CPU_COMPARE_ERROR; + + remoteDriverLock(priv); + + args.xml = (char *) xmlDesc; + + memset(&ret, 0, sizeof (ret)); + if (call(conn, priv, 0, REMOTE_PROC_CPU_COMPARE, + (xdrproc_t) xdr_remote_cpu_compare_args, (char *) &args, + (xdrproc_t) xdr_remote_cpu_compare_ret, (char *) &ret) == -1) + goto done; + + rv = ret.result; + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ @@ -8830,6 +8856,7 @@ static virDriver remote_driver = { remoteIsSecure, /* isSecure */ remoteDomainIsActive, /* domainIsActive */ remoteDomainIsPersistent, /* domainIsPersistent */ + remoteCPUCompare, /* cpuCompare */ }; static virNetworkDriver network_driver = { -- 1.6.5.6

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- daemon/remote.c | 22 ++++++++++++++++++++++ 1 files changed, 22 insertions(+), 0 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 7a43046..a6f4bec 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5226,6 +5226,28 @@ static int remoteDispatchIsSecure(struct qemud_server *server ATTRIBUTE_UNUSED, } +static int +remoteDispatchCpuCompare (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *err, + remote_cpu_compare_args *args, + remote_cpu_compare_ret *ret) +{ + int result; + + result = virConnectCompareCPU(conn, args->xml); + if (result == VIR_CPU_COMPARE_ERROR) { + remoteDispatchConnError(err, conn); + return -1; + } + + ret->result = result; + return 0; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:04:05AM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- daemon/remote.c | 22 ++++++++++++++++++++++ 1 files changed, 22 insertions(+), 0 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c index 7a43046..a6f4bec 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5226,6 +5226,28 @@ static int remoteDispatchIsSecure(struct qemud_server *server ATTRIBUTE_UNUSED, }
+static int +remoteDispatchCpuCompare (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *err, + remote_cpu_compare_args *args, + remote_cpu_compare_ret *ret) +{ + int result; + + result = virConnectCompareCPU(conn, args->xml); + if (result == VIR_CPU_COMPARE_ERROR) { + remoteDispatchConnError(err, conn); + return -1; + } + + ret->result = result; + return 0; +} + + /*----- Helpers. -----*/
/* get_nonnull_domain and get_nonnull_network turn an on-wire
This one needs to be merged with patch 6, since after rpcgen runs for patch 6, you won't be able to compile without this patch being present. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Each driver supporting CPU selection must fill in host CPU capabilities. When filling them, drivers for hypervisors running on the same node as libvirtd can use cpuNodeData() to obtain raw CPU data. Other drivers, such as VMware, need to implement their own way of getting such data. Raw data can be decoded into virCPUDefPtr using cpuDecode() function. When implementing virConnectCompareCPU(), a hypervisor driver can just call cpuCompareXML() function with host CPU capabilities. For each guest for which a driver supports selecting CPU models, it must set the appropriate feature in guest's capabilities: virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0) Actions needed when a domain is being created depend on whether the hypervisor understands raw CPU data (currently CPUID for i686, x86_64 architectures) or symbolic names has to be used. Typical use by hypervisors which prefer CPUID (such as VMware and Xen): - convert guest CPU configuration from domain's XML into a set of raw data structures each representing one of the feature policies: cpuEncode(conn, architecture, guest_cpu_config, &forced_data, &required_data, &optional_data, &disabled_data, &forbidden_data) - create a mask or whatever the hypervisor expects to see and pass it to the hypervisor Typical use by hypervisors with symbolic model names (such as QEMU): - get raw CPU data for a computed guest CPU: cpuGuestData(conn, host_cpu, guest_cpu_config, &data) - decode raw data into virCPUDefPtr with a possible restriction on allowed model names: cpuDecode(conn, guest, data, n_allowed_models, allowed_models) - pass guest->model and guest->features to the hypervisor Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- configure.in | 20 + src/Makefile.am | 12 + src/cpu/cpu.c | 242 ++++++++++ src/cpu/cpu.h | 129 +++++ src/cpu/cpu_generic.c | 123 +++++ src/cpu/cpu_generic.h | 32 ++ src/cpu/cpu_map.c | 130 ++++++ src/cpu/cpu_map.h | 41 ++ src/cpu/cpu_x86.c | 1159 ++++++++++++++++++++++++++++++++++++++++++++++ src/cpu/cpu_x86.h | 31 ++ src/cpu/cpu_x86_data.h | 45 ++ src/libvirt_private.syms | 10 + 12 files changed, 1974 insertions(+), 0 deletions(-) create mode 100644 src/cpu/cpu.c create mode 100644 src/cpu/cpu.h create mode 100644 src/cpu/cpu_generic.c create mode 100644 src/cpu/cpu_generic.h create mode 100644 src/cpu/cpu_map.c create mode 100644 src/cpu/cpu_map.h create mode 100644 src/cpu/cpu_x86.c create mode 100644 src/cpu/cpu_x86.h create mode 100644 src/cpu/cpu_x86_data.h diff --git a/configure.in b/configure.in index 6ed2efd..4b26c4e 100644 --- a/configure.in +++ b/configure.in @@ -78,6 +78,26 @@ AC_SUBST(VERSION_SCRIPT_FLAGS) LIBVIRT_COMPILE_WARNINGS([maximum]) +AC_MSG_CHECKING([for CPUID instruction]) +AC_COMPILE_IFELSE(AC_LANG_PROGRAM( + [[ + #include <stdint.h> + ]], + [[ + uint32_t eax, ebx, ecx, edx; + asm volatile ( + "cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (eax)); + ]]), + [have_cpuid=yes], + [have_cpuid=no]) +if test "x$have_cpuid" = xyes; then + AC_DEFINE_UNQUOTED([HAVE_CPUID], 1, [whether CPUID instruction is supported]) +fi +AC_MSG_RESULT([$have_cpuid]) + + dnl Support large files / 64 bit seek offsets. dnl Use --disable-largefile if you don't want this. AC_SYS_LARGEFILE diff --git a/src/Makefile.am b/src/Makefile.am index 432a66e..471e403 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,7 @@ INCLUDES = \ -DSBINDIR=\""$(sbindir)"\" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -DDATADIR=\""$(datadir)/libvirt"\" \ -DLOCAL_STATE_DIR=\""$(localstatedir)"\" \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" \ $(WARN_CFLAGS) \ @@ -280,6 +281,11 @@ NODE_DEVICE_DRIVER_UDEV_SOURCES = \ node_device/node_device_udev.c \ node_device/node_device_udev.h +CPU_SOURCES = \ + cpu/cpu.h cpu/cpu.c \ + cpu/cpu_generic.h cpu/cpu_generic.c \ + cpu/cpu_x86.h cpu/cpu_x86.c cpu/cpu_x86_data.h \ + cpu/cpu_map.h cpu/cpu_map.c ######################### # @@ -301,6 +307,12 @@ libvirt_conf_la_SOURCES = $(CONF_SOURCES) libvirt_conf_la_CFLAGS = libvirt_conf_la_LDFLAGS = +noinst_LTLIBRARIES += libvirt_cpu.la +libvirt_la_LIBADD += libvirt_cpu.la +libvirt_cpu_la_CFLAGS = \ + -I@top_srcdir@/src/conf +libvirt_cpu_la_SOURCES = $(CPU_SOURCES) + noinst_LTLIBRARIES += libvirt_driver.la libvirt_la_LIBADD += libvirt_driver.la diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c new file mode 100644 index 0000000..c9ec64b --- /dev/null +++ b/src/cpu/cpu.c @@ -0,0 +1,242 @@ +/* + * cpu.c: internal functions for CPU manipulation + * + * 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 "xml.h" +#include "cpu.h" +#include "cpu_x86.h" +#include "cpu_generic.h" + + +#define NR_DRIVERS ARRAY_CARDINALITY(drivers) +#define VIR_FROM_THIS VIR_FROM_CPU + +static struct cpuArchDriver *drivers[] = { + &cpuDriverX86, + /* generic driver must always be the last one */ + &cpuDriverGeneric +}; + + +static struct cpuArchDriver * +cpuGetSubDriver(virConnectPtr conn, + const char *arch) +{ + unsigned int i; + unsigned int j; + + if (arch == NULL) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("undefined hardware architecture")); + return NULL; + } + + for (i = 0; i < NR_DRIVERS - 1; i++) { + for (j = 0; j < drivers[i]->narch; j++) { + if (STREQ(arch, drivers[i]->arch[j])) + return drivers[i]; + } + } + + /* use generic driver by default */ + return drivers[NR_DRIVERS - 1]; +} + + +virCPUCompareResult +cpuCompareXML(virConnectPtr conn, + virCPUDefPtr host, + const char *xml) +{ + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + virCPUDefPtr cpu = NULL; + virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR; + + doc = xmlParseMemory(xml, strlen(xml)); + + if (doc == NULL || (ctxt = xmlXPathNewContext(doc)) == NULL) { + virReportOOMError(conn); + goto cleanup; + } + + ctxt->node = xmlDocGetRootElement(doc); + + cpu = virCPUDefParseXML(conn, ctxt->node, ctxt, VIR_CPU_TYPE_AUTO); + if (cpu == NULL) + goto cleanup; + + ret = cpuCompare(conn, host, cpu); + +cleanup: + virCPUDefFree(cpu); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + + return ret; +} + + +virCPUCompareResult +cpuCompare(virConnectPtr conn, + virCPUDefPtr host, + virCPUDefPtr cpu) +{ + struct cpuArchDriver *driver; + + if ((driver = cpuGetSubDriver(conn, host->arch)) == NULL) + return VIR_CPU_COMPARE_ERROR; + + if (driver->compare == NULL) { + virCPUReportError(conn, VIR_ERR_NO_SUPPORT, + _("cannot compare CPUs of %s architecture"), + host->arch); + return VIR_CPU_COMPARE_ERROR; + } + + return driver->compare(host, cpu); +} + + +int +cpuDecode(virConnectPtr conn, + virCPUDefPtr cpu, + const union cpuData *data, + unsigned int nmodels, + const char **models) +{ + struct cpuArchDriver *driver; + + if (cpu == NULL) { + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("invalid CPU definition")); + return -1; + } + + if ((driver = cpuGetSubDriver(conn, cpu->arch)) == NULL) + return -1; + + if (driver->decode == NULL) { + virCPUReportError(conn, VIR_ERR_NO_SUPPORT, + _("cannot decode CPU data for %s architecture"), + cpu->arch); + return -1; + } + + return driver->decode(cpu, data, nmodels, models); +} + + +int +cpuEncode(virConnectPtr conn, + const char *arch, + const virCPUDefPtr cpu, + union cpuData **forced, + union cpuData **required, + union cpuData **optional, + union cpuData **disabled, + union cpuData **forbidden) +{ + struct cpuArchDriver *driver; + + if ((driver = cpuGetSubDriver(conn, arch)) == NULL) + return -1; + + if (driver->encode == NULL) { + virCPUReportError(conn, VIR_ERR_NO_SUPPORT, + _("cannot encode CPU data for %s architecture"), + arch); + return -1; + } + + return driver->encode(cpu, forced, required, + optional, disabled, forbidden); +} + + +void +cpuDataFree(virConnectPtr conn, + const char *arch, + union cpuData *data) +{ + struct cpuArchDriver *driver; + + if (data == NULL) + return; + + if ((driver = cpuGetSubDriver(conn, arch)) == NULL) + return; + + if (driver->free == NULL) { + virCPUReportError(conn, VIR_ERR_NO_SUPPORT, + _("cannot free CPU data for %s architecture"), + arch); + return; + } + + driver->free(data); +} + + +union cpuData * +cpuNodeData(virConnectPtr conn, + const char *arch) +{ + struct cpuArchDriver *driver; + + if ((driver = cpuGetSubDriver(conn, arch)) == NULL) + return NULL; + + if (driver->nodeData == NULL) { + virCPUReportError(conn, VIR_ERR_NO_SUPPORT, + _("cannot get node CPU data for %s architecture"), + arch); + return NULL; + } + + return driver->nodeData(); +} + + +virCPUCompareResult +cpuGuestData(virConnectPtr conn, + virCPUDefPtr host, + virCPUDefPtr guest, + union cpuData **data) +{ + struct cpuArchDriver *driver; + + if ((driver = cpuGetSubDriver(conn, host->arch)) == NULL) + return VIR_CPU_COMPARE_ERROR; + + if (driver->guestData == NULL) { + virCPUReportError(conn, VIR_ERR_NO_SUPPORT, + _("cannot compute guest CPU data for %s architecture"), + host->arch); + return VIR_CPU_COMPARE_ERROR; + } + + return driver->guestData(host, guest, data); +} + diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h new file mode 100644 index 0000000..5b1636d --- /dev/null +++ b/src/cpu/cpu.h @@ -0,0 +1,129 @@ +/* + * cpu.h: internal functions for CPU manipulation + * + * 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_H__ +#define __VIR_CPU_H__ + +#include "virterror_internal.h" +#include "datatypes.h" +#include "conf/cpu_conf.h" +#include "cpu_x86_data.h" + + +#define virCPUReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_CPU, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + + +union cpuData { + struct cpuX86Data x86; + /* generic driver needs no data */ +}; + + +typedef virCPUCompareResult +(*cpuArchCompare) (virCPUDefPtr host, + virCPUDefPtr cpu); + +typedef int +(*cpuArchDecode) (virCPUDefPtr cpu, + const union cpuData *data, + unsigned int nmodels, + const char **models); + +typedef int +(*cpuArchEncode) (const virCPUDefPtr cpu, + union cpuData **forced, + union cpuData **required, + union cpuData **optional, + union cpuData **disabled, + union cpuData **forbidden); + +typedef void +(*cpuArchDataFree) (union cpuData *data); + +typedef union cpuData * +(*cpuArchNodeData) (void); + +typedef virCPUCompareResult +(*cpuArchGuestData) (virCPUDefPtr host, + virCPUDefPtr guest, + union cpuData **data); + + +struct cpuArchDriver { + const char *name; + const char **arch; + unsigned int narch; + cpuArchCompare compare; + cpuArchDecode decode; + cpuArchEncode encode; + cpuArchDataFree free; + cpuArchNodeData nodeData; + cpuArchGuestData guestData; +}; + + +extern virCPUCompareResult +cpuCompareXML(virConnectPtr conn, + virCPUDefPtr host, + const char *xml); + +extern virCPUCompareResult +cpuCompare (virConnectPtr conn, + virCPUDefPtr host, + virCPUDefPtr cpu); + +extern int +cpuDecode (virConnectPtr conn, + virCPUDefPtr cpu, + const union cpuData *data, + unsigned int nmodels, + const char **models); + +extern int +cpuEncode (virConnectPtr conn, + const char *arch, + const virCPUDefPtr cpu, + union cpuData **forced, + union cpuData **required, + union cpuData **optional, + union cpuData **disabled, + union cpuData **forbidden); + +extern void +cpuDataFree (virConnectPtr conn, + const char *arch, + union cpuData *data); + +extern union cpuData * +cpuNodeData (virConnectPtr conn, + const char *arch); + +extern virCPUCompareResult +cpuGuestData(virConnectPtr conn, + virCPUDefPtr host, + virCPUDefPtr guest, + union cpuData **data); + +#endif /* __VIR_CPU_H__ */ diff --git a/src/cpu/cpu_generic.c b/src/cpu/cpu_generic.c new file mode 100644 index 0000000..4caa06a --- /dev/null +++ b/src/cpu/cpu_generic.c @@ -0,0 +1,123 @@ +/* + * cpu_generic.c: CPU manipulation driver for architectures which are not + * handled by their own driver + * + * 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 "hash.h" +#include "cpu.h" +#include "cpu_generic.h" + + +#define VIR_FROM_THIS VIR_FROM_CPU + + +static virHashTablePtr +genericHashFeatures(virCPUDefPtr cpu) +{ + virHashTablePtr hash; + unsigned int i; + + if ((hash = virHashCreate(cpu->nfeatures)) == NULL) + return NULL; + + for (i = 0; i < cpu->nfeatures; i++) { + if (virHashAddEntry(hash, + cpu->features[i].name, + cpu->features + i)) { + virHashFree(hash, NULL); + return NULL; + } + } + + return hash; +} + + +static virCPUCompareResult +genericCompare(virCPUDefPtr host, + virCPUDefPtr cpu) +{ + virHashTablePtr hash; + virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR; + unsigned int i; + unsigned int reqfeatures; + + if ((cpu->arch && STRNEQ(host->arch, cpu->arch)) || + STRNEQ(host->model, cpu->model)) + return VIR_CPU_COMPARE_INCOMPATIBLE; + + if ((hash = genericHashFeatures(host)) == NULL) { + virReportOOMError(NULL); + goto cleanup; + } + + reqfeatures = 0; + for (i = 0; i < cpu->nfeatures; i++) { + void *hval = virHashLookup(hash, cpu->features[i].name); + + if (hval) { + if (cpu->type == VIR_CPU_TYPE_GUEST && + cpu->features[i].policy == VIR_CPU_FEATURE_FORBID) { + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto cleanup; + } + reqfeatures++; + } + else { + if (cpu->type == VIR_CPU_TYPE_HOST || + cpu->features[i].policy == VIR_CPU_FEATURE_REQUIRE) { + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto cleanup; + } + } + } + + if (host->nfeatures > reqfeatures) { + if (cpu->type == VIR_CPU_TYPE_GUEST && + cpu->match == VIR_CPU_MATCH_STRICT) + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + else + ret = VIR_CPU_COMPARE_SUPERSET; + } + else + ret = VIR_CPU_COMPARE_IDENTICAL; + +cleanup: + virHashFree(hash, NULL); + return ret; +} + + +struct cpuArchDriver cpuDriverGeneric = { + .name = "generic", + .arch = NULL, + .narch = 0, + .compare = genericCompare, + .decode = NULL, + .encode = NULL, + .free = NULL, + .nodeData = NULL, + .guestData = NULL +}; + diff --git a/src/cpu/cpu_generic.h b/src/cpu/cpu_generic.h new file mode 100644 index 0000000..8fc952a --- /dev/null +++ b/src/cpu/cpu_generic.h @@ -0,0 +1,32 @@ +/* + * cpu_generic.h: CPU manipulation driver for architectures which are not + * handled by their own driver + * + * 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_GENERIC_H__ +#define __VIR_CPU_GENERIC_H__ + +#include "cpu.h" + +extern struct cpuArchDriver cpuDriverGeneric; + +#endif /* __VIR_CPU_GENERIC_H__ */ diff --git a/src/cpu/cpu_map.c b/src/cpu/cpu_map.c new file mode 100644 index 0000000..55fb1ea --- /dev/null +++ b/src/cpu/cpu_map.c @@ -0,0 +1,130 @@ +/* + * cpu_map.c: internal functions for handling CPU mapping configuration + * + * 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 "memory.h" +#include "cpu.h" +#include "cpu_map.h" + + +#define VIR_FROM_THIS VIR_FROM_CPU + +#define CPUMAPFILE DATADIR "/cpu_map.xml" + + +static int load(xmlXPathContextPtr ctxt, + const char *node, + cpuMapLoadCallback callback, + void *data) +{ + int ret = -1; + xmlNodePtr ctxt_node; + xmlNodePtr cur; + + ctxt_node = ctxt->node; + + cur = ctxt_node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST node)) { + ctxt->node = cur; + if (callback(ctxt, data) < 0) + goto cleanup; + } + + cur = cur->next; + } + + ret = 0; + +cleanup: + ctxt->node = ctxt_node; + + return ret; +} + + +int cpuMapLoad(const char *arch, + cpuMapLoadCallback feature_cb, + void *model_data, + cpuMapLoadCallback model_cb, + void *feature_data) +{ + xmlDocPtr xml = NULL; + xmlXPathContextPtr ctxt = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *xpath = NULL; + int ret = -1; + + if (arch == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("undefined hardware architecture")); + return -1; + } + + if ((xml = xmlParseFile(CPUMAPFILE)) == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot parse CPU map file: %s"), + CPUMAPFILE); + goto cleanup; + } + + if ((ctxt = xmlXPathNewContext(xml)) == NULL) + goto no_memory; + + virBufferVSprintf(&buf, "./arch[@name='%s']", arch); + if (virBufferError(&buf)) + goto no_memory; + + xpath = virBufferContentAndReset(&buf); + + ctxt->node = xmlDocGetRootElement(xml); + + if ((ctxt->node = virXPathNode(NULL, xpath, ctxt)) == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot find CPU map for %s architecture"), arch); + goto cleanup; + } + + if ((feature_cb && load(ctxt, "feature", feature_cb, feature_data) < 0) || + (model_cb && load(ctxt, "model", model_cb, model_data) < 0)) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot parse CPU map for %s architecture"), arch); + goto cleanup; + } + + ret = 0; + +cleanup: + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + VIR_FREE(xpath); + + return ret; + +no_memory: + virReportOOMError(NULL); + goto cleanup; +} + diff --git a/src/cpu/cpu_map.h b/src/cpu/cpu_map.h new file mode 100644 index 0000000..affe534 --- /dev/null +++ b/src/cpu/cpu_map.h @@ -0,0 +1,41 @@ +/* + * cpu_map.h: internal functions for handling CPU mapping configuration + * + * 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_MAP_H__ +#define __VIR_CPU_MAP_H__ + +#include "xml.h" + + +typedef int +(*cpuMapLoadCallback) (xmlXPathContextPtr ctxt, + void *data); + +extern int +cpuMapLoad(const char *arch, + cpuMapLoadCallback feature_cb, + void *model_data, + cpuMapLoadCallback model_cb, + void *feature_data); + +#endif /* __VIR_CPU_MAP_H__ */ diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c new file mode 100644 index 0000000..3bb0df3 --- /dev/null +++ b/src/cpu/cpu_x86.c @@ -0,0 +1,1159 @@ +/* + * cpu_x86.c: CPU driver for CPUs with x86 compatible CPUID instruction + * + * 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 <stdint.h> + +#include "logging.h" +#include "memory.h" +#include "cpu.h" +#include "cpu_map.h" +#include "cpu_x86.h" + + +#define VIR_FROM_THIS VIR_FROM_CPU + +static const char *archs[] = { "i686", "x86_64" }; + +struct x86_feature { + char *name; + unsigned int ncpuid; + struct cpuX86cpuid *cpuid; + + struct x86_feature *prev; + struct x86_feature *next; +}; + +struct x86_model { + char *name; + unsigned int ncpuid; + struct cpuX86cpuid *cpuid; + + struct x86_model *prev; + struct x86_model *next; +}; + +struct x86_map { + struct x86_feature *features; + struct x86_model *models; +}; + + +enum compare_result { + SUBSET, + EQUAL, + SUPERSET, + UNRELATED +}; + + +static struct cpuX86cpuid * +x86cpuidFind(struct cpuX86cpuid *cpuids, + unsigned int ncpuids, + uint32_t function) +{ + unsigned int i; + + for (i = 0; i < ncpuids; i++) { + if (cpuids[i].function == function) + return cpuids + i; + } + + return NULL; +} + + +static inline int +x86cpuidMatch(const struct cpuX86cpuid *cpuid1, + const struct cpuX86cpuid *cpuid2) +{ + return (cpuid1->eax == cpuid2->eax && + cpuid1->ebx == cpuid2->ebx && + cpuid1->ecx == cpuid2->ecx && + cpuid1->edx == cpuid2->edx); +} + + +static inline int +x86cpuidMatchMasked(const struct cpuX86cpuid *cpuid, + const struct cpuX86cpuid *mask) +{ + return ((cpuid->eax & mask->eax) == mask->eax && + (cpuid->ebx & mask->ebx) == mask->ebx && + (cpuid->ecx & mask->ecx) == mask->ecx && + (cpuid->edx & mask->edx) == mask->edx); +} + + +static inline int +x86cpuidMatchAny(const struct cpuX86cpuid *cpuid, + const struct cpuX86cpuid *mask) +{ + return ((cpuid->eax & mask->eax) || + (cpuid->ebx & mask->ebx) || + (cpuid->ecx & mask->ecx) || + (cpuid->edx & mask->edx)); +} + + +static inline void +x86cpuidSetBits(struct cpuX86cpuid *cpuid, + const struct cpuX86cpuid *mask) +{ + cpuid->eax |= mask->eax; + cpuid->ebx |= mask->ebx; + cpuid->ecx |= mask->ecx; + cpuid->edx |= mask->edx; +} + + +static inline void +x86cpuidClearBits(struct cpuX86cpuid *cpuid, + const struct cpuX86cpuid *mask) +{ + cpuid->eax &= ~mask->eax; + cpuid->ebx &= ~mask->ebx; + cpuid->ecx &= ~mask->ecx; + cpuid->edx &= ~mask->edx; +} + + +static struct cpuX86cpuid * +x86DataCpuid(const union cpuData *data, + uint32_t function) +{ + struct cpuX86cpuid *cpuids; + int len; + unsigned int i; + + if (function < CPUX86_EXTENDED) { + cpuids = data->x86.basic; + len = data->x86.basic_len; + i = function; + } + else { + cpuids = data->x86.extended; + len = data->x86.extended_len; + i = function - CPUX86_EXTENDED; + } + + if (i < len) + return cpuids + i; + else + return NULL; +} + + +static void +x86DataFree(union cpuData *data) +{ + if (data == NULL) + return; + + VIR_FREE(data->x86.basic); + VIR_FREE(data->x86.extended); + VIR_FREE(data); +} + + +static union cpuData * +x86DataCopy(const union cpuData *data) +{ + union cpuData *copy = NULL; + int i; + + if (VIR_ALLOC(copy) < 0 + || VIR_ALLOC_N(copy->x86.basic, data->x86.basic_len) < 0 + || VIR_ALLOC_N(copy->x86.extended, data->x86.extended_len) < 0) { + x86DataFree(copy); + return NULL; + } + + copy->x86.basic_len = data->x86.basic_len; + for (i = 0; i < data->x86.basic_len; i++) + copy->x86.basic[i] = data->x86.basic[i]; + + copy->x86.extended_len = data->x86.extended_len; + for (i = 0; i < data->x86.extended_len; i++) + copy->x86.extended[i] = data->x86.extended[i]; + + return copy; +} + + +static union cpuData * +x86DataFromModel(const struct x86_model *model) +{ + union cpuData *data = NULL; + uint32_t basic_len = 0; + uint32_t extended_len = 0; + struct cpuX86cpuid *cpuid; + int i; + + for (i = 0; i < model->ncpuid; i++) { + cpuid = model->cpuid + i; + if (cpuid->function < CPUX86_EXTENDED) { + if (cpuid->function >= basic_len) + basic_len = cpuid->function + 1; + } + else if (cpuid->function - CPUX86_EXTENDED >= extended_len) + extended_len = cpuid->function - CPUX86_EXTENDED + 1; + } + + if (VIR_ALLOC(data) < 0 + || VIR_ALLOC_N(data->x86.basic, basic_len) < 0 + || VIR_ALLOC_N(data->x86.extended, extended_len) < 0) { + x86DataFree(data); + return NULL; + } + + data->x86.basic_len = basic_len; + data->x86.extended_len = extended_len; + + for (i = 0; i < model->ncpuid; i++) { + cpuid = x86DataCpuid(data, model->cpuid[i].function); + *cpuid = model->cpuid[i]; + } + + return data; +} + + +static void +x86FeatureFree(struct x86_feature *feature) +{ + if (feature == NULL) + return; + + VIR_FREE(feature->name); + VIR_FREE(feature->cpuid); + VIR_FREE(feature); +} + + +static struct x86_feature * +x86FeatureFind(const struct x86_map *map, + const char *name) +{ + struct x86_feature *feature; + + feature = map->features; + while (feature != NULL) { + if (STREQ(feature->name, name)) + return feature; + + feature = feature->next; + } + + return NULL; +} + + +static int +x86FeatureLoad(xmlXPathContextPtr ctxt, + void *data) +{ + struct x86_map *map = data; + xmlNodePtr *nodes = NULL; + struct x86_feature *feature = NULL; + int ret = 0; + int i; + int n; + + if (VIR_ALLOC(feature) < 0) + goto no_memory; + + feature->name = virXPathString(NULL, "string(@name)", ctxt); + if (feature->name == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing CPU feature name")); + goto ignore; + } + + if (x86FeatureFind(map, feature->name)) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("CPU feature %s already defined"), feature->name); + goto ignore; + } + + n = virXPathNodeSet(NULL, "./cpuid", ctxt, &nodes); + if (n < 0) + goto ignore; + + if (n > 0) { + if (VIR_ALLOC_N(feature->cpuid, n) < 0) + goto no_memory; + feature->ncpuid = n; + } + + for (i = 0; i < n; i++) { + struct cpuX86cpuid *cpuid = feature->cpuid + i; + int fun, eax, ebx, ecx, edx; + + ctxt->node = nodes[i]; + fun = virXPathULongHex(NULL, "string(@function)", ctxt, + (unsigned long *) &cpuid->function); + eax = virXPathULongHex(NULL, "string(@eax)", ctxt, + (unsigned long *) &cpuid->eax); + ebx = virXPathULongHex(NULL, "string(@ebx)", ctxt, + (unsigned long *) &cpuid->ebx); + ecx = virXPathULongHex(NULL, "string(@ecx)", ctxt, + (unsigned long *) &cpuid->ecx); + edx = virXPathULongHex(NULL, "string(@edx)", ctxt, + (unsigned long *) &cpuid->edx); + + if (fun < 0 || eax == -2 || ebx == -2 || ecx == -2 || edx == -2) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("Invalid cpuid[%d] in %s feature"), i, feature->name); + goto ignore; + } + } + + if (map->features == NULL) + map->features = feature; + else { + feature->next = map->features; + map->features->prev = feature; + map->features = feature; + } + +out: + return ret; + +no_memory: + virReportOOMError(NULL); + ret = -1; + +ignore: + x86FeatureFree(feature); + goto out; +} + + +static void +x86ModelFree(struct x86_model *model) +{ + if (model == NULL) + return; + + VIR_FREE(model->name); + VIR_FREE(model->cpuid); + VIR_FREE(model); +} + + +static struct x86_model * +x86ModelCopy(const struct x86_model *model) +{ + struct x86_model *copy; + int i; + + if (VIR_ALLOC(copy) < 0 + || (copy->name = strdup(model->name)) == NULL + || VIR_ALLOC_N(copy->cpuid, model->ncpuid) < 0) { + x86ModelFree(copy); + return NULL; + } + + copy->ncpuid = model->ncpuid; + for (i = 0; i < model->ncpuid; i++) + copy->cpuid[i] = model->cpuid[i]; + + return copy; +} + + +static int +x86ModelAddCpuid(struct x86_model *model, + const struct cpuX86cpuid *cpuid) +{ + struct cpuX86cpuid *model_cpuid; + + model_cpuid = x86cpuidFind(model->cpuid, model->ncpuid, cpuid->function); + + if (model_cpuid != NULL) + x86cpuidSetBits(model_cpuid, cpuid); + else { + if (VIR_REALLOC_N(model->cpuid, model->ncpuid + 1) < 0) + return -1; + + model->cpuid[model->ncpuid] = *cpuid; + model->ncpuid++; + } + + return 0; +} + + +static void +x86ModelSubtract(struct x86_model *model1, + const struct x86_model *model2) +{ + int i; + struct cpuX86cpuid *cpuid; + + for (i = 0; i < model2->ncpuid; i++) { + cpuid = x86cpuidFind(model1->cpuid, + model1->ncpuid, + model2->cpuid[i].function); + if (cpuid != NULL) + x86cpuidClearBits(cpuid, model2->cpuid + i); + } +} + + +static int +x86ModelAdd(struct x86_model *model1, + const struct x86_model *model2) +{ + int i; + + for (i = 0; i < model2->ncpuid; i++) { + if (x86ModelAddCpuid(model1, model2->cpuid + i)) + return -1; + } + + return 0; +} + + +static struct x86_model * +x86ModelFind(const struct x86_map *map, + const char *name) +{ + struct x86_model *model; + + model = map->models; + while (model != NULL) { + if (STREQ(model->name, name)) + return model; + + model = model->next; + } + + return NULL; +} + + +static int +x86ModelMergeFeature(struct x86_model *model, + const struct x86_feature *feature) +{ + int i; + + if (feature == NULL) + return 0; + + for (i = 0; i < feature->ncpuid; i++) { + if (x86ModelAddCpuid(model, feature->cpuid + i)) + return -1; + } + + return 0; +} + + +static struct x86_model * +x86ModelFromCPU(const virCPUDefPtr cpu, + const struct x86_map *map, + int policy) +{ + struct x86_model *model = NULL; + int i; + + if (cpu->type == VIR_CPU_TYPE_HOST + || policy == VIR_CPU_FEATURE_REQUIRE) { + if ((model = x86ModelFind(map, cpu->model)) == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("Unknown CPU model %s"), cpu->model); + goto error; + } + + if ((model = x86ModelCopy(model)) == NULL) + goto no_memory; + } + else if (VIR_ALLOC(model) < 0) + goto no_memory; + + for (i = 0; i < cpu->nfeatures; i++) { + const struct x86_feature *feature; + + if (cpu->type == VIR_CPU_TYPE_GUEST + && cpu->features[i].policy != policy) + continue; + + if ((feature = x86FeatureFind(map, cpu->features[i].name)) == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("Unknown CPU feature %s"), cpu->features[i].name); + goto error; + } + + if (x86ModelMergeFeature(model, feature)) + goto no_memory; + } + + return model; + +no_memory: + virReportOOMError(NULL); + +error: + x86ModelFree(model); + return NULL; +} + + +static enum compare_result +x86ModelCompare(const struct x86_model *model1, + const struct x86_model *model2) +{ + enum compare_result result = EQUAL; + struct cpuX86cpuid *cpuid1; + struct cpuX86cpuid *cpuid2; + int i; + + for (i = 0; i < model1->ncpuid; i++) { + enum compare_result match = SUPERSET; + + cpuid1 = model1->cpuid + i; + cpuid2 = x86cpuidFind(model2->cpuid, + model2->ncpuid, + cpuid1->function); + if (cpuid2 != NULL) { + if (x86cpuidMatch(cpuid1, cpuid2)) + continue; + else if (!x86cpuidMatchMasked(cpuid1, cpuid2)) + match = SUBSET; + } + + if (result == EQUAL) + result = match; + else if (result != match) + return UNRELATED; + } + + for (i = 0; i < model2->ncpuid; i++) { + enum compare_result match = SUBSET; + + cpuid2 = model2->cpuid + i; + cpuid1 = x86cpuidFind(model1->cpuid, + model1->ncpuid, + cpuid2->function); + if (cpuid1 != NULL) { + if (x86cpuidMatch(cpuid2, cpuid1)) + continue; + else if (!x86cpuidMatchMasked(cpuid2, cpuid1)) + match = SUPERSET; + } + + if (result == EQUAL) + result = match; + else if (result != match) + return UNRELATED; + } + + return result; +} + + +static int +x86ModelLoad(xmlXPathContextPtr ctxt, + void *data) +{ + struct x86_map *map = data; + xmlNodePtr *nodes = NULL; + struct x86_model *model = NULL; + int ret = 0; + int i; + int n; + + if (VIR_ALLOC(model) < 0) + goto no_memory; + + model->name = virXPathString(NULL, "string(@name)", ctxt); + if (model->name == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing CPU model name")); + goto ignore; + } + + if (virXPathNode(NULL, "./model", ctxt) != NULL) { + const struct x86_model *ancestor; + char *name; + + name = virXPathString(NULL, "string(./model/@name)", ctxt); + if (name == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("Missing ancestor's name in CPU model %s"), + model->name); + goto ignore; + } + + if ((ancestor = x86ModelFind(map, name)) == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("Ancestor model %s not found for CPU model %s"), + name, model->name); + VIR_FREE(name); + goto ignore; + } + + VIR_FREE(name); + + if (VIR_ALLOC_N(model->cpuid, ancestor->ncpuid) < 0) + goto no_memory; + + model->ncpuid = ancestor->ncpuid; + memcpy(model->cpuid, ancestor->cpuid, + sizeof(*model->cpuid) * model->ncpuid); + } + + n = virXPathNodeSet(NULL, "./feature", ctxt, &nodes); + if (n < 0) + goto ignore; + + for (i = 0; i < n; i++) { + const struct x86_feature *feature; + char *name; + + if ((name = virXMLPropString(nodes[i], "name")) == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("Missing feature name for CPU model %s"), model->name); + goto ignore; + } + + if ((feature = x86FeatureFind(map, name)) == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("Feature %s required by CPU model %s not found"), + name, model->name); + VIR_FREE(name); + goto ignore; + } + VIR_FREE(name); + + if (x86ModelMergeFeature(model, feature)) + goto no_memory; + } + + if (map->models == NULL) + map->models = model; + else { + model->next = map->models; + map->models->prev = model; + map->models = model; + } + +out: + return ret; + +no_memory: + virReportOOMError(NULL); + ret = -1; + +ignore: + x86ModelFree(model); + goto out; +} + + +static void +x86MapFree(struct x86_map *map) +{ + if (map == NULL) + return; + + while (map->features != NULL) { + struct x86_feature *feature = map->features; + map->features = feature->next; + x86FeatureFree(feature); + } + + while (map->models != NULL) { + struct x86_model *model = map->models; + map->models = model->next; + x86ModelFree(model); + } + + VIR_FREE(map); +} + + +static struct x86_map * +x86LoadMap(void) +{ + struct x86_map *map; + + if (VIR_ALLOC(map) < 0) { + virReportOOMError(NULL); + return NULL; + } + + if (cpuMapLoad("x86", + x86FeatureLoad, map, + x86ModelLoad, map) < 0) + goto error; + + return map; + +error: + x86MapFree(map); + return NULL; +} + + +static virCPUCompareResult +x86Compute(virCPUDefPtr host, + virCPUDefPtr cpu, + union cpuData **guest) +{ + struct cpuX86cpuid cpuid_zero = { 0, 0, 0, 0, 0 }; + struct x86_map *map = NULL; + struct x86_model *host_model = NULL; + struct x86_model *cpu_force = NULL; + struct x86_model *cpu_require = NULL; + struct x86_model *cpu_optional = NULL; + struct x86_model *cpu_disable = NULL; + struct x86_model *cpu_forbid = NULL; + struct x86_model *diff = NULL; + struct x86_model *guest_model = NULL; + virCPUCompareResult ret; + enum compare_result result; + int i; + + if (cpu->arch != NULL) { + bool found = false; + + for (i = 0; i < ARRAY_CARDINALITY(archs); i++) { + if (STREQ(archs[i], cpu->arch)) { + found = true; + break; + } + } + + if (!found) + return VIR_CPU_COMPARE_INCOMPATIBLE; + } + + if ((map = x86LoadMap()) == NULL) + goto error; + + if (!(host_model = x86ModelFromCPU(host, map, 0))) + goto error; + + if (!(cpu_force = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORCE))) + goto error; + + if (!(cpu_require = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_REQUIRE))) + goto error; + + if (!(cpu_optional = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_OPTIONAL))) + goto error; + + if (!(cpu_disable = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_DISABLE))) + goto error; + + if (!(cpu_forbid = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORBID))) + goto error; + + if ((diff = x86ModelCopy(host_model)) == NULL) + goto no_memory; + + x86ModelSubtract(diff, cpu_require); + x86ModelSubtract(diff, cpu_optional); + + for (i = 0; i < cpu_forbid->ncpuid; i++) { + const struct cpuX86cpuid *cpuid1; + const struct cpuX86cpuid *cpuid2; + + cpuid1 = cpu_forbid->cpuid + i; + cpuid2 = x86cpuidFind(host_model->cpuid, + host_model->ncpuid, + cpuid1->function); + + if (cpuid2 != NULL && x86cpuidMatchAny(cpuid2, cpuid1)) { + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto out; + } + } + + result = x86ModelCompare(host_model, cpu_require); + if (result == SUBSET || result == UNRELATED) { + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto out; + } + + ret = VIR_CPU_COMPARE_IDENTICAL; + + for (i = 0; i < host_model->ncpuid; i++) { + if (!x86cpuidMatch(host_model->cpuid + i, &cpuid_zero)) { + ret = VIR_CPU_COMPARE_SUPERSET; + break; + } + } + + if (ret == VIR_CPU_COMPARE_SUPERSET + && cpu->type == VIR_CPU_TYPE_GUEST + && cpu->match == VIR_CPU_MATCH_STRICT) { + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto out; + } + + if (guest != NULL) { + if ((guest_model = x86ModelCopy(host_model)) == NULL) + goto no_memory; + + if (cpu->type == VIR_CPU_TYPE_GUEST + && cpu->match == VIR_CPU_MATCH_EXACT) + x86ModelSubtract(guest_model, diff); + + if (x86ModelAdd(guest_model, cpu_force)) + goto no_memory; + + x86ModelSubtract(guest_model, cpu_disable); + + if ((*guest = x86DataFromModel(guest_model)) == NULL) + goto no_memory; + } + +out: + x86MapFree(map); + x86ModelFree(host_model); + x86ModelFree(diff); + x86ModelFree(cpu_force); + x86ModelFree(cpu_require); + x86ModelFree(cpu_optional); + x86ModelFree(cpu_disable); + x86ModelFree(cpu_forbid); + x86ModelFree(guest_model); + + return ret; + +no_memory: + virReportOOMError(NULL); + +error: + ret = VIR_CPU_COMPARE_ERROR; + goto out; +} + + +static virCPUCompareResult +x86Compare(virCPUDefPtr host, + virCPUDefPtr cpu) +{ + return x86Compute(host, cpu, NULL); +} + + +static virCPUCompareResult +x86GuestData(virCPUDefPtr host, + virCPUDefPtr guest, + union cpuData **data) +{ + return x86Compute(host, guest, data); +} + + +static int +x86Decode(virCPUDefPtr cpu, + const union cpuData *data, + unsigned int nmodels, + const char **models) +{ + int ret = -1; + struct x86_map *map; + const struct x86_feature *feature; + const struct x86_model *model = NULL; + const struct x86_model *candidate; + union cpuData *tmp = NULL; + struct cpuX86cpuid *cpuid; + int i; + + if (data == NULL || (map = x86LoadMap()) == NULL) + return -1; + + candidate = map->models; + while (candidate != NULL) { + for (i = 0; i < candidate->ncpuid; i++) { + cpuid = x86DataCpuid(data, candidate->cpuid[i].function); + if (cpuid == NULL + || !x86cpuidMatchMasked(cpuid, candidate->cpuid + i)) + goto next; + } + + if (model == NULL + || x86ModelCompare(model, candidate) == SUBSET) { + bool found = false; + for (i = 0; i < nmodels; i++) { + if (STREQ(models[i], candidate->name)) { + found = true; + break; + } + } + + if (nmodels > 0 && !found) { + VIR_DEBUG("CPU model %s not allowed by hypervisor; ignoring", + candidate->name); + } + else + model = candidate; + } + + next: + candidate = candidate->next; + } + + if (model == NULL) { + virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Cannot find suitable CPU model for given data")); + goto out; + } + + if ((cpu->model = strdup(model->name)) == NULL + || (tmp = x86DataCopy(data)) == NULL) { + virReportOOMError(NULL); + goto out; + } + + for (i = 0; i < model->ncpuid; i++) { + x86cpuidClearBits(x86DataCpuid(tmp, model->cpuid[i].function), + model->cpuid + i); + } + + feature = map->features; + while (feature != NULL) { + for (i = 0; i < feature->ncpuid; i++) { + if ((cpuid = x86DataCpuid(tmp, feature->cpuid[i].function)) + && x86cpuidMatchMasked(cpuid, feature->cpuid + i)) { + x86cpuidClearBits(cpuid, feature->cpuid + i); + if (virCPUDefAddFeature(NULL, cpu, feature->name, + VIR_CPU_FEATURE_REQUIRE) < 0) + goto out; + } + } + + feature = feature->next; + } + + ret = 0; + +out: + x86DataFree(tmp); + x86MapFree(map); + + return ret; +} + + +static union cpuData * +x86EncodePolicy(const virCPUDefPtr cpu, + const struct x86_map *map, + enum virCPUFeaturePolicy policy) +{ + struct x86_model *model; + union cpuData *data = NULL; + + if (!(model = x86ModelFromCPU(cpu, map, policy))) + return NULL; + + if (!(data = x86DataFromModel(model))) + virReportOOMError(NULL); + + x86ModelFree(model); + + return data; +} + + +static int +x86Encode(const virCPUDefPtr cpu, + union cpuData **forced, + union cpuData **required, + union cpuData **optional, + union cpuData **disabled, + union cpuData **forbidden) +{ + struct x86_map *map = NULL; + union cpuData *data_forced = NULL; + union cpuData *data_required = NULL; + union cpuData *data_optional = NULL; + union cpuData *data_disabled = NULL; + union cpuData *data_forbidden = NULL; + int ret = -1; + + if ((map = x86LoadMap()) == NULL) + goto error; + + if (forced) { + data_forced = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_FORCE); + if (!data_forced) + goto error; + } + + if (required) { + data_required = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_REQUIRE); + if (!data_required) + goto error; + } + + if (optional) { + data_optional = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_OPTIONAL); + if (!data_optional) + goto error; + } + + if (disabled) { + data_disabled = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_DISABLE); + if (!data_disabled) + goto error; + } + + if (forbidden) { + data_forbidden = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_FORBID); + if (!data_forbidden) + goto error; + } + + if (forced) + *forced = data_forced; + if (required) + *required = data_required; + if (optional) + *optional = data_optional; + if (disabled) + *disabled = data_disabled; + if (forbidden) + *forbidden = data_forbidden; + + ret = 0; + +cleanup: + x86MapFree(map); + + return ret; + +error: + x86DataFree(data_forced); + x86DataFree(data_required); + x86DataFree(data_optional); + x86DataFree(data_disabled); + x86DataFree(data_forbidden); + goto cleanup; +} + + +#if HAVE_CPUID +static int +cpuidSet(uint32_t base, struct cpuX86cpuid **set) +{ + uint32_t max; + uint32_t i; + + asm("cpuid" + : "=a" (max) + : "a" (base)); + + max -= base; + + if (virAllocN(set, sizeof(**set), max + 1) < 0) { + virReportOOMError(NULL); + return -1; + } + + for (i = 0; i <= max; i++) { + struct cpuX86cpuid *cpuid = (*set) + i; + + cpuid->function = base | i; + +#if __x86_64__ + asm("cpuid" + : "=a" (cpuid->eax), + "=b" (cpuid->ebx), + "=c" (cpuid->ecx), + "=d" (cpuid->edx) + : "a" (cpuid->function)); +#else + /* we need to avoid direct use of ebx for CPUID output as it is used + * for global offset table on i386 with -fPIC + */ + asm("push %%ebx;" + "cpuid;" + "mov %%ebx, %1;" + "pop %%ebx;" + : "=a" (cpuid->eax), + "=r" (cpuid->ebx), + "=c" (cpuid->ecx), + "=d" (cpuid->edx) + : "a" (cpuid->function) + : "cc"); +#endif + } + + return max + 1; +} + + +static union cpuData * +x86NodeData(void) +{ + union cpuData *data; + + if (VIR_ALLOC(data) < 0) { + virReportOOMError(NULL); + return NULL; + } + + data->x86.basic_len = cpuidSet(CPUX86_BASIC, &data->x86.basic); + if (data->x86.basic_len < 0) + goto error; + + data->x86.extended_len = cpuidSet(CPUX86_EXTENDED, &data->x86.extended); + if (data->x86.extended_len < 0) + goto error; + + return data; + +error: + x86DataFree(data); + + return NULL; +} +#endif + + +struct cpuArchDriver cpuDriverX86 = { + .name = "x86", + .arch = archs, + .narch = ARRAY_CARDINALITY(archs), + .compare = x86Compare, + .decode = x86Decode, + .encode = x86Encode, + .free = x86DataFree, +#if HAVE_CPUID + .nodeData = x86NodeData, +#else + .nodeData = NULL, +#endif + .guestData = x86GuestData +}; + diff --git a/src/cpu/cpu_x86.h b/src/cpu/cpu_x86.h new file mode 100644 index 0000000..58f1aae --- /dev/null +++ b/src/cpu/cpu_x86.h @@ -0,0 +1,31 @@ +/* + * cpu_x86.h: CPU driver for CPUs with x86 compatible CPUID instruction + * + * 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_X86_H__ +#define __VIR_CPU_X86_H__ + +#include "cpu.h" + +extern struct cpuArchDriver cpuDriverX86; + +#endif /* __VIR_CPU_X86_H__ */ diff --git a/src/cpu/cpu_x86_data.h b/src/cpu/cpu_x86_data.h new file mode 100644 index 0000000..8b09d23 --- /dev/null +++ b/src/cpu/cpu_x86_data.h @@ -0,0 +1,45 @@ +/* + * cpu_x86_data.h: x86 specific CPU data + * + * 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_X86_DATA_H__ +#define __VIR_CPU_X86_DATA_H__ + +struct cpuX86cpuid { + uint32_t function; + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; +}; + +#define CPUX86_BASIC 0x0 +#define CPUX86_EXTENDED 0x80000000 + +struct cpuX86Data { + int basic_len; + struct cpuX86cpuid *basic; + int extended_len; + struct cpuX86cpuid *extended; +}; + +#endif /* __VIR_CPU_X86_DATA_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 9acd062..f90f269 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -71,6 +71,16 @@ virCgroupGetFreezerState; virCgroupSetFreezerState; +# cpu.h +cpuCompare; +cpuCompareXML; +cpuDataFree; +cpuDecode; +cpuEncode; +cpuGuestData; +cpuNodeData; + + # cpu_conf.h virCPUDefFree; virCPUDefParseXML; -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:04:06AM +0100, Jiri Denemark wrote:
diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c new file mode 100644 index 0000000..3bb0df3 --- /dev/null +++ b/src/cpu/cpu_x86.c +#if HAVE_CPUID +static int +cpuidSet(uint32_t base, struct cpuX86cpuid **set) +{ + uint32_t max; + uint32_t i; + + asm("cpuid" + : "=a" (max) + : "a" (base));
I'm having trouble with this asm block causing a SEGV on i386 ==10392== Invalid read of size 4 ==10392== at 0x404FC50: ??? (in /home/berrange/src/xen/libvirt/src/.libs/libvirt.so.0.7.4) ==10392== by 0x409199D: x86NodeData (cpu_x86.c:1129) ==10392== by 0x408F298: cpuNodeData (cpu.c:218) ==10392== by 0x8093BA9: qemudCapsInitCPU (qemu_conf.c:987) ==10392== by 0x8093D23: qemudCapsInit (qemu_conf.c:1031) ==10392== by 0x807DEBC: qemudStartup (qemu_driver.c:1067) ==10392== by 0x4096F8D: virStateInitialize (libvirt.c:830) ==10392== by 0x805FB8B: main (libvirtd.c:3149) ==10392== Address 0x756e666b is not stack'd, malloc'd or (recently) free'd ==10392== ==10392== ==10392== Process terminating with default action of signal 11 (SIGSEGV) ==10392== Access not within mapped region at address 0x756E666B ==10392== at 0x404FC50: ??? (in /home/berrange/src/xen/libvirt/src/.libs/libvirt.so.0.7.4) ==10392== by 0x409199D: x86NodeData (cpu_x86.c:1129) ==10392== by 0x408F298: cpuNodeData (cpu.c:218) ==10392== by 0x8093BA9: qemudCapsInitCPU (qemu_conf.c:987) ==10392== by 0x8093D23: qemudCapsInit (qemu_conf.c:1031) ==10392== by 0x807DEBC: qemudStartup (qemu_driver.c:1067) ==10392== by 0x4096F8D: virStateInitialize (libvirt.c:830) ==10392== by 0x805FB8B: main (libvirtd.c:3149)
+ + max -= base; + + if (virAllocN(set, sizeof(**set), max + 1) < 0) { + virReportOOMError(NULL); + return -1; + } + + for (i = 0; i <= max; i++) { + struct cpuX86cpuid *cpuid = (*set) + i; + + cpuid->function = base | i; + +#if __x86_64__ + asm("cpuid" + : "=a" (cpuid->eax), + "=b" (cpuid->ebx), + "=c" (cpuid->ecx), + "=d" (cpuid->edx) + : "a" (cpuid->function)); +#else + /* we need to avoid direct use of ebx for CPUID output as it is used + * for global offset table on i386 with -fPIC + */ + asm("push %%ebx;" + "cpuid;" + "mov %%ebx, %1;" + "pop %%ebx;" + : "=a" (cpuid->eax), + "=r" (cpuid->ebx), + "=c" (cpuid->ecx), + "=d" (cpuid->edx) + : "a" (cpuid->function) + : "cc"); +#endif + } + + return max + 1; +} +
Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/Makefile.am | 2 + src/cpu/cpu_map.xml | 304 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+), 0 deletions(-) create mode 100644 src/cpu/cpu_map.xml diff --git a/src/Makefile.am b/src/Makefile.am index 471e403..7d731de 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -287,6 +287,8 @@ CPU_SOURCES = \ cpu/cpu_x86.h cpu/cpu_x86.c cpu/cpu_x86_data.h \ cpu/cpu_map.h cpu/cpu_map.c +EXTRA_DIST += cpu/cpu_map.xml + ######################### # # Build up list of libvirt.la source files based on configure conditions diff --git a/src/cpu/cpu_map.xml b/src/cpu/cpu_map.xml new file mode 100644 index 0000000..3d1cef9 --- /dev/null +++ b/src/cpu/cpu_map.xml @@ -0,0 +1,304 @@ +<cpus> + <arch name="x86"> + <!-- standard features, EDX --> + <feature name='fpu'> <!-- CPUID_FP87 --> + <cpuid function='0x00000001' edx='0x00000001'/> + </feature> + <feature name='vme'> <!-- CPUID_VME --> + <cpuid function='0x00000001' edx='0x00000002'/> + </feature> + <feature name='de'> <!-- CPUID_DE --> + <cpuid function='0x00000001' edx='0x00000004'/> + </feature> + <feature name='pse'> <!-- CPUID_PSE --> + <cpuid function='0x00000001' edx='0x00000008'/> + </feature> + <feature name='tsc'> <!-- CPUID_TSC --> + <cpuid function='0x00000001' edx='0x00000010'/> + </feature> + <feature name='msr'> <!-- CPUID_MSR --> + <cpuid function='0x00000001' edx='0x00000020'/> + </feature> + <feature name='pae'> <!-- CPUID_PAE --> + <cpuid function='0x00000001' edx='0x00000040'/> + </feature> + <feature name='mce'> <!-- CPUID_MCE --> + <cpuid function='0x00000001' edx='0x00000080'/> + </feature> + <feature name='cx8'> <!-- CPUID_CX8 --> + <cpuid function='0x00000001' edx='0x00000100'/> + </feature> + <feature name='apic'> <!-- CPUID_APIC --> + <cpuid function='0x00000001' edx='0x00000200'/> + </feature> + <feature name='sep'> <!-- CPUID_SEP --> + <cpuid function='0x00000001' edx='0x00000800'/> + </feature> + <feature name='mtrr'> <!-- CPUID_MTRR --> + <cpuid function='0x00000001' edx='0x00001000'/> + </feature> + <feature name='pge'> <!-- CPUID_PGE --> + <cpuid function='0x00000001' edx='0x00002000'/> + </feature> + <feature name='mca'> <!-- CPUID_MCA --> + <cpuid function='0x00000001' edx='0x00004000'/> + </feature> + <feature name='cmov'> <!-- CPUID_CMOV --> + <cpuid function='0x00000001' edx='0x00008000'/> + </feature> + <feature name='pat'> <!-- CPUID_PAT --> + <cpuid function='0x00000001' edx='0x00010000'/> + </feature> + <feature name='pse36'> <!-- CPUID_PSE36 --> + <cpuid function='0x00000001' edx='0x00020000'/> + </feature> + <feature name='pn'> <!-- CPUID_PN --> + <cpuid function='0x00000001' edx='0x00040000'/> + </feature> + <feature name='clflush'> <!-- CPUID_CLFLUSH --> + <cpuid function='0x00000001' edx='0x00080000'/> + </feature> + <feature name='ds'> <!-- CPUID_DTS --> + <cpuid function='0x00000001' edx='0x00200000'/> + </feature> + <feature name='acpi'> <!-- CPUID_ACPI --> + <cpuid function='0x00000001' edx='0x00400000'/> + </feature> + <feature name='mmx'> <!-- CPUID_MMX --> + <cpuid function='0x00000001' edx='0x00800000'/> + </feature> + <feature name='fxsr'> <!-- CPUID_FXSR --> + <cpuid function='0x00000001' edx='0x01000000'/> + </feature> + <feature name='sse'> <!-- CPUID_SSE --> + <cpuid function='0x00000001' edx='0x02000000'/> + </feature> + <feature name='sse2'> <!-- CPUID_SSE2 --> + <cpuid function='0x00000001' edx='0x04000000'/> + </feature> + <feature name='ss'> <!-- CPUID_SS --> + <cpuid function='0x00000001' edx='0x08000000'/> + </feature> + <feature name='ht'> <!-- CPUID_HT --> + <cpuid function='0x00000001' edx='0x10000000'/> + </feature> + <feature name='tm'> <!-- CPUID_TM --> + <cpuid function='0x00000001' edx='0x20000000'/> + </feature> + <feature name='ia64'> <!-- CPUID_IA64 --> + <cpuid function='0x00000001' edx='0x40000000'/> + </feature> + <feature name='pbe'> <!-- CPUID_PBE --> + <cpuid function='0x00000001' edx='0x80000000'/> + </feature> + + <!-- standard features, ECX --> + <feature name='pni'> <!-- CPUID_EXT_SSE3 --> + <cpuid function='0x00000001' ecx='0x00000001'/> + </feature> + <feature name='monitor'> <!-- CPUID_EXT_MONITOR --> + <cpuid function='0x00000001' ecx='0x00000008'/> + </feature> + <feature name='ds_cpl'> <!-- CPUID_EXT_DSCPL --> + <cpuid function='0x00000001' ecx='0x00000010'/> + </feature> + <feature name='vmx'> <!-- CPUID_EXT_VMX --> + <cpuid function='0x00000001' ecx='0x00000020'/> + </feature> + <feature name='est'> <!-- CPUID_EXT_EST --> + <cpuid function='0x00000001' ecx='0x00000080'/> + </feature> + <feature name='tm2'> <!-- CPUID_EXT_TM2 --> + <cpuid function='0x00000001' ecx='0x00000100'/> + </feature> + <feature name='ssse3'> <!-- CPUID_EXT_SSSE3 --> + <cpuid function='0x00000001' ecx='0x00000200'/> + </feature> + <feature name='cid'> <!-- CPUID_EXT_CID --> + <cpuid function='0x00000001' ecx='0x00000400'/> + </feature> + <feature name='cx16'> <!-- CPUID_EXT_CX16 --> + <cpuid function='0x00000001' ecx='0x00002000'/> + </feature> + <feature name='xtpr'> <!-- CPUID_EXT_XTPR --> + <cpuid function='0x00000001' ecx='0x00004000'/> + </feature> + <feature name='dca'> <!-- CPUID_EXT_DCA --> + <cpuid function='0x00000001' ecx='0x00040000'/> + </feature> + <feature name='x2apic'> <!-- CPUID_EXT_X2APIC --> + <cpuid function='0x00000001' ecx='0x00200000'/> + </feature> + <feature name='popcnt'> <!-- CPUID_EXT_POPCNT --> + <cpuid function='0x00000001' ecx='0x00800000'/> + </feature> + <feature name='hypervisor'> <!-- CPUID_EXT_HYPERVISOR --> + <cpuid function='0x00000001' ecx='0x80000000'/> + </feature> + + <!-- extended features, EDX --> + <feature name='syscall'> <!-- CPUID_EXT2_SYSCALL --> + <cpuid function='0x80000001' edx='0x00000800'/> + </feature> + <feature name='nx'> <!-- CPUID_EXT2_NX --> + <cpuid function='0x80000001' edx='0x00100000'/> + </feature> + <feature name='mmxext'> <!-- CPUID_EXT2_MMXEXT --> + <cpuid function='0x80000001' edx='0x00400000'/> + </feature> + <feature name='fxsr_opt'> <!-- CPUID_EXT2_FFXSR --> + <cpuid function='0x80000001' edx='0x02000000'/> + </feature> + <feature name='pdpe1gb'> <!-- CPUID_EXT2_PDPE1GB --> + <cpuid function='0x80000001' edx='0x04000000'/> + </feature> + <feature name='rdtscp'> <!-- CPUID_EXT2_RDTSCP --> + <cpuid function='0x80000001' edx='0x08000000'/> + </feature> + <feature name='lm'> <!-- CPUID_EXT2_LM --> + <cpuid function='0x80000001' edx='0x20000000'/> + </feature> + <feature name='3dnowext'> <!-- CPUID_EXT2_3DNOWEXT --> + <cpuid function='0x80000001' edx='0x40000000'/> + </feature> + <feature name='3dnow'> <!-- CPUID_EXT2_3DNOW --> + <cpuid function='0x80000001' edx='0x80000000'/> + </feature> + + <!-- extended features, ECX --> + <feature name='lahf_lm'> <!-- CPUID_EXT3_LAHF_LM --> + <cpuid function='0x80000001' ecx='0x00000001'/> + </feature> + <feature name='cmp_legacy'> <!-- CPUID_EXT3_CMP_LEG --> + <cpuid function='0x80000001' ecx='0x00000002'/> + </feature> + <feature name='svm'> <!-- CPUID_EXT3_SVM --> + <cpuid function='0x80000001' ecx='0x00000004'/> + </feature> + <feature name='extapic'> <!-- CPUID_EXT3_EXTAPIC --> + <cpuid function='0x80000001' ecx='0x00000008'/> + </feature> + <feature name='cr8legacy'> <!-- CPUID_EXT3_CR8LEG --> + <cpuid function='0x80000001' ecx='0x00000010'/> + </feature> + <feature name='abm'> <!-- CPUID_EXT3_ABM --> + <cpuid function='0x80000001' ecx='0x00000020'/> + </feature> + <feature name='sse4a'> <!-- CPUID_EXT3_SSE4A --> + <cpuid function='0x80000001' ecx='0x00000040'/> + </feature> + <feature name='misalignsse'> <!-- CPUID_EXT3_MISALIGNSSE --> + <cpuid function='0x80000001' ecx='0x00000080'/> + </feature> + <feature name='3dnowprefetch'> <!-- CPUID_EXT3_3DNOWPREFETCH --> + <cpuid function='0x80000001' ecx='0x00000100'/> + </feature> + <feature name='osvw'> <!-- CPUID_EXT3_OSVW --> + <cpuid function='0x80000001' ecx='0x00000200'/> + </feature> + <feature name='skinit'> <!-- CPUID_EXT3_SKINIT --> + <cpuid function='0x80000001' ecx='0x00001000'/> + </feature> + <feature name='wdt'> + <cpuid function='0x80000001' ecx='0x00002000'/> + </feature> + + <!-- models --> + <model name="486"> + <feature name="fpu"/> + <feature name="vme"/> + <feature name="pse"/> + </model> + + <model name="pentium"> + <model name="486"/> + <feature name="de"/> + <feature name="tsc"/> + <feature name="msr"/> + <feature name="mce"/> + <feature name="cx8"/> + <feature name="mmx"/> + </model> + + <model name="pentium2"> + <model name="pentium"/> + <feature name="pae"/> + <feature name="sep"/> + <feature name="mtrr"/> + <feature name="pge"/> + <feature name="mca"/> + <feature name="cmov"/> + <feature name="pat"/> + <feature name="pse36"/> + <feature name="fxsr"/> + </model> + + <model name="pentium3"> + <model name="pentium2"/> + <feature name="sse"/> + </model> + + <model name="pentiumpro"> + <feature name="fpu"/> + <feature name="de"/> + <feature name="pse"/> + <feature name="tsc"/> + <feature name="msr"/> + <feature name="mce"/> + <feature name="cx8"/> + <feature name="pge"/> + <feature name="cmov"/> + <feature name="pat"/> + <feature name="fxsr"/> + <feature name="mmx"/> + <feature name="sse"/> + <feature name="sse2"/> + <feature name="pae"/> + <feature name="sep"/> + <feature name="apic"/> + </model> + + <model name="qemu32"> + <model name="pentiumpro"/> + <feature name="pni"/> + </model> + + <model name="coreduo"> + <model name="pentiumpro"/> + <feature name="vme"/> + <feature name="mtrr"/> + <feature name="clflush"/> + <feature name="mca"/> + <feature name="pni"/> + <feature name="monitor"/> + <feature name="nx"/> + </model> + + <model name="qemu64"> + <model name="pentiumpro"/> + <feature name="mtrr"/> + <feature name="clflush"/> + <feature name="mca"/> + <feature name="pse36"/> + <feature name="pni"/> + <feature name="lm"/> + <feature name="syscall"/> + <feature name="nx"/> + <feature name="svm"/> + </model> + + <model name="core2duo"> + <model name="pentiumpro"/> + <feature name="mtrr"/> + <feature name="clflush"/> + <feature name="mca"/> + <feature name="pse36"/> + <feature name="pni"/> + <feature name="monitor"/> + <feature name="ssse3"/> + <feature name="lm"/> + <feature name="syscall"/> + <feature name="nx"/> + </model> + </arch> +</cpus> -- 1.6.5.6

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/esx/esx_driver.c | 1 + src/lxc/lxc_driver.c | 1 + src/openvz/openvz_driver.c | 1 + src/test/test_driver.c | 1 + src/uml/uml_driver.c | 1 + src/vbox/vbox_tmpl.c | 1 + 6 files changed, 6 insertions(+), 0 deletions(-) diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index ea464a3..c7b9e06 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3428,6 +3428,7 @@ static virDriver esxDriver = { esxIsSecure, /* isSecure */ esxDomainIsActive, /* domainIsActive */ esxDomainIsPersistent, /* domainIsPersistent */ + NULL, /* cpuCompare */ }; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index c8e2dca..1ce7ad6 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2454,6 +2454,7 @@ static virDriver lxcDriver = { lxcIsSecure, lxcDomainIsActive, lxcDomainIsPersistent, + NULL, /* cpuCompare */ }; static virStateDriver lxcStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 1c0fccc..b62070f 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1533,6 +1533,7 @@ static virDriver openvzDriver = { openvzIsSecure, openvzDomainIsActive, openvzDomainIsPersistent, + NULL, /* cpuCompare */ }; int openvzRegister(void) { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 7db9a4c..5c61f24 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5236,6 +5236,7 @@ static virDriver testDriver = { testIsSecure, /* isEncrypted */ testDomainIsActive, /* domainIsActive */ testDomainIsPersistent, /* domainIsPersistent */ + NULL, /* cpuCompare */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 48ef103..e6ba773 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1922,6 +1922,7 @@ static virDriver umlDriver = { umlIsSecure, umlDomainIsActive, umlDomainIsPersistent, + NULL, /* cpuCompare */ }; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index d6b681c..bede574 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7049,6 +7049,7 @@ virDriver NAME(Driver) = { vboxIsSecure, vboxDomainIsActive, vboxDomainIsPersistent, + NULL, /* cpuCompare */ }; virNetworkDriver NAME(NetworkDriver) = { -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:04:08AM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/esx/esx_driver.c | 1 + src/lxc/lxc_driver.c | 1 + src/openvz/openvz_driver.c | 1 + src/test/test_driver.c | 1 + src/uml/uml_driver.c | 1 + src/vbox/vbox_tmpl.c | 1 + 6 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index ea464a3..c7b9e06 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3428,6 +3428,7 @@ static virDriver esxDriver = { esxIsSecure, /* isSecure */ esxDomainIsActive, /* domainIsActive */ esxDomainIsPersistent, /* domainIsPersistent */ + NULL, /* cpuCompare */ };
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index c8e2dca..1ce7ad6 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2454,6 +2454,7 @@ static virDriver lxcDriver = { lxcIsSecure, lxcDomainIsActive, lxcDomainIsPersistent, + NULL, /* cpuCompare */ };
static virStateDriver lxcStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 1c0fccc..b62070f 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1533,6 +1533,7 @@ static virDriver openvzDriver = { openvzIsSecure, openvzDomainIsActive, openvzDomainIsPersistent, + NULL, /* cpuCompare */ };
int openvzRegister(void) { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 7db9a4c..5c61f24 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5236,6 +5236,7 @@ static virDriver testDriver = { testIsSecure, /* isEncrypted */ testDomainIsActive, /* domainIsActive */ testDomainIsPersistent, /* domainIsPersistent */ + NULL, /* cpuCompare */ };
static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 48ef103..e6ba773 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1922,6 +1922,7 @@ static virDriver umlDriver = { umlIsSecure, umlDomainIsActive, umlDomainIsPersistent, + NULL, /* cpuCompare */ };
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index d6b681c..bede574 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7049,6 +7049,7 @@ virDriver NAME(Driver) = { vboxIsSecure, vboxDomainIsActive, vboxDomainIsPersistent, + NULL, /* cpuCompare */ };
virNetworkDriver NAME(NetworkDriver) = {
This one needs to be merged with patch 4, otherwise you can't compile the intermediate steps. Also need to add Xen, Phyp & OpenNebula drivers to it. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

...
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index d6b681c..bede574 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7049,6 +7049,7 @@ virDriver NAME(Driver) = { vboxIsSecure, vboxDomainIsActive, vboxDomainIsPersistent, + NULL, /* cpuCompare */ };
virNetworkDriver NAME(NetworkDriver) = {
This one needs to be merged with patch 4, otherwise you can't compile the intermediate steps.
OK. I guess api_extension.html needs updating then. It suggests this kind of patch separation...
Also need to add Xen, Phyp & OpenNebula drivers to it.
Ah. My bad, I completely forgot about them as they were not compiled when I was testing this patchset. Jirka

On Thu, Dec 17, 2009 at 12:30:45PM +0100, Jiri Denemark wrote:
...
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index d6b681c..bede574 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7049,6 +7049,7 @@ virDriver NAME(Driver) = { vboxIsSecure, vboxDomainIsActive, vboxDomainIsPersistent, + NULL, /* cpuCompare */ };
virNetworkDriver NAME(NetworkDriver) = {
This one needs to be merged with patch 4, otherwise you can't compile the intermediate steps.
OK. I guess api_extension.html needs updating then. It suggests this kind of patch separation...
Oh, I didn't realize that. Basically the key idea is that you should be able to compile successfully without warnings, at each step in the pach series. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_conf.c | 401 +++++++++++++++++++++++++++++++++++++++++++++--- src/qemu/qemu_conf.h | 7 + src/qemu/qemu_driver.c | 43 ++++-- 3 files changed, 421 insertions(+), 30 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 2ee2042..303a1bf 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -52,6 +52,7 @@ #include "nodeinfo.h" #include "logging.h" #include "network.h" +#include "cpu/cpu.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -608,6 +609,151 @@ qemudGetOldMachines(const char *ostype, return 0; } + +typedef int +(*qemudParseCPUModels)(const char *output, + unsigned int *retcount, + const char ***retcpus); + +/* Format: + * <arch> <model> + */ +static int +qemudParseX86Models(const char *output, + unsigned int *retcount, + const char ***retcpus) +{ + const char *p = output; + const char *next; + unsigned int count = 0; + const char **cpus = NULL; + int i; + + do { + const char *t; + + if ((next = strchr(p, '\n'))) + next++; + + if (!(t = strchr(p, ' ')) || (next && t >= next)) + continue; + + if (!STRPREFIX(p, "x86")) + continue; + + p = t; + while (*p == ' ') + p++; + + if (*p == '\0' || *p == '\n') + continue; + + if (retcpus) { + if (VIR_REALLOC_N(cpus, count + 1) < 0) + goto error; + + if (next) + cpus[count] = strndup(p, next - p - 1); + else + cpus[count] = strdup(p); + + if (!cpus[count]) + goto error; + } + count++; + } while ((p = next)); + + if (retcount) + *retcount = count; + if (retcpus) + *retcpus = cpus; + + return 0; + +error: + if (cpus) { + for (i = 0; i < count; i++) + VIR_FREE(cpus[i]); + } + VIR_FREE(cpus); + + return -1; +} + + +int +qemudProbeCPUModels(const char *qemu, + const char *arch, + unsigned int *count, + const char ***cpus) +{ + const char *const qemuarg[] = { qemu, "-cpu", "?", NULL }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; + char *output = NULL; + int newstdout = -1; + int ret = -1; + pid_t child; + int status; + int len; + qemudParseCPUModels parse; + + if (count) + *count = 0; + if (cpus) + *cpus = NULL; + + if (STREQ(arch, "i686") || STREQ(arch, "x86_64")) + parse = qemudParseX86Models; + else { + VIR_DEBUG(_("don't know how to parse %s CPU models"), arch); + return 0; + } + + if (virExec(NULL, qemuarg, qemuenv, NULL, + &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) + return -1; + + len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); + if (len < 0) { + virReportSystemError(NULL, errno, "%s", + _("Unable to read QEMU supported CPU models")); + goto cleanup; + } + + if (parse(output, count, cpus) < 0) { + virReportOOMError(NULL); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(output); + if (close(newstdout) < 0) + ret = -1; + +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + ret = -1; + } + /* Check & log unexpected exit status, but don't fail, + * as there's really no need to throw an error if we did + * actually read a valid version number above */ + if (WEXITSTATUS(status) != 0) { + VIR_WARN(_("Unexpected exit status '%d', qemu probably failed"), + WEXITSTATUS(status)); + } + + return ret; +} + + static int qemudCapsInitGuest(virCapsPtr caps, virCapsPtr old_caps, @@ -624,6 +770,7 @@ qemudCapsInitGuest(virCapsPtr caps, virCapsGuestMachinePtr *machines = NULL; int nmachines = 0; struct stat st; + unsigned int ncpus; /* Check for existance of base emulator, or alternate base * which can be used with magic cpu choice @@ -725,6 +872,11 @@ qemudCapsInitGuest(virCapsPtr caps, guest->arch.defaultInfo.emulator_mtime = binary_mtime; + if (qemudProbeCPUModels(binary, info->arch, &ncpus, NULL) == 0 + && ncpus > 0 + && !virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0)) + return -1; + if (hvm) { if (virCapabilitiesAddGuestDomain(guest, "qemu", @@ -808,6 +960,49 @@ qemudCapsInitGuest(virCapsPtr caps, return 0; } + +static int +qemudCapsInitCPU(virCapsPtr caps, + const char *arch) +{ + virCPUDefPtr cpu = NULL; + union cpuData *data = NULL; + virNodeInfo nodeinfo; + int ret = -1; + + if (VIR_ALLOC(cpu) < 0 + || !(cpu->arch = strdup(arch))) { + virReportOOMError(NULL); + goto error; + } + + if (nodeGetInfo(NULL, &nodeinfo)) + goto error; + + cpu->type = VIR_CPU_TYPE_HOST; + cpu->sockets = nodeinfo.sockets * nodeinfo.nodes; + cpu->cores = nodeinfo.cores; + cpu->threads = nodeinfo.threads; + + if (!(data = cpuNodeData(NULL, arch)) + || cpuDecode(NULL, cpu, data, 0, NULL) < 0) + goto error; + + caps->host.cpu = cpu; + + ret = 0; + +cleanup: + cpuDataFree(NULL, arch, data); + + return ret; + +error: + virCPUDefFree(cpu); + goto cleanup; +} + + virCapsPtr qemudCapsInit(virCapsPtr old_caps) { struct utsname utsname; virCapsPtr caps; @@ -832,8 +1027,17 @@ virCapsPtr qemudCapsInit(virCapsPtr old_caps) { VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities"); } + if (old_caps == NULL || old_caps->host.cpu == NULL) { + if (qemudCapsInitCPU(caps, utsname.machine) < 0) + VIR_WARN0("Failed to get host CPU"); + } + else { + caps->host.cpu = old_caps->host.cpu; + old_caps->host.cpu = NULL; + } + virCapabilitiesAddHostMigrateTransport(caps, - "tcp"); + "Tcp"); /* First the pure HVM guests */ for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_hvm) ; i++) @@ -1563,6 +1767,99 @@ static void qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev, } } + +static int +qemudBuildCommandLineCPU(virConnectPtr conn, + const struct qemud_driver *driver, + const virDomainDefPtr def, + const char *emulator, + const struct utsname *ut, + char **opt) +{ + const virCPUDefPtr host = driver->caps->host.cpu; + virCPUDefPtr guest = NULL; + unsigned int ncpus; + const char **cpus = NULL; + union cpuData *data = NULL; + int ret = -1; + virBuffer buf = VIR_BUFFER_INITIALIZER; + int i; + + if (qemudProbeCPUModels(emulator, ut->machine, &ncpus, &cpus) < 0) + goto cleanup; + + if (ncpus > 0 && host && def->cpu && def->cpu->model) { + virCPUCompareResult cmp; + + cmp = cpuGuestData(conn, host, def->cpu, &data); + switch (cmp) { + case VIR_CPU_COMPARE_INCOMPATIBLE: + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("guest CPU is not compatible with host CPU")); + /* fall through */ + case VIR_CPU_COMPARE_ERROR: + goto cleanup; + + default: + break; + } + + if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(ut->machine))) + goto no_memory; + + if (qemudProbeCPUModels(emulator, ut->machine, &ncpus, &cpus) < 0 + || cpuDecode(conn, guest, data, ncpus, cpus) < 0) + goto cleanup; + + virBufferVSprintf(&buf, "%s", guest->model); + for (i = 0; i < guest->nfeatures; i++) + virBufferVSprintf(&buf, ",+%s", guest->features[i].name); + } + else { + /* + * Need to force a 32-bit guest CPU type if + * + * 1. guest OS is i686 + * 2. host OS is x86_64 + * 3. emulator is qemu-kvm or kvm + * + * Or + * + * 1. guest OS is i686 + * 2. emulator is qemu-system-x86_64 + */ + if (STREQ(def->os.arch, "i686") && + ((STREQ(ut->machine, "x86_64") && + strstr(emulator, "kvm")) || + strstr(emulator, "x86_64"))) + virBufferAddLit(&buf, "qemu32"); + } + + if (virBufferError(&buf)) + goto no_memory; + + *opt = virBufferContentAndReset(&buf); + + ret = 0; + +cleanup: + virCPUDefFree(guest); + cpuDataFree(conn, ut->machine, data); + + if (cpus) { + for (i = 0; i < ncpus; i++) + VIR_FREE(cpus[i]); + VIR_FREE(cpus); + } + + return ret; + +no_memory: + virReportOOMError(conn); + goto cleanup; +} + + #define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" @@ -1610,7 +1907,7 @@ int qemudBuildCommandLine(virConnectPtr conn, const char *emulator; char uuid[VIR_UUID_STRING_BUFLEN]; char domid[50]; - const char *cpu = NULL; + char *cpu; uname_normalize(&ut); @@ -1670,24 +1967,6 @@ int qemudBuildCommandLine(virConnectPtr conn, def->virtType == VIR_DOMAIN_VIRT_KVM) enableKVM = 1; - /* - * Need to force a 32-bit guest CPU type if - * - * 1. guest OS is i686 - * 2. host OS is x86_64 - * 3. emulator is qemu-kvm or kvm - * - * Or - * - * 1. guest OS is i686 - * 2. emulator is qemu-system-x86_64 - */ - if (STREQ(def->os.arch, "i686") && - ((STREQ(ut.machine, "x86_64") && - strstr(emulator, "kvm")) || - strstr(emulator, "x86_64"))) - cpu = "qemu32"; - #define ADD_ARG_SPACE \ do { \ if (qargc == qarga) { \ @@ -1788,9 +2067,14 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG_LIT("-M"); ADD_ARG_LIT(def->os.machine); } + + if (qemudBuildCommandLineCPU(conn, driver, def, emulator, &ut, &cpu) < 0) + goto error; + if (cpu) { ADD_ARG_LIT("-cpu"); ADD_ARG_LIT(cpu); + VIR_FREE(cpu); } if (disableKQEMU) @@ -3429,6 +3713,79 @@ error: return NULL; } + +static int +qemuParseCommandLineCPU(virConnectPtr conn, + virDomainDefPtr dom, + const char *val) +{ + virCPUDefPtr cpu; + const char *p = val; + const char *next; + + if (VIR_ALLOC(cpu) < 0) + goto no_memory; + + cpu->type = VIR_CPU_TYPE_GUEST; + + do { + if (*p == '\0' || *p == ',') + goto syntax; + + if ((next = strchr(p, ','))) + next++; + + if (!cpu->model) { + if (next) + cpu->model = strndup(p, next - p - 1); + else + cpu->model = strdup(p); + + if (!cpu->model) + goto no_memory; + } + else if (*p == '+' || *p == '-') { + char *feature; + int policy; + int ret; + + if (*p == '+') + policy = VIR_CPU_FEATURE_REQUIRE; + else + policy = VIR_CPU_FEATURE_DISABLE; + + p++; + if (*p == '\0' || *p == ',') + goto syntax; + + if (next) + feature = strndup(p, next - p - 1); + else + feature = strdup(p); + + ret = virCPUDefAddFeature(conn, cpu, feature, policy); + VIR_FREE(feature); + if (ret < 0) + goto error; + } + } while ((p = next)); + + dom->cpu = cpu; + return 0; + +syntax: + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown CPU syntax '%s'"), val); + goto error; + +no_memory: + virReportOOMError(conn); +error: + virCPUDefFree(cpu); + return -1; +} + + /* * Analyse the env and argv settings and reconstruct a * virDomainDefPtr representing these settings as closely @@ -3856,6 +4213,10 @@ virDomainDefPtr qemuParseCommandLine(virConnectPtr conn, _("unknown video adapter type '%s'"), val); goto error; } + } else if (STREQ(arg, "-cpu")) { + WANT_VALUE(); + if (qemuParseCommandLineCPU(conn, def, val) < 0) + goto error; } else if (STREQ(arg, "-domid")) { WANT_VALUE(); /* ignore, generted on the fly */ diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 248677a..e958850 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -37,6 +37,8 @@ #include "security/security_driver.h" #include "cgroup.h" #include "pci.h" +#include "cpu_conf.h" +#include "driver.h" #define qemudDebug(fmt, ...) do {} while(0) @@ -203,6 +205,11 @@ int qemudProbeMachineTypes (const char *binary, virCapsGuestMachinePtr **machines, int *nmachines); +int qemudProbeCPUModels (const char *qemu, + const char *arch, + unsigned int *count, + const char ***cpus); + int qemudCanonicalizeMachine (struct qemud_driver *driver, virDomainDefPtr def); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9ef6c35..ceabff2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -73,6 +73,7 @@ #include "cgroup.h" #include "libvirt_internal.h" #include "xml.h" +#include "cpu/cpu.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -2844,24 +2845,19 @@ static int qemudGetMaxVCPUs(virConnectPtr conn, const char *type) { static char *qemudGetCapabilities(virConnectPtr conn) { struct qemud_driver *driver = conn->privateData; - virCapsPtr caps; + virCapsPtr caps = NULL; char *xml = NULL; qemuDriverLock(driver); - if ((caps = qemudCapsInit(qemu_driver->caps)) == NULL) { - virReportOOMError(conn); - goto cleanup; - } + if ((caps = qemudCapsInit(qemu_driver->caps)) == NULL) + goto no_memory; caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc; caps->privateDataFreeFunc = qemuDomainObjPrivateFree; if (qemu_driver->securityDriver && - qemudSecurityCapsInit(qemu_driver->securityDriver, caps) < 0) { - virCapabilitiesFree(caps); - virReportOOMError(conn); - goto cleanup; - } + qemudSecurityCapsInit(qemu_driver->securityDriver, caps) < 0) + goto no_memory; virCapabilitiesFree(qemu_driver->caps); qemu_driver->caps = caps; @@ -2873,6 +2869,11 @@ cleanup: qemuDriverUnlock(driver); return xml; + +no_memory: + virCapabilitiesFree(caps); + virReportOOMError(conn); + goto cleanup; } @@ -7849,6 +7850,27 @@ out: return ret; } +static int +qemuCPUCompare(virConnectPtr conn, + const char *xmlDesc) +{ + struct qemud_driver *driver = conn->privateData; + int ret = VIR_CPU_COMPARE_ERROR; + + qemuDriverLock(driver); + + if (!driver->caps || !driver->caps->host.cpu) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, + "%s", _("cannot get host CPU capabilities")); + } + else + ret = cpuCompareXML(conn, driver->caps->host.cpu, xmlDesc); + + qemuDriverUnlock(driver); + + return ret; +} + static virDriver qemuDriver = { VIR_DRV_QEMU, "QEMU", @@ -7923,6 +7945,7 @@ static virDriver qemuDriver = { qemuIsSecure, qemuDomainIsActive, qemuDomainIsPersistent, + qemuCPUCompare, /* cpuCompare */ }; -- 1.6.5.6

On Wed, Dec 16, 2009 at 12:04:09AM +0100, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_conf.c | 401 +++++++++++++++++++++++++++++++++++++++++++++--- src/qemu/qemu_conf.h | 7 + src/qemu/qemu_driver.c | 43 ++++-- 3 files changed, 421 insertions(+), 30 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c + +int +qemudProbeCPUModels(const char *qemu, + const char *arch, + unsigned int *count, + const char ***cpus) +{ + const char *const qemuarg[] = { qemu, "-cpu", "?", NULL }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; + char *output = NULL; + int newstdout = -1; + int ret = -1; + pid_t child; + int status; + int len; + qemudParseCPUModels parse; + + if (count) + *count = 0; + if (cpus) + *cpus = NULL; + + if (STREQ(arch, "i686") || STREQ(arch, "x86_64")) + parse = qemudParseX86Models; + else { + VIR_DEBUG(_("don't know how to parse %s CPU models"), arch); + return 0; + } + + if (virExec(NULL, qemuarg, qemuenv, NULL, + &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) + return -1; + + len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); + if (len < 0) { + virReportSystemError(NULL, errno, "%s", + _("Unable to read QEMU supported CPU models")); + goto cleanup; + } + + if (parse(output, count, cpus) < 0) {
Can you put this in a nice namespace, qemuParseCPUs or something along those lines.
@@ -808,6 +960,49 @@ qemudCapsInitGuest(virCapsPtr caps, return 0; }
+ +static int +qemudCapsInitCPU(virCapsPtr caps, + const char *arch) +{ + virCPUDefPtr cpu = NULL; + union cpuData *data = NULL; + virNodeInfo nodeinfo; + int ret = -1; + + if (VIR_ALLOC(cpu) < 0 + || !(cpu->arch = strdup(arch))) { + virReportOOMError(NULL); + goto error; + } + + if (nodeGetInfo(NULL, &nodeinfo)) + goto error; + + cpu->type = VIR_CPU_TYPE_HOST; + cpu->sockets = nodeinfo.sockets * nodeinfo.nodes;
I'm not sure this is the right thing todo here, since it means this data for 'sockets' differs from that shown by the nodeinfo command. If an app needs the total number of sockets, they can already calculate that either from the nodeinfo API, or from the NUMA topology data in the cpabilities, so we don't need to do it here too. Just make it match nodeinfo.sockets.
+ cpu->cores = nodeinfo.cores; + cpu->threads = nodeinfo.threads; + + if (!(data = cpuNodeData(NULL, arch)) + || cpuDecode(NULL, cpu, data, 0, NULL) < 0) + goto error; + + caps->host.cpu = cpu; + + ret = 0; + +cleanup: + cpuDataFree(NULL, arch, data); + + return ret; + +error: + virCPUDefFree(cpu); + goto cleanup; +} + + virCapsPtr qemudCapsInit(virCapsPtr old_caps) { struct utsname utsname; virCapsPtr caps; @@ -832,8 +1027,17 @@ virCapsPtr qemudCapsInit(virCapsPtr old_caps) { VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities"); }
+ if (old_caps == NULL || old_caps->host.cpu == NULL) { + if (qemudCapsInitCPU(caps, utsname.machine) < 0) + VIR_WARN0("Failed to get host CPU"); + } + else { + caps->host.cpu = old_caps->host.cpu; + old_caps->host.cpu = NULL; + } + virCapabilitiesAddHostMigrateTransport(caps, - "tcp"); + "Tcp");
Why this change ? The migrate transport is the uri prefix, so this is changing the semantics.
/* First the pure HVM guests */ for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_hvm) ; i++) @@ -1563,6 +1767,99 @@ static void qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev, } }
+ +static int +qemudBuildCommandLineCPU(virConnectPtr conn, + const struct qemud_driver *driver, + const virDomainDefPtr def, + const char *emulator, + const struct utsname *ut, + char **opt) +{ + const virCPUDefPtr host = driver->caps->host.cpu; + virCPUDefPtr guest = NULL; + unsigned int ncpus; + const char **cpus = NULL; + union cpuData *data = NULL; + int ret = -1; + virBuffer buf = VIR_BUFFER_INITIALIZER; + int i; + + if (qemudProbeCPUModels(emulator, ut->machine, &ncpus, &cpus) < 0) + goto cleanup; + + if (ncpus > 0 && host && def->cpu && def->cpu->model) { + virCPUCompareResult cmp; + + cmp = cpuGuestData(conn, host, def->cpu, &data); + switch (cmp) { + case VIR_CPU_COMPARE_INCOMPATIBLE: + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("guest CPU is not compatible with host CPU")); + /* fall through */ + case VIR_CPU_COMPARE_ERROR: + goto cleanup; + + default: + break; + } + + if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(ut->machine))) + goto no_memory; + + if (qemudProbeCPUModels(emulator, ut->machine, &ncpus, &cpus) < 0 + || cpuDecode(conn, guest, data, ncpus, cpus) < 0) + goto cleanup;
Can't we avoid calling Probe again here, since we alrady did this 20 lines earlier. This second time will leak memory from the first time.
+ + virBufferVSprintf(&buf, "%s", guest->model); + for (i = 0; i < guest->nfeatures; i++) + virBufferVSprintf(&buf, ",+%s", guest->features[i].name); + } + else { + /* + * Need to force a 32-bit guest CPU type if + * + * 1. guest OS is i686 + * 2. host OS is x86_64 + * 3. emulator is qemu-kvm or kvm + * + * Or + * + * 1. guest OS is i686 + * 2. emulator is qemu-system-x86_64 + */ + if (STREQ(def->os.arch, "i686") && + ((STREQ(ut->machine, "x86_64") && + strstr(emulator, "kvm")) || + strstr(emulator, "x86_64"))) + virBufferAddLit(&buf, "qemu32"); + }
Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c + +int +qemudProbeCPUModels(const char *qemu, + const char *arch, + unsigned int *count, + const char ***cpus) +{ ... + qemudParseCPUModels parse; + + if (count) + *count = 0; + if (cpus) + *cpus = NULL; + ... + if (len < 0) { + virReportSystemError(NULL, errno, "%s", + _("Unable to read QEMU supported CPU models")); + goto cleanup; + } + + if (parse(output, count, cpus) < 0) {
Can you put this in a nice namespace, qemuParseCPUs or something along those lines.
This was discussed on IRC and "no" is the result. It's just a local variable...
+static int +qemudCapsInitCPU(virCapsPtr caps, + const char *arch) +{ + virCPUDefPtr cpu = NULL; + union cpuData *data = NULL; + virNodeInfo nodeinfo; + int ret = -1; + + if (VIR_ALLOC(cpu) < 0 + || !(cpu->arch = strdup(arch))) { + virReportOOMError(NULL); + goto error; + } + + if (nodeGetInfo(NULL, &nodeinfo)) + goto error; + + cpu->type = VIR_CPU_TYPE_HOST; + cpu->sockets = nodeinfo.sockets * nodeinfo.nodes;
I'm not sure this is the right thing todo here, since it means this data for 'sockets' differs from that shown by the nodeinfo command. If an app needs the total number of sockets, they can already calculate that either from the nodeinfo API, or from the NUMA topology data in the cpabilities, so we don't need to do it here too. Just make it match nodeinfo.sockets.
OK, makes sense.
@@ -832,8 +1027,17 @@ virCapsPtr qemudCapsInit(virCapsPtr old_caps) { VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities"); }
+ if (old_caps == NULL || old_caps->host.cpu == NULL) { + if (qemudCapsInitCPU(caps, utsname.machine) < 0) + VIR_WARN0("Failed to get host CPU"); + } + else { + caps->host.cpu = old_caps->host.cpu; + old_caps->host.cpu = NULL; + } + virCapabilitiesAddHostMigrateTransport(caps, - "tcp"); + "Tcp");
Why this change ? The migrate transport is the uri prefix, so this is changing the semantics.
Ah, it must have happened when rebasing to latest master and solving conflicts. It looks like I hit ~ by accident or something like that. Thanks for spotting that.
+ if (qemudProbeCPUModels(emulator, ut->machine, &ncpus, &cpus) < 0) + goto cleanup; + + if (ncpus > 0 && host && def->cpu && def->cpu->model) { + virCPUCompareResult cmp; + + cmp = cpuGuestData(conn, host, def->cpu, &data); + switch (cmp) { + case VIR_CPU_COMPARE_INCOMPATIBLE: + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("guest CPU is not compatible with host CPU")); + /* fall through */ + case VIR_CPU_COMPARE_ERROR: + goto cleanup; + + default: + break; + } + + if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(ut->machine))) + goto no_memory; + + if (qemudProbeCPUModels(emulator, ut->machine, &ncpus, &cpus) < 0 + || cpuDecode(conn, guest, data, ncpus, cpus) < 0) + goto cleanup;
Can't we avoid calling Probe again here, since we alrady did this 20 lines earlier. This second time will leak memory from the first time.
Sure. The second call wasn't supposed to be there. Thanks. Jirka

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- tools/virsh.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 65 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 8f96ca8..e992999 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -6836,6 +6836,70 @@ cmdDetachDisk(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "cpu-compare" command + */ +static const vshCmdInfo info_cpu_compare[] = { + {"help", gettext_noop("compare host CPU with a CPU described by an XML file")}, + {"desc", gettext_noop("compare CPU with host CPU")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_cpu_compare[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML CPU description")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdCPUCompare(vshControl *ctl, const vshCmd *cmd) +{ + char *from; + int found; + int ret = TRUE; + char *buffer; + int result; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) + return FALSE; + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) + return FALSE; + + result = virConnectCompareCPU(ctl->conn, buffer); + free (buffer); + + switch (result) { + case VIR_CPU_COMPARE_INCOMPATIBLE: + vshPrint(ctl, _("CPU described in %s is incompatible with host CPU\n"), + from); + ret = FALSE; + break; + + case VIR_CPU_COMPARE_IDENTICAL: + vshPrint(ctl, _("CPU described in %s is identical to host CPU\n"), + from); + ret = TRUE; + break; + + case VIR_CPU_COMPARE_SUPERSET: + vshPrint(ctl, _("Host CPU is a superset of CPU described in %s\n"), + from); + ret = TRUE; + break; + + case VIR_CPU_COMPARE_ERROR: + default: + vshError(ctl, _("Failed to compare host CPU with %s"), from); + ret = FALSE; + } + + return ret; +} + /* Common code for the edit / net-edit / pool-edit functions which follow. */ static char * editWriteToTempFile (vshControl *ctl, const char *doc) @@ -7207,6 +7271,7 @@ static const vshCmdDef commands[] = { #ifndef WIN32 {"console", cmdConsole, opts_console, info_console}, #endif + {"cpu-compare", cmdCPUCompare, opts_cpu_compare, info_cpu_compare}, {"create", cmdCreate, opts_create, info_create}, {"start", cmdStart, opts_start, info_start}, {"destroy", cmdDestroy, opts_destroy, info_destroy}, -- 1.6.5.6

2009/12/16 Jiri Denemark <jdenemar@redhat.com>:
Hi,
This is a third version of CPU selection patchset.
Changes in version 3: - fix build and segfault on i386
Changes in version 2: - virConnectGetHostCPU() API call was completely removed - 'CPU driver implementation' (11/14) patch was dropped - virConnectCompareCPU() API call is implemented directly by hypervisor drivers - new cpuCompareXML() internal function to make virConnectCompareCPU() simpler
Jirka
You've put some relevant documentation about the CPU flag handling to the commit messages. IMHO this documentation should be collected and the libvirt user relevant part (for example from patch 1 v3) should go to the libvirt website sections about the domain/capabilities XML format and the libvirt library/driver developer related part (for example from patch 9 v3) should go to src/cpu/README, or something like that. Matthias

Changes in version 3: - fix build and segfault on i386
Changes in version 2: - virConnectGetHostCPU() API call was completely removed - 'CPU driver implementation' (11/14) patch was dropped - virConnectCompareCPU() API call is implemented directly by hypervisor drivers - new cpuCompareXML() internal function to make virConnectCompareCPU() simpler
Jirka
You've put some relevant documentation about the CPU flag handling to the commit messages. IMHO this documentation should be collected and the libvirt user relevant part (for example from patch 1 v3) should go to the libvirt website sections about the domain/capabilities XML format
Right, good idea.
and the libvirt library/driver developer related part (for example from patch 9 v3) should go to src/cpu/README, or something like that.
This is also a good idea but I'm not sure where to put the documentation. Is the src/cpu/README a good place or is there any better place for documenting internals of libvirt for driver developers? Perhaps HACKING? Although it seems to document a bit more general techniques used in libvirt. Jirka

On Thu, Dec 17, 2009 at 11:14:24AM +0100, Jiri Denemark wrote:
Changes in version 3: - fix build and segfault on i386
Changes in version 2: - virConnectGetHostCPU() API call was completely removed - 'CPU driver implementation' (11/14) patch was dropped - virConnectCompareCPU() API call is implemented directly by hypervisor drivers - new cpuCompareXML() internal function to make virConnectCompareCPU() simpler
Jirka
You've put some relevant documentation about the CPU flag handling to the commit messages. IMHO this documentation should be collected and the libvirt user relevant part (for example from patch 1 v3) should go to the libvirt website sections about the domain/capabilities XML format
Right, good idea.
and the libvirt library/driver developer related part (for example from patch 9 v3) should go to src/cpu/README, or something like that.
This is also a good idea but I'm not sure where to put the documentation. Is the src/cpu/README a good place or is there any better place for documenting internals of libvirt for driver developers? Perhaps HACKING? Although it seems to document a bit more general techniques used in libvirt.
Actually anything that's intended for driver developers can also go on the website in this section: http://libvirt.org/internals.html We should move other stuff we've written like my THREADS.txt guides to there too some time Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (4)
-
Daniel P. Berrange
-
Daniel Veillard
-
Jiri Denemark
-
Matthias Bolte