[libvirt] [PATCH 00/14] CPU selection patches

Hi, Here it is, finally. Jirka Jiri Denemark (14): 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 CPU driver implementation Set getHostCPU handler in drivers Implement CPU selection in QEMU driver Implement virsh commands configure.in | 20 + daemon/Makefile.am | 2 + daemon/libvirtd.c | 3 + daemon/remote.c | 42 ++ daemon/remote_dispatch_prototypes.h | 16 + docs/schemas/capability.rng | 46 ++- docs/schemas/domain.rng | 62 ++ include/libvirt/libvirt.h.in | 15 + include/libvirt/virterror.h | 2 + src/Makefile.am | 43 ++- 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 | 206 +++++++ src/cpu/cpu.h | 124 ++++ src/cpu/cpu_driver.c | 190 ++++++ src/cpu/cpu_driver.h | 40 ++ 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 | 1142 +++++++++++++++++++++++++++++++++++ src/cpu/cpu_x86.h | 31 + src/cpu/cpu_x86_data.h | 45 ++ src/datatypes.h | 2 + src/driver.h | 20 + src/esx/esx_driver.c | 1 + src/libvirt.c | 129 ++++ src/libvirt_private.syms | 19 + src/libvirt_public.syms | 6 + 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 | 37 +- src/remote/remote_driver.c | 125 ++++ src/remote/remote_protocol.c | 27 + src/remote/remote_protocol.x | 18 +- src/test/test_driver.c | 1 + src/uml/uml_driver.c | 1 + src/util/virterror.c | 8 + src/vbox/vbox_tmpl.c | 1 + tools/virsh.c | 65 ++ 47 files changed, 4029 insertions(+), 36 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_driver.c create mode 100644 src/cpu/cpu_driver.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

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

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- include/libvirt/libvirt.h.in | 15 +++++++++++++++ src/libvirt_public.syms | 6 ++++++ 2 files changed, 21 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6c3aded..ec7f475 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1676,6 +1676,21 @@ 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); + +char *virConnectGetHostCPU(virConnectPtr conn); #ifdef __cplusplus diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b4f57e7..2193151 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -343,4 +343,10 @@ LIBVIRT_0.7.3 { virInterfaceIsActive; } LIBVIRT_0.7.2; +LIBVIRT_0.7.5 { + global: + virConnectCompareCPU; + virConnectGetHostCPU; +} LIBVIRT_0.7.3; + # .... define new API here using predicted next version number .... -- 1.6.5.6

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/driver.h | 20 ++++++++++++++++++++ src/libvirt_private.syms | 1 + 2 files changed, 21 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 09ce8e2..16bf9bf 100644 --- a/src/driver.h +++ b/src/driver.h @@ -349,6 +349,9 @@ typedef int typedef int (*virDrvDomainIsPersistent)(virDomainPtr dom); +typedef char * + (*virDrvGetHostCPU)(virConnectPtr conn); + /** * _virDriver: * @@ -435,6 +438,7 @@ struct _virDriver { virDrvConnectIsSecure isSecure; virDrvDomainIsActive domainIsActive; virDrvDomainIsPersistent domainIsPersistent; + virDrvGetHostCPU getHostCPU; }; typedef int @@ -944,6 +948,21 @@ struct _virStreamDriver { }; +typedef int (*virDrvCPUCompare)(virConnectPtr conn, + const char *cpu); + + +typedef struct _virCPUDriver virCPUDriver; +typedef virCPUDriver *virCPUDriverPtr; +struct _virCPUDriver { + const char *name; /* the name of the driver */ + virDrvOpen open; + virDrvClose close; + + virDrvCPUCompare compare; +}; + + /* * Registration * TODO: also need ways to (des)activate a given driver @@ -955,6 +974,7 @@ int virRegisterInterfaceDriver(virInterfaceDriverPtr); int virRegisterStorageDriver(virStorageDriverPtr); int virRegisterDeviceMonitor(virDeviceMonitorPtr); int virRegisterSecretDriver(virSecretDriverPtr); +int virRegisterCPUDriver(virCPUDriverPtr); #ifdef WITH_LIBVIRTD int virRegisterStateDriver(virStateDriverPtr); #endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 9acd062..f10527a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -276,6 +276,7 @@ virRegisterStateDriver; virRegisterStorageDriver; virRegisterDeviceMonitor; virRegisterSecretDriver; +virRegisterCPUDriver; # json.h -- 1.6.5.6

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- include/libvirt/virterror.h | 1 + src/libvirt.c | 129 +++++++++++++++++++++++++++++++++++++++++++ src/util/virterror.c | 5 ++ 3 files changed, 135 insertions(+), 0 deletions(-) diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 30f88e8..85d562e 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -175,6 +175,7 @@ typedef enum { VIR_ERR_OPERATION_TIMEOUT, /* timeout occurred during operation */ VIR_ERR_MIGRATE_PERSIST_FAILED, /* a migration worked, but making the VM persist on the dest host failed */ + VIR_WAR_NO_CPU, /* failed to start cpu driver */ } virErrorNumber; /** diff --git a/src/libvirt.c b/src/libvirt.c index 008e322..997cdcc 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -87,6 +87,8 @@ static virDeviceMonitorPtr virDeviceMonitorTab[MAX_DRIVERS]; static int virDeviceMonitorTabCount = 0; static virSecretDriverPtr virSecretDriverTab[MAX_DRIVERS]; static int virSecretDriverTabCount = 0; +static virCPUDriverPtr virCPUDriverTab[MAX_DRIVERS]; +static int virCPUDriverTabCount = 0; #ifdef WITH_LIBVIRTD static virStateDriverPtr virStateDriverTab[MAX_DRIVERS]; static int virStateDriverTabCount = 0; @@ -745,6 +747,37 @@ virRegisterSecretDriver(virSecretDriverPtr driver) } /** + * virRegisterCPUDriver: + * @driver: pointer to a cpu driver block + * + * Register a CPU driver + * + * Returns the driver priority or -1 in case of error. + */ +int +virRegisterCPUDriver(virCPUDriverPtr driver) +{ + if (virInitialize() < 0) + return -1; + + if (driver == NULL) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + if (virCPUDriverTabCount >= MAX_DRIVERS) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + DEBUG ("registering %s as cpu driver %d", + driver->name, virCPUDriverTabCount); + + virCPUDriverTab[virCPUDriverTabCount] = driver; + return virCPUDriverTabCount++; +} + +/** * virRegisterDriver: * @driver: pointer to a driver block * @@ -1180,6 +1213,25 @@ do_open (const char *name, } } + for (i = 0; i < virCPUDriverTabCount; i++) { + res = virCPUDriverTab[i]->open (ret, auth, flags); + DEBUG("cpu driver %d %s returned %s", + i, virCPUDriverTab[i]->name, + res == VIR_DRV_OPEN_SUCCESS ? "SUCCESS" : + (res == VIR_DRV_OPEN_DECLINED ? "DECLINED" : + (res == VIR_DRV_OPEN_ERROR ? "ERROR" : "unknown status"))); + if (res == VIR_DRV_OPEN_ERROR) { + if (STREQ(virCPUDriverTab[i]->name, "remote")) { + virLibConnWarning (NULL, VIR_WAR_NO_CPU, + "Is the daemon running ?"); + } + break; + } else if (res == VIR_DRV_OPEN_SUCCESS) { + ret->cpuDriver = virCPUDriverTab[i]; + break; + } + } + return ret; failed: @@ -10701,3 +10753,80 @@ 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->cpuDriver != NULL && conn->cpuDriver->compare != NULL) { + int ret; + + ret = conn->cpuDriver->compare(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; +} + +/** + * virConnectGetHostCPU: + * @conn: virConnect connection + * + * Returns XML description of host CPU. The returned <cpu> element contains + * data from the <cpu> element of host capabilities which correspond to the + * structure of <cpu> element in domain configuration XML. + */ +char * +virConnectGetHostCPU(virConnectPtr conn) +{ + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return NULL; + } + + if (conn->driver != NULL && conn->driver->getHostCPU != NULL) { + char *ret; + + if ((ret = conn->driver->getHostCPU(conn)) != NULL) { + VIR_DEBUG("result=%s", ret); + return ret; + } + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + + /* Copy to connection error object for back compatibility */ + virSetConnError(conn); + return NULL; +} diff --git a/src/util/virterror.c b/src/util/virterror.c index b1a96f8..e70850b 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -1109,6 +1109,11 @@ virErrorMsg(virErrorNumber error, const char *info) errmsg = _("Failed to make domain persistent after migration"); else errmsg = _("Failed to make domain persistent after migration: %s"); + case VIR_WAR_NO_CPU: + if (info == NULL) + errmsg = _("Failed to find a cpu driver"); + else + errmsg = _("Failed to find a cpu driver: %s"); break; } return (errmsg); -- 1.6.5.6

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- daemon/remote_dispatch_prototypes.h | 16 ++++++++++++++++ src/remote/remote_protocol.c | 27 +++++++++++++++++++++++++++ src/remote/remote_protocol.x | 18 +++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletions(-) diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h index b05a9a4..a6c13f0 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -50,6 +50,14 @@ static int remoteDispatchClose( remote_error *err, void *args, void *ret); +static int remoteDispatchCpuCompare( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_cpu_compare_args *args, + remote_cpu_compare_ret *ret); static int remoteDispatchDomainAttachDevice( struct qemud_server *server, struct qemud_client *client, @@ -458,6 +466,14 @@ static int remoteDispatchGetCapabilities( remote_error *err, void *args, remote_get_capabilities_ret *ret); +static int remoteDispatchGetHostCpu( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + void *args, + remote_get_host_cpu_ret *ret); static int remoteDispatchGetHostname( struct qemud_server *server, struct qemud_client *client, diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index a10558a..221e81c 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -2857,6 +2857,33 @@ xdr_remote_interface_is_active_ret (XDR *xdrs, remote_interface_is_active_ret *o } bool_t +xdr_remote_cpu_compare_args (XDR *xdrs, remote_cpu_compare_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_cpu_compare_ret (XDR *xdrs, remote_cpu_compare_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->result)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_get_host_cpu_ret (XDR *xdrs, remote_get_host_cpu_ret *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->cpu)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 92f7010..1a06c16 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1433,6 +1433,20 @@ struct remote_interface_is_active_ret { }; +struct remote_cpu_compare_args { + remote_nonnull_string xml; +}; + +struct remote_cpu_compare_ret { + int result; +}; + + +struct remote_get_host_cpu_ret { + remote_nonnull_string cpu; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1611,7 +1625,9 @@ 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, + REMOTE_PROC_GET_HOST_CPU = 159 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.5.6

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/remote/remote_driver.c | 125 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 125 insertions(+), 0 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index affeb0f..b60d6d4 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7465,6 +7465,122 @@ done: return rv; } + +static virDrvOpenStatus ATTRIBUTE_NONNULL (1) +remoteCPUOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + if (inside_daemon) + return VIR_DRV_OPEN_DECLINED; + + if (conn->driver && + STREQ (conn->driver->name, "remote")) { + struct private_data *priv = conn->privateData; + /* If we're here, the remote driver is already + * in use due to a) a QEMU uri, or b) a remote + * URI. So we can re-use existing connection + */ + remoteDriverLock(priv); + priv->localUses++; + conn->cpuPrivateData = priv; + remoteDriverUnlock(priv); + return VIR_DRV_OPEN_SUCCESS; + } else if (conn->networkDriver && + STREQ (conn->networkDriver->name, "remote")) { + struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + conn->cpuPrivateData = priv; + priv->localUses++; + remoteDriverUnlock(priv); + return VIR_DRV_OPEN_SUCCESS; + } else { + /* Using a non-remote driver, so we need to open a + * new connection for network APIs, forcing it to + * use the UNIX transport. This handles Xen driver + * which doesn't have its own impl of the network APIs. + */ + struct private_data *priv; + int ret; + ret = remoteOpenSecondaryDriver(conn, + auth, + flags, + &priv); + if (ret == VIR_DRV_OPEN_SUCCESS) + conn->cpuPrivateData = priv; + return ret; + } +} + + +static int remoteCPUClose(virConnectPtr conn) +{ + int ret = 0; + struct private_data *priv = conn->cpuPrivateData; + + remoteDriverLock(priv); + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + conn->cpuPrivateData = NULL; + remoteDriverUnlock(priv); + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + } + if (priv) + remoteDriverUnlock(priv); + return ret; +} + + +static int +remoteCPUCompare (virConnectPtr conn, const char *xmlDesc) +{ + struct private_data *priv = conn->cpuPrivateData; + 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; +} + +static char * +remoteGetHostCPU (virConnectPtr conn) +{ + char *rv = NULL; + remote_get_host_cpu_ret ret; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_GET_HOST_CPU, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_get_host_cpu_ret, (char *)&ret) == -1) + goto done; + + /* Caller frees this. */ + rv = ret.cpu; + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ @@ -8830,6 +8946,7 @@ static virDriver remote_driver = { remoteIsSecure, /* isSecure */ remoteDomainIsActive, /* domainIsActive */ remoteDomainIsPersistent, /* domainIsPersistent */ + remoteGetHostCPU, /* getHostCPU */ }; static virNetworkDriver network_driver = { @@ -8928,6 +9045,13 @@ static virSecretDriver secret_driver = { .undefine = remoteSecretUndefine }; +static virCPUDriver cpu_driver = { + .name = "remote", + .open = remoteCPUOpen, + .close = remoteCPUClose, + .compare = remoteCPUCompare +}; + static virDeviceMonitor dev_monitor = { .name = "remote", .open = remoteDevMonOpen, @@ -8967,6 +9091,7 @@ remoteRegister (void) if (virRegisterStorageDriver (&storage_driver) == -1) return -1; if (virRegisterDeviceMonitor (&dev_monitor) == -1) return -1; if (virRegisterSecretDriver (&secret_driver) == -1) return -1; + if (virRegisterCPUDriver (&cpu_driver) == -1) return -1; #ifdef WITH_LIBVIRTD if (virRegisterStateDriver (&state_driver) == -1) return -1; #endif -- 1.6.5.6

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- daemon/remote.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 42 insertions(+), 0 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 7a43046..77777a3 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5226,6 +5226,48 @@ 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; +} + +static int +remoteDispatchGetHostCpu (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *err, + void *args ATTRIBUTE_UNUSED, + remote_get_host_cpu_ret *ret) +{ + char *cpu; + + cpu = virConnectGetHostCPU (conn); + if (cpu == NULL) { + remoteDispatchConnError(err, conn); + return -1; + } + + ret->cpu = cpu; + return 0; +} + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire -- 1.6.5.6

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. 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 | 206 +++++++++ src/cpu/cpu.h | 124 +++++ 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 | 1142 ++++++++++++++++++++++++++++++++++++++++++++++ src/cpu/cpu_x86.h | 31 ++ src/cpu/cpu_x86_data.h | 45 ++ src/libvirt_private.syms | 9 + 12 files changed, 1915 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 6135932..15f2b3d 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..e204303 --- /dev/null +++ b/src/cpu/cpu.c @@ -0,0 +1,206 @@ +/* + * 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 "cpu.h" +#include "cpu_x86.h" +#include "cpu_generic.h" + + +#define NR_DRIVERS ARRAY_CARDINALITY(drivers) + +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]; +} + + +int +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..4d565b5 --- /dev/null +++ b/src/cpu/cpu.h @@ -0,0 +1,124 @@ +/* + * 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 +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..96b23bb --- /dev/null +++ b/src/cpu/cpu_x86.c @@ -0,0 +1,1142 @@ +/* + * 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 (VIR_ALLOC_N(*set, max + 1) < 0) { + virReportOOMError(NULL); + return -1; + } + + for (i = 0; i <= max; i++) { + struct cpuX86cpuid *cpuid = (*set) + i; + + cpuid->function = base | i; + asm("cpuid" + : "=a" (cpuid->eax), + "=b" (cpuid->ebx), + "=c" (cpuid->ecx), + "=d" (cpuid->edx) + : "a" (cpuid->function)); + } + + 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 f10527a..a6cf174 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -71,6 +71,15 @@ virCgroupGetFreezerState; virCgroupSetFreezerState; +# cpu.h +cpuCompare; +cpuDataFree; +cpuDecode; +cpuEncode; +cpuGuestData; +cpuNodeData; + + # cpu_conf.h virCPUDefFree; virCPUDefParseXML; -- 1.6.5.6

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> --- daemon/Makefile.am | 2 + daemon/libvirtd.c | 3 + src/Makefile.am | 20 +++++- src/cpu/cpu_driver.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cpu/cpu_driver.h | 40 +++++++++++ src/datatypes.h | 2 + 6 files changed, 256 insertions(+), 1 deletions(-) create mode 100644 src/cpu/cpu_driver.c create mode 100644 src/cpu/cpu_driver.h diff --git a/daemon/Makefile.am b/daemon/Makefile.am index ab3f238..d239a89 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -110,6 +110,8 @@ endif if WITH_NODE_DEVICES libvirtd_LDADD += ../src/libvirt_driver_nodedev.la endif + + libvirtd_LDADD += ../src/libvirt_driver_cpu.la endif libvirtd_LDADD += ../src/libvirt.la diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 6b7e33d..56c16dc 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -94,6 +94,7 @@ #include "node_device/node_device_driver.h" #endif #include "secret/secret_driver.h" +#include "cpu/cpu_driver.h" #endif @@ -867,6 +868,7 @@ static struct qemud_server *qemudInitialize(void) { virDriverLoadModule("lxc"); virDriverLoadModule("uml"); virDriverLoadModule("one"); + virDriverLoadModule("cpu"); #else #ifdef WITH_NETWORK networkRegister(); @@ -893,6 +895,7 @@ static struct qemud_server *qemudInitialize(void) { #ifdef WITH_ONE oneRegister(); #endif + cpuRegister(); #endif virEventRegisterImpl(virEventAddHandleImpl, diff --git a/src/Makefile.am b/src/Makefile.am index 7d731de..3551a10 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -289,6 +289,9 @@ CPU_SOURCES = \ EXTRA_DIST += cpu/cpu_map.xml +CPU_DRIVER_SOURCES = \ + cpu/cpu_driver.h cpu/cpu_driver.c + ######################### # # Build up list of libvirt.la source files based on configure conditions @@ -696,6 +699,20 @@ libvirt_driver_security_la_CFLAGS += $(APPARMOR_CFLAGS) libvirt_driver_security_la_LDFLAGS += $(APPARMOR_LIBS) endif + +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_cpu.la +else +noinst_LTLIBRARIES += libvirt_driver_cpu.la +libvirt_la_LIBADD += libvirt_driver_cpu.la +endif +libvirt_driver_cpu_la_CFLAGS = \ + -I@top_srcdir@/src/conf +if WITH_DRIVER_MODULES +libvirt_driver_cpu_la_LDFLAGS = -module -avoid-version +endif +libvirt_driver_cpu_la_SOURCES = $(CPU_DRIVER_SOURCES) + # Add all conditional sources just in case... EXTRA_DIST += \ $(TEST_DRIVER_SOURCES) \ @@ -724,7 +741,8 @@ EXTRA_DIST += \ $(SECURITY_DRIVER_SELINUX_SOURCES) \ $(SECURITY_DRIVER_APPARMOR_SOURCES) \ $(SECRET_DRIVER_SOURCES) \ - $(VBOX_DRIVER_EXTRA_DIST) + $(VBOX_DRIVER_EXTRA_DIST) \ + $(CPU_DRIVER_SOURCES) check-local: if WITH_QEMU diff --git a/src/cpu/cpu_driver.c b/src/cpu/cpu_driver.c new file mode 100644 index 0000000..60425b4 --- /dev/null +++ b/src/cpu/cpu_driver.c @@ -0,0 +1,190 @@ +/* + * cpu_driver.c: Unified CPU driver; unified interface to architecture + * specific CPU drivers. + * + * 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 "datatypes.h" +#include "virterror_internal.h" +#include "logging.h" +#include "driver.h" + +#include "cpu_driver.h" +#include "cpu.h" + + +#define VIR_FROM_THIS VIR_FROM_CPU + + +struct cpu_driver { + virMutex lock; + + virCPUDefPtr host_cpu; +}; + + +static virCPUDefPtr +cpuParseString(virConnectPtr conn, + const char *cpu, + enum virCPUType mode) +{ + xmlDocPtr xml = NULL; + xmlXPathContextPtr ctxt = NULL; + virCPUDefPtr def = NULL; + + xml = xmlParseMemory(cpu, strlen(cpu)); + + if (xml == NULL || (ctxt = xmlXPathNewContext(xml)) == NULL) { + virReportOOMError(conn); + goto cleanup; + } + + ctxt->node = xmlDocGetRootElement(xml); + def = virCPUDefParseXML(conn, ctxt->node, ctxt, mode); + +cleanup: + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + + return def; +} + + +static virCPUDefPtr +cpuGetHostCPU(virConnectPtr conn) +{ + virCPUDefPtr cpu; + const char *xml; + + if (conn->driver == NULL || conn->driver->getHostCPU == NULL) { + virCPUReportError(conn, VIR_ERR_NO_SUPPORT, "virConnectGetHostCPU"); + goto error; + } + + if ((xml = conn->driver->getHostCPU(conn)) == NULL) + goto error; + + cpu = cpuParseString(conn, xml, VIR_CPU_TYPE_HOST); + VIR_FREE(xml); + + if (cpu != NULL) + return cpu; + +error: + virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get host CPU")); + return NULL; +} + + +static void +cpuDriverLock(struct cpu_driver *driver) +{ + virMutexLock(&driver->lock); +} + + +static void +cpuDriverUnlock(struct cpu_driver *driver) +{ + virMutexUnlock(&driver->lock); +} + + +static virDrvOpenStatus +cpuDriverOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + struct cpu_driver *driver; + + if (VIR_ALLOC(driver) < 0) { + virReportOOMError(conn); + return VIR_DRV_OPEN_ERROR; + } + + conn->cpuPrivateData = driver; + + return VIR_DRV_OPEN_SUCCESS; +} + + +static int +cpuDriverClose(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + struct cpu_driver *driver = conn->cpuPrivateData; + + if (driver != NULL) + virCPUDefFree(driver->host_cpu); + + conn->cpuPrivateData = NULL; + + VIR_FREE(driver); + + return 0; +} + + +static int +cpuDriverCompare(virConnectPtr conn, + const char *xml) +{ + struct cpu_driver *driver = conn->cpuPrivateData; + virCPUDefPtr cpu = NULL; + int ret = VIR_CPU_COMPARE_ERROR; + + VIR_DEBUG("conn=%p, xml=%s", conn, xml); + + cpuDriverLock(driver); + + if ((cpu = cpuParseString(conn, xml, VIR_CPU_TYPE_AUTO)) == NULL) + goto cleanup; + + if (driver->host_cpu == NULL && + (driver->host_cpu = cpuGetHostCPU(conn)) == NULL) + goto cleanup; + + ret = cpuCompare(conn, driver->host_cpu, cpu); + +cleanup: + cpuDriverUnlock(driver); + + virCPUDefFree(cpu); + return ret; +} + + +virCPUDriver driver = { + .name = "cpu", + .open = cpuDriverOpen, + .close = cpuDriverClose, + + .compare = cpuDriverCompare +}; + + +int cpuRegister(void) +{ + return virRegisterCPUDriver(&driver); +} diff --git a/src/cpu/cpu_driver.h b/src/cpu/cpu_driver.h new file mode 100644 index 0000000..51b7314 --- /dev/null +++ b/src/cpu/cpu_driver.h @@ -0,0 +1,40 @@ +/* + * cpu_driver.c: Unified CPU driver; unified interface to architecture + * specific CPU drivers. + * + * 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_DRIVER_H__ +#define __VIR_CPU_DRIVER_H__ + +#include "driver.h" +#include "virterror_internal.h" +#include "cpu_conf.h" + + +#define virCPUReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_CPU, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + + +int cpuRegister(void); + +#endif /* __VIR_CPU_DRIVER_H__ */ diff --git a/src/datatypes.h b/src/datatypes.h index afb51dc..685aacc 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -141,6 +141,7 @@ struct _virConnect { virStorageDriverPtr storageDriver; virDeviceMonitorPtr deviceMonitor; virSecretDriverPtr secretDriver; + virCPUDriverPtr cpuDriver; /* Private data pointer which can be used by driver and * network driver as they wish. @@ -152,6 +153,7 @@ struct _virConnect { void * storagePrivateData; void * devMonPrivateData; void * secretPrivateData; + void * cpuPrivateData; /* * The lock mutex must be acquired before accessing/changing -- 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..953ee55 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, /* getHostCPU */ }; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index c8e2dca..e574e9e 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2454,6 +2454,7 @@ static virDriver lxcDriver = { lxcIsSecure, lxcDomainIsActive, lxcDomainIsPersistent, + NULL, /* getHostCPU */ }; static virStateDriver lxcStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 1c0fccc..faabd26 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1533,6 +1533,7 @@ static virDriver openvzDriver = { openvzIsSecure, openvzDomainIsActive, openvzDomainIsPersistent, + NULL, /* getHostCPU */ }; int openvzRegister(void) { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 7db9a4c..d2e77f9 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, /* getHostCPU */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 48ef103..9ef8e95 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1922,6 +1922,7 @@ static virDriver umlDriver = { umlIsSecure, umlDomainIsActive, umlDomainIsPersistent, + NULL, /* getHostCPU */ }; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index d6b681c..16a3caf 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7049,6 +7049,7 @@ virDriver NAME(Driver) = { vboxIsSecure, vboxDomainIsActive, vboxDomainIsPersistent, + NULL, /* getHostCPU */ }; virNetworkDriver NAME(NetworkDriver) = { -- 1.6.5.6

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 | 37 ++++-- 3 files changed, 415 insertions(+), 30 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 86172c6..237c9b7 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 b5f5086..886c0ce 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2844,24 +2844,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 +2868,11 @@ cleanup: qemuDriverUnlock(driver); return xml; + +no_memory: + virCapabilitiesFree(caps); + virReportOOMError(conn); + goto cleanup; } @@ -7846,6 +7846,22 @@ out: return ret; } +static char * +qemuGetHostCPU(virConnectPtr conn) +{ + struct qemud_driver *driver = conn->privateData; + char *cpu = NULL; + + qemuDriverLock(driver); + + if (driver->caps && driver->caps->host.cpu) + cpu = virCPUDefFormat(conn, driver->caps->host.cpu, "", 0); + + qemuDriverUnlock(driver); + + return cpu; +} + static virDriver qemuDriver = { VIR_DRV_QEMU, "QEMU", @@ -7920,6 +7936,7 @@ static virDriver qemuDriver = { qemuIsSecure, qemuDomainIsActive, qemuDomainIsPersistent, + qemuGetHostCPU, /* getHostCPU */ }; -- 1.6.5.6

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

Jiri Denemark (14): 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 CPU driver implementation Set getHostCPU handler in drivers Implement CPU selection in QEMU driver Implement virsh commands
Hmm, actually this is pretty stupid. I'm going to remove 'CPU driver implementation' completely and stick virConnectCompareCPU() directly into hypervisor drivers instead of virConnectGetHostCPU()... Jirka
participants (1)
-
Jiri Denemark