diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 31862e2bfe..018d57170e 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -61,6 +61,19 @@ VIR_ENUM_IMPL(virCPUFeaturePolicy, VIR_CPU_FEATURE_LAST, "forbid") +void ATTRIBUTE_NONNULL(1) +virCPUDefFreeModel(virCPUDefPtr def) +{ + unsigned int i; + + VIR_FREE(def->model); + VIR_FREE(def->vendor); + + for (i = 0; i < def->nfeatures; i++) + VIR_FREE(def->features[i].name); + VIR_FREE(def->features); +} + void virCPUDefFree(virCPUDefPtr def) { @@ -69,13 +82,8 @@ virCPUDefFree(virCPUDefPtr def) if (!def) return; - VIR_FREE(def->model); VIR_FREE(def->arch); - VIR_FREE(def->vendor); - - for (i = 0 ; i < def->nfeatures ; i++) - VIR_FREE(def->features[i].name); - VIR_FREE(def->features); + virCPUDefFreeModel(def); for (i = 0 ; i < def->ncells ; i++) { VIR_FREE(def->cells[i].cpumask); @@ -87,6 +95,42 @@ virCPUDefFree(virCPUDefPtr def) } +int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) +virCPUDefCopyModel(virCPUDefPtr dst, + const virCPUDefPtr src, + bool resetPolicy) +{ + unsigned int i; + + if ((src->model && !(dst->model = strdup(src->model))) + || (src->vendor && !(dst->vendor = strdup(src->vendor))) + || VIR_ALLOC_N(dst->features, src->nfeatures) < 0) + goto no_memory; + dst->nfeatures_max = dst->nfeatures = src->nfeatures; + + for (i = 0; i < dst->nfeatures; i++) { + if (dst->type != src->type && resetPolicy) { + if (dst->type == VIR_CPU_TYPE_HOST) + dst->features[i].policy = -1; + else if (src->features[i].policy == -1) + dst->features[i].policy = VIR_CPU_FEATURE_REQUIRE; + else + dst->features[i].policy = src->features[i].policy; + } else { + dst->features[i].policy = src->features[i].policy; + } + + if (!(dst->features[i].name = strdup(src->features[i].name))) + goto no_memory; + } + + return 0; + +no_memory: + virReportOOMError(); + return -1; +} + virCPUDefPtr virCPUDefCopy(const virCPUDefPtr cpu) { @@ -96,13 +140,8 @@ virCPUDefCopy(const virCPUDefPtr cpu) if (!cpu) return NULL; - if (VIR_ALLOC(copy) < 0 - || (cpu->arch && !(copy->arch = strdup(cpu->arch))) - || (cpu->model && !(copy->model = strdup(cpu->model))) - || (cpu->vendor && !(copy->vendor = strdup(cpu->vendor))) - || VIR_ALLOC_N(copy->features, cpu->nfeatures) < 0) + if (VIR_ALLOC(copy) < 0) goto no_memory; - copy->nfeatures_max = cpu->nfeatures; copy->type = cpu->type; copy->mode = cpu->mode; @@ -111,13 +150,12 @@ virCPUDefCopy(const virCPUDefPtr cpu) copy->sockets = cpu->sockets; copy->cores = cpu->cores; copy->threads = cpu->threads; - copy->nfeatures = cpu->nfeatures; - for (i = 0; i < copy->nfeatures; i++) { - copy->features[i].policy = cpu->features[i].policy; - if (!(copy->features[i].name = strdup(cpu->features[i].name))) - goto no_memory; - } + if (cpu->arch && !(copy->arch = strdup(cpu->arch))) + goto no_memory; + + if (virCPUDefCopyModel(copy, cpu, false) < 0) + goto error; if (cpu->ncells) { if (VIR_ALLOC_N(copy->cells, cpu->ncells) < 0) @@ -144,6 +182,7 @@ virCPUDefCopy(const virCPUDefPtr cpu) no_memory: virReportOOMError(); +error: virCPUDefFree(copy); return NULL; } diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index 4b0b35bda3..f8b7bf9563 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -118,9 +118,17 @@ struct _virCPUDef { }; +void ATTRIBUTE_NONNULL(1) +virCPUDefFreeModel(virCPUDefPtr def); + void virCPUDefFree(virCPUDefPtr def); +int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) +virCPUDefCopyModel(virCPUDefPtr dst, + const virCPUDefPtr src, + bool resetPolicy); + virCPUDefPtr virCPUDefCopy(const virCPUDefPtr cpu); diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index ad2d5cda45..308604ba56 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -1686,8 +1686,8 @@ error: static int -x86Update(virCPUDefPtr guest, - const virCPUDefPtr host) +x86UpdateCustom(virCPUDefPtr guest, + const virCPUDefPtr host) { int ret = -1; unsigned int i; @@ -1731,6 +1731,32 @@ cleanup: return ret; } +static int +x86Update(virCPUDefPtr guest, + const virCPUDefPtr host) +{ + switch ((enum virCPUMode) guest->mode) { + case VIR_CPU_MODE_CUSTOM: + return x86UpdateCustom(guest, host); + + case VIR_CPU_MODE_HOST_MODEL: + case VIR_CPU_MODE_HOST_PASSTHROUGH: + if (guest->mode == VIR_CPU_MODE_HOST_MODEL) + guest->match = VIR_CPU_MATCH_EXACT; + else + guest->match = VIR_CPU_MATCH_MINIMUM; + virCPUDefFreeModel(guest); + return virCPUDefCopyModel(guest, host, true); + + case VIR_CPU_MODE_LAST: + break; + } + + virCPUReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected CPU mode: %d"), guest->mode); + return -1; +} + static int x86HasFeature(const union cpuData *data, const char *name) { diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index cfb88f8e0a..f7386026c1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -181,9 +181,11 @@ cpuUpdate; # cpu_conf.h virCPUDefAddFeature; virCPUDefCopy; +virCPUDefCopyModel; virCPUDefFormat; virCPUDefFormatBuf; virCPUDefFree; +virCPUDefFreeModel; virCPUDefParseXML; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 6b03c6238a..a25f4df591 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1050,20 +1050,20 @@ char *qemuDomainDefFormatXML(struct qemud_driver *driver, { char *ret = NULL; virCPUDefPtr cpu = NULL; - virCPUDefPtr def_cpu; - - def_cpu = def->cpu; + virCPUDefPtr def_cpu = def->cpu; /* Update guest CPU requirements according to host CPU */ - if ((flags & VIR_DOMAIN_XML_UPDATE_CPU) && def_cpu && def_cpu->model) { + if ((flags & VIR_DOMAIN_XML_UPDATE_CPU) && + def_cpu && + (def_cpu->mode != VIR_CPU_MODE_CUSTOM || def_cpu->model)) { if (!driver->caps || !driver->caps->host.cpu) { qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", _("cannot get host CPU capabilities")); goto cleanup; } - if (!(cpu = virCPUDefCopy(def_cpu)) - || cpuUpdate(cpu, driver->caps->host.cpu)) + if (!(cpu = virCPUDefCopy(def_cpu)) || + cpuUpdate(cpu, driver->caps->host.cpu) < 0) goto cleanup; def->cpu = cpu; } diff --git a/tests/cputest.c b/tests/cputest.c index d8c171398c..938e08777c 100644 --- a/tests/cputest.c +++ b/tests/cputest.c @@ -162,7 +162,8 @@ error: static int cpuTestCompareXML(const char *arch, const virCPUDefPtr cpu, - const char *name) + const char *name, + unsigned int flags) { char *xml = NULL; char *expected = NULL; @@ -176,7 +177,7 @@ cpuTestCompareXML(const char *arch, if (virtTestLoadFile(xml, &expected) < 0) goto cleanup; - if (!(actual = virCPUDefFormat(cpu, 0))) + if (!(actual = virCPUDefFormat(cpu, flags))) goto cleanup; if (STRNEQ(expected, actual)) { @@ -310,7 +311,7 @@ cpuTestGuestData(const void *arg) } result = virBufferContentAndReset(&buf); - ret = cpuTestCompareXML(data->arch, guest, result); + ret = cpuTestCompareXML(data->arch, guest, result, 0); cleanup: VIR_FREE(result); @@ -354,7 +355,7 @@ cpuTestBaseline(const void *arg) if (virAsprintf(&result, "%s-result", data->name) < 0) goto cleanup; - if (cpuTestCompareXML(data->arch, baseline, result) < 0) + if (cpuTestCompareXML(data->arch, baseline, result, 0) < 0) goto cleanup; for (i = 0; i < ncpus; i++) { @@ -406,7 +407,8 @@ cpuTestUpdate(const void *arg) if (virAsprintf(&result, "%s+%s", data->host, data->name) < 0) goto cleanup; - ret = cpuTestCompareXML(data->arch, cpu, result); + ret = cpuTestCompareXML(data->arch, cpu, result, + VIR_DOMAIN_XML_UPDATE_CPU); cleanup: virCPUDefFree(host); @@ -592,6 +594,9 @@ mymain(void) DO_TEST_UPDATE("x86", "host", "min", IDENTICAL); DO_TEST_UPDATE("x86", "host", "pentium3", IDENTICAL); DO_TEST_UPDATE("x86", "host", "guest", SUPERSET); + DO_TEST_UPDATE("x86", "host", "host-model", IDENTICAL); + DO_TEST_UPDATE("x86", "host", "host-model-nofallback", IDENTICAL); + DO_TEST_UPDATE("x86", "host", "host-passthrough", IDENTICAL); /* computing baseline CPUs */ DO_TEST_BASELINE("x86", "incompatible-vendors", -1); @@ -622,6 +627,9 @@ mymain(void) DO_TEST_GUESTDATA("x86", "host", "guest", models, "qemu64", 0); DO_TEST_GUESTDATA("x86", "host", "guest", nomodel, NULL, -1); DO_TEST_GUESTDATA("x86", "host", "guest-nofallback", models, "Penryn", -1); + DO_TEST_GUESTDATA("x86", "host", "host+host-model", models, "Penryn", 0); + DO_TEST_GUESTDATA("x86", "host", "host+host-model-nofallback", + models, "Penryn", -1); free(map); return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE); diff --git a/tests/cputestdata/x86-host+host+host-model,models,Penryn-result.xml b/tests/cputestdata/x86-host+host+host-model,models,Penryn-result.xml new file mode 100644 index 0000000000..e2b7f5b11d --- /dev/null +++ b/tests/cputestdata/x86-host+host+host-model,models,Penryn-result.xml @@ -0,0 +1,19 @@ + + x86_64 + core2duo + + + + + + + + + + + + + + + + diff --git a/tests/cputestdata/x86-host+host-model-nofallback.xml b/tests/cputestdata/x86-host+host-model-nofallback.xml new file mode 100644 index 0000000000..bbb68fb6ef --- /dev/null +++ b/tests/cputestdata/x86-host+host-model-nofallback.xml @@ -0,0 +1,19 @@ + + Penryn + Intel + + + + + + + + + + + + + + + + diff --git a/tests/cputestdata/x86-host+host-model.xml b/tests/cputestdata/x86-host+host-model.xml new file mode 100644 index 0000000000..c1014e2de7 --- /dev/null +++ b/tests/cputestdata/x86-host+host-model.xml @@ -0,0 +1,18 @@ + + Penryn + Intel + + + + + + + + + + + + + + + diff --git a/tests/cputestdata/x86-host+host-passthrough.xml b/tests/cputestdata/x86-host+host-passthrough.xml new file mode 100644 index 0000000000..721fae5836 --- /dev/null +++ b/tests/cputestdata/x86-host+host-passthrough.xml @@ -0,0 +1,18 @@ + + Penryn + Intel + + + + + + + + + + + + + + + diff --git a/tests/cputestdata/x86-host-model-nofallback.xml b/tests/cputestdata/x86-host-model-nofallback.xml new file mode 100644 index 0000000000..4e42eb4a98 --- /dev/null +++ b/tests/cputestdata/x86-host-model-nofallback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/cputestdata/x86-host-model.xml b/tests/cputestdata/x86-host-model.xml new file mode 100644 index 0000000000..fd50c03a79 --- /dev/null +++ b/tests/cputestdata/x86-host-model.xml @@ -0,0 +1 @@ + diff --git a/tests/cputestdata/x86-host-passthrough.xml b/tests/cputestdata/x86-host-passthrough.xml new file mode 100644 index 0000000000..655c7a7f60 --- /dev/null +++ b/tests/cputestdata/x86-host-passthrough.xml @@ -0,0 +1 @@ +