diff --git a/po/POTFILES.in b/po/POTFILES.in index 25dbc842d8..1469240221 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -43,6 +43,7 @@ src/conf/virchrdev.c src/conf/virdomainobjlist.c src/conf/virsecretobj.c src/cpu/cpu.c +src/cpu/cpu_arm.c src/cpu/cpu_map.c src/cpu/cpu_ppc64.c src/cpu/cpu_x86.c diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index fae3885ef5..e6f6335ff5 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -579,38 +579,71 @@ cpuBaseline(virCPUDefPtr *cpus, /** - * cpuUpdate: + * virCPUUpdate: * - * @guest: guest CPU definition + * @arch: CPU architecture + * @guest: guest CPU definition to be updated * @host: host CPU definition * * Updates @guest CPU definition according to @host CPU. This is required to - * support guest CPU definition which are relative to host CPU, such as CPUs - * with VIR_CPU_MODE_CUSTOM and optional features or VIR_CPU_MATCH_MINIMUM, or - * CPUs with non-custom mode (VIR_CPU_MODE_HOST_MODEL, - * VIR_CPU_MODE_HOST_PASSTHROUGH). + * support guest CPU definitions specified relatively to host CPU, such as + * CPUs with VIR_CPU_MODE_CUSTOM and optional features or + * VIR_CPU_MATCH_MINIMUM, or CPUs with VIR_CPU_MODE_HOST_MODEL. + * When the guest CPU was not specified relatively, the function does nothing + * and returns success. * * Returns 0 on success, -1 on error. */ int -cpuUpdate(virCPUDefPtr guest, - const virCPUDef *host) +virCPUUpdate(virArch arch, + virCPUDefPtr guest, + const virCPUDef *host) { struct cpuArchDriver *driver; - VIR_DEBUG("guest=%p, host=%p", guest, host); + VIR_DEBUG("arch=%s, guest=%p mode=%s model=%s, host=%p model=%s", + virArchToString(arch), guest, virCPUModeTypeToString(guest->mode), + NULLSTR(guest->model), host, NULLSTR(host ? host->model : NULL)); - if ((driver = cpuGetSubDriver(host->arch)) == NULL) + if (!(driver = cpuGetSubDriver(arch))) return -1; - if (driver->update == NULL) { + if (guest->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) + return 0; + + if (guest->mode == VIR_CPU_MODE_CUSTOM && + guest->match != VIR_CPU_MATCH_MINIMUM) { + size_t i; + bool optional = false; + + for (i = 0; i < guest->nfeatures; i++) { + if (guest->features[i].policy == VIR_CPU_FEATURE_OPTIONAL) { + optional = true; + break; + } + } + + if (!optional) + return 0; + } + + /* We get here if guest CPU is either + * - host-model + * - custom with minimum match + * - custom with optional features + */ + if (!driver->update) { virReportError(VIR_ERR_NO_SUPPORT, - _("cannot update guest CPU data for %s architecture"), - virArchToString(host->arch)); + _("cannot update guest CPU for %s architecture"), + virArchToString(arch)); return -1; } - return driver->update(guest, host); + if (driver->update(guest, host) < 0) + return -1; + + VIR_DEBUG("model=%s", NULLSTR(guest->model)); + return 0; } diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index 422818ec49..dac7688126 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -87,8 +87,8 @@ typedef virCPUDefPtr unsigned int flags); typedef int -(*cpuArchUpdate) (virCPUDefPtr guest, - const virCPUDef *host); +(*virCPUArchUpdate)(virCPUDefPtr guest, + const virCPUDef *host); typedef int (*cpuArchHasFeature) (const virCPUData *data, @@ -114,7 +114,7 @@ struct cpuArchDriver { cpuArchNodeData nodeData; cpuArchGuestData guestData; cpuArchBaseline baseline; - cpuArchUpdate update; + virCPUArchUpdate update; cpuArchHasFeature hasFeature; cpuArchDataFormat dataFormat; cpuArchDataParse dataParse; @@ -182,9 +182,10 @@ cpuBaseline (virCPUDefPtr *cpus, ATTRIBUTE_NONNULL(1); int -cpuUpdate (virCPUDefPtr guest, +virCPUUpdate(virArch arch, + virCPUDefPtr guest, const virCPUDef *host) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + ATTRIBUTE_NONNULL(2); int cpuHasFeature(const virCPUData *data, diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c index a3aed6bc8c..e8ab954ce7 100644 --- a/src/cpu/cpu_arm.c +++ b/src/cpu/cpu_arm.c @@ -43,15 +43,41 @@ armDataFree(virCPUDataPtr data) VIR_FREE(data); } + static int -armUpdate(virCPUDefPtr guest, - const virCPUDef *host) +virCPUarmUpdate(virCPUDefPtr guest, + const virCPUDef *host) { + int ret = -1; + virCPUDefPtr updated = NULL; + + if (guest->mode != VIR_CPU_MODE_HOST_MODEL) + return 0; + + if (!host) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("unknown host CPU model")); + goto cleanup; + } + + if (!(updated = virCPUDefCopyWithoutModel(guest))) + goto cleanup; + + updated->mode = VIR_CPU_MODE_CUSTOM; + if (virCPUDefCopyModel(updated, host, true) < 0) + goto cleanup; + + virCPUDefStealModel(guest, updated); + guest->mode = VIR_CPU_MODE_CUSTOM; guest->match = VIR_CPU_MATCH_EXACT; - virCPUDefFreeModel(guest); - return virCPUDefCopyModel(guest, host, true); + ret = 0; + + cleanup: + virCPUDefFree(updated); + return ret; } + static virCPUCompareResult armGuestData(virCPUDefPtr host ATTRIBUTE_UNUSED, virCPUDefPtr guest ATTRIBUTE_UNUSED, @@ -104,6 +130,6 @@ struct cpuArchDriver cpuDriverArm = { .nodeData = NULL, .guestData = armGuestData, .baseline = armBaseline, - .update = armUpdate, + .update = virCPUarmUpdate, .hasFeature = NULL, }; diff --git a/src/cpu/cpu_ppc64.c b/src/cpu/cpu_ppc64.c index ad2d6a7ea1..00faa22904 100644 --- a/src/cpu/cpu_ppc64.c +++ b/src/cpu/cpu_ppc64.c @@ -751,27 +751,21 @@ ppc64DriverGuestData(virCPUDefPtr host, } static int -ppc64DriverUpdate(virCPUDefPtr guest, - const virCPUDef *host) +virCPUppc64Update(virCPUDefPtr guest, + const virCPUDef *host ATTRIBUTE_UNUSED) { - switch ((virCPUMode) guest->mode) { - case VIR_CPU_MODE_HOST_PASSTHROUGH: + /* + * - host-passthrough doesn't even get here + * - host-model is used for host CPU running in a compatibility mode and + * it needs to remain unchanged + * - custom doesn't support any optional features, there's nothing to + * update + */ + + if (guest->mode == VIR_CPU_MODE_CUSTOM) guest->match = VIR_CPU_MATCH_EXACT; - guest->fallback = VIR_CPU_FALLBACK_FORBID; - virCPUDefFreeModel(guest); - return virCPUDefCopyModel(guest, host, true); - case VIR_CPU_MODE_HOST_MODEL: - case VIR_CPU_MODE_CUSTOM: - return 0; - - case VIR_CPU_MODE_LAST: - break; - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unexpected CPU mode: %d"), guest->mode); - return -1; + return 0; } static virCPUDefPtr @@ -915,7 +909,7 @@ struct cpuArchDriver cpuDriverPPC64 = { .nodeData = ppc64DriverNodeData, .guestData = ppc64DriverGuestData, .baseline = ppc64DriverBaseline, - .update = ppc64DriverUpdate, + .update = virCPUppc64Update, .hasFeature = NULL, .getModels = ppc64DriverGetModels, }; diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index 8c95b7a1e1..bd2670374c 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -1122,40 +1122,6 @@ x86ModelFromCPU(const virCPUDef *cpu, } -static int -x86ModelSubtractCPU(virCPUx86ModelPtr model, - const virCPUDef *cpu, - virCPUx86MapPtr map) -{ - virCPUx86ModelPtr cpu_model; - size_t i; - - if (!(cpu_model = x86ModelFind(map, cpu->model))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unknown CPU model %s"), - cpu->model); - return -1; - } - - x86DataSubtract(&model->data, &cpu_model->data); - - for (i = 0; i < cpu->nfeatures; i++) { - virCPUx86FeaturePtr feature; - - if (!(feature = x86FeatureFind(map, cpu->features[i].name))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unknown CPU feature %s"), - cpu->features[i].name); - return -1; - } - - x86DataSubtract(&model->data, &feature->data); - } - - return 0; -} - - static virCPUx86CompareResult x86ModelCompare(virCPUx86ModelPtr model1, virCPUx86ModelPtr model2) @@ -2533,23 +2499,85 @@ x86Baseline(virCPUDefPtr *cpus, static int -x86UpdateCustom(virCPUDefPtr guest, +x86UpdateHostModel(virCPUDefPtr guest, + const virCPUDef *host, + virCPUx86MapPtr map) +{ + virCPUDefPtr updated = NULL; + size_t i; + int ret = -1; + + if (!(updated = virCPUDefCopyWithoutModel(host))) + goto cleanup; + + /* Remove non-migratable features by default */ + updated->type = VIR_CPU_TYPE_GUEST; + updated->mode = VIR_CPU_MODE_CUSTOM; + if (virCPUDefCopyModel(updated, host, true) < 0) + goto cleanup; + + i = 0; + while (i < updated->nfeatures) { + if (x86FeatureIsMigratable(updated->features[i].name, map) && + STRNEQ(updated->features[i].name, "cmt") && + STRNEQ(updated->features[i].name, "mbm_total") && + STRNEQ(updated->features[i].name, "mbm_local")) { + i++; + } else { + VIR_FREE(updated->features[i].name); + VIR_DELETE_ELEMENT_INPLACE(updated->features, i, updated->nfeatures); + } + } + + if (guest->vendor_id) { + VIR_FREE(updated->vendor_id); + if (VIR_STRDUP(updated->vendor_id, guest->vendor_id) < 0) + goto cleanup; + } + + for (i = 0; i < guest->nfeatures; i++) { + if (virCPUDefUpdateFeature(updated, + guest->features[i].name, + guest->features[i].policy) < 0) + goto cleanup; + } + + virCPUDefStealModel(guest, updated); + guest->mode = VIR_CPU_MODE_CUSTOM; + guest->match = VIR_CPU_MATCH_EXACT; + ret = 0; + + cleanup: + virCPUDefFree(updated); + return ret; +} + + +static int +virCPUx86Update(virCPUDefPtr guest, const virCPUDef *host) { + virCPUx86ModelPtr model = NULL; + virCPUx86MapPtr map; int ret = -1; size_t i; - virCPUx86MapPtr map; - virCPUx86ModelPtr host_model = NULL; - if (!(map = virCPUx86GetMap()) || - !(host_model = x86ModelFromCPU(host, map, VIR_CPU_FEATURE_REQUIRE))) + if (!host) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("unknown host CPU model")); + return -1; + } + + if (!(map = virCPUx86GetMap())) + return -1; + + if (!(model = x86ModelFromCPU(host, map, -1))) goto cleanup; for (i = 0; i < guest->nfeatures; i++) { if (guest->features[i].policy == VIR_CPU_FEATURE_OPTIONAL) { int supported = x86FeatureInData(guest->features[i].name, - &host_model->data, map); - + &model->data, map); if (supported < 0) goto cleanup; else if (supported) @@ -2559,106 +2587,18 @@ x86UpdateCustom(virCPUDefPtr guest, } } - if (guest->match == VIR_CPU_MATCH_MINIMUM) { - guest->match = VIR_CPU_MATCH_EXACT; - if (x86ModelSubtractCPU(host_model, guest, map) || - x86DataToCPUFeatures(guest, VIR_CPU_FEATURE_REQUIRE, - &host_model->data, map)) - goto cleanup; - } - - ret = 0; + if (guest->mode == VIR_CPU_MODE_HOST_MODEL || + guest->match == VIR_CPU_MATCH_MINIMUM) + ret = x86UpdateHostModel(guest, host, map); + else + ret = 0; cleanup: - x86ModelFree(host_model); + x86ModelFree(model); return ret; } -static int -x86UpdateHostModel(virCPUDefPtr guest, - const virCPUDef *host, - bool passthrough) -{ - virCPUDefPtr oldguest = NULL; - virCPUx86MapPtr map; - size_t i; - int ret = -1; - - if (!(map = virCPUx86GetMap())) - goto cleanup; - - /* update the host model according to the desired configuration */ - if (!(oldguest = virCPUDefCopy(guest))) - goto cleanup; - - virCPUDefFreeModel(guest); - if (virCPUDefCopyModel(guest, host, true) < 0) - goto cleanup; - - if (oldguest->vendor_id) { - VIR_FREE(guest->vendor_id); - if (VIR_STRDUP(guest->vendor_id, oldguest->vendor_id) < 0) - goto cleanup; - } - - /* Remove non-migratable features and CMT related features which QEMU - * knows nothing about. - * Note: this only works as long as no CPU model contains non-migratable - * features directly */ - i = 0; - while (i < guest->nfeatures) { - if (x86FeatureIsMigratable(guest->features[i].name, map) && - STRNEQ(guest->features[i].name, "cmt") && - STRNEQ(guest->features[i].name, "mbm_total") && - STRNEQ(guest->features[i].name, "mbm_local")) { - i++; - } else { - VIR_FREE(guest->features[i].name); - VIR_DELETE_ELEMENT_INPLACE(guest->features, i, guest->nfeatures); - } - } - for (i = 0; !passthrough && i < oldguest->nfeatures; i++) { - if (virCPUDefUpdateFeature(guest, - oldguest->features[i].name, - oldguest->features[i].policy) < 0) - goto cleanup; - } - - ret = 0; - - cleanup: - virCPUDefFree(oldguest); - return ret; -} - - -static int -x86Update(virCPUDefPtr guest, - const virCPUDef *host) -{ - switch ((virCPUMode) guest->mode) { - case VIR_CPU_MODE_CUSTOM: - return x86UpdateCustom(guest, host); - - case VIR_CPU_MODE_HOST_MODEL: - guest->match = VIR_CPU_MATCH_EXACT; - return x86UpdateHostModel(guest, host, false); - - case VIR_CPU_MODE_HOST_PASSTHROUGH: - guest->match = VIR_CPU_MATCH_MINIMUM; - return x86UpdateHostModel(guest, host, true); - - case VIR_CPU_MODE_LAST: - break; - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unexpected CPU mode: %d"), guest->mode); - return -1; -} - - static int x86HasFeature(const virCPUData *data, const char *name) @@ -2716,7 +2656,7 @@ struct cpuArchDriver cpuDriverX86 = { #endif .guestData = x86GuestData, .baseline = x86Baseline, - .update = x86Update, + .update = virCPUx86Update, .hasFeature = x86HasFeature, .dataFormat = x86CPUDataFormat, .dataParse = x86CPUDataParse, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c1285700ad..6dfab8284a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -981,7 +981,7 @@ cpuGetModels; cpuGuestData; cpuHasFeature; cpuNodeData; -cpuUpdate; +virCPUUpdate; # cpu/cpu_x86.h diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f3549d2f1b..8b56ef7e4c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6568,7 +6568,7 @@ qemuBuildCpuModelArgStr(virQEMUDriverPtr driver, if (cpu->mode == VIR_CPU_MODE_HOST_MODEL && !migrating && - cpuUpdate(cpu, host) < 0) + virCPUUpdate(def->os.arch, cpu, host) < 0) goto cleanup; if (compareAgainstHost && diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 309d21b793..537024f22e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3407,7 +3407,7 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver, goto cleanup; } - if (cpuUpdate(def->cpu, caps->host.cpu) < 0) + if (virCPUUpdate(def->os.arch, def->cpu, caps->host.cpu) < 0) goto cleanup; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index d2d28489d7..b6e050c3b6 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4522,7 +4522,7 @@ qemuProcessStartValidateGuestCPU(virDomainObjPtr vm, if (cpu->mode == VIR_CPU_MODE_HOST_MODEL && flags & VIR_QEMU_PROCESS_START_NEW && - cpuUpdate(cpu, host) < 0) + virCPUUpdate(vm->def->os.arch, cpu, host) < 0) goto cleanup; cmp = cpuGuestData(host, cpu, &data, &compare_msg); diff --git a/tests/cputest.c b/tests/cputest.c index ee779fb7c6..e6696e94be 100644 --- a/tests/cputest.c +++ b/tests/cputest.c @@ -398,7 +398,7 @@ cpuTestUpdate(const void *arg) !(cpu = cpuTestLoadXML(data->arch, data->name))) goto cleanup; - if (cpuUpdate(cpu, host) < 0) + if (virCPUUpdate(host->arch, cpu, host) < 0) goto cleanup; if (virAsprintf(&result, "%s+%s", data->host, data->name) < 0) @@ -622,11 +622,14 @@ mymain(void) host "/" cpu " (" #result ")", \ host, cpu, NULL, 0, NULL, 0, result) +#define DO_TEST_UPDATE_ONLY(arch, host, cpu) \ + DO_TEST(arch, cpuTestUpdate, \ + cpu " on " host, \ + host, cpu, NULL, 0, NULL, 0, 0) \ + #define DO_TEST_UPDATE(arch, host, cpu, result) \ do { \ - DO_TEST(arch, cpuTestUpdate, \ - cpu " on " host, \ - host, cpu, NULL, 0, NULL, 0, 0); \ + DO_TEST_UPDATE_ONLY(arch, host, cpu); \ DO_TEST_COMPARE(arch, host, host "+" cpu, result); \ } while (0) @@ -737,8 +740,9 @@ mymain(void) DO_TEST_UPDATE("x86", "host", "guest", VIR_CPU_COMPARE_SUPERSET); DO_TEST_UPDATE("x86", "host", "host-model", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE("x86", "host", "host-model-nofallback", VIR_CPU_COMPARE_IDENTICAL); - DO_TEST_UPDATE("x86", "host", "host-passthrough", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE("x86", "host-invtsc", "host-model", VIR_CPU_COMPARE_SUPERSET); + DO_TEST_UPDATE_ONLY("x86", "host", "host-passthrough"); + DO_TEST_UPDATE_ONLY("x86", "host", "host-passthrough-features"); DO_TEST_UPDATE("ppc64", "host", "guest", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE("ppc64", "host", "guest-nofallback", VIR_CPU_COMPARE_INCOMPATIBLE); diff --git a/tests/cputestdata/x86-host+host-model-nofallback.xml b/tests/cputestdata/x86-host+host-model-nofallback.xml index bbb68fb6ef..0c3ede0f6a 100644 --- a/tests/cputestdata/x86-host+host-model-nofallback.xml +++ b/tests/cputestdata/x86-host+host-model-nofallback.xml @@ -1,4 +1,4 @@ - + Penryn Intel diff --git a/tests/cputestdata/x86-host+host-model.xml b/tests/cputestdata/x86-host+host-model.xml index c1014e2de7..a284767379 100644 --- a/tests/cputestdata/x86-host+host-model.xml +++ b/tests/cputestdata/x86-host+host-model.xml @@ -1,4 +1,4 @@ - + Penryn Intel diff --git a/tests/cputestdata/x86-host+host-passthrough-features.xml b/tests/cputestdata/x86-host+host-passthrough-features.xml new file mode 100644 index 0000000000..dc2b775617 --- /dev/null +++ b/tests/cputestdata/x86-host+host-passthrough-features.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/cputestdata/x86-host+host-passthrough.xml b/tests/cputestdata/x86-host+host-passthrough.xml index 721fae5836..655c7a7f60 100644 --- a/tests/cputestdata/x86-host+host-passthrough.xml +++ b/tests/cputestdata/x86-host+host-passthrough.xml @@ -1,18 +1 @@ - - Penryn - Intel - - - - - - - - - - - - - - - + diff --git a/tests/cputestdata/x86-host+min.xml b/tests/cputestdata/x86-host+min.xml index 6d2d5cda35..a284767379 100644 --- a/tests/cputestdata/x86-host+min.xml +++ b/tests/cputestdata/x86-host+min.xml @@ -1,17 +1,18 @@ Penryn - - - - - - - - - - - - - + Intel + + + + + + + + + + + + + diff --git a/tests/cputestdata/x86-host+pentium3.xml b/tests/cputestdata/x86-host+pentium3.xml index a8c15f433a..a284767379 100644 --- a/tests/cputestdata/x86-host+pentium3.xml +++ b/tests/cputestdata/x86-host+pentium3.xml @@ -1,27 +1,18 @@ - pentium3 - - - - - - - - - - - - - - - - - - + Penryn + Intel - - - - - + + + + + + + + + + + + + diff --git a/tests/cputestdata/x86-host-invtsc+host-model.xml b/tests/cputestdata/x86-host-invtsc+host-model.xml index ad1bbf807c..998ed23d7b 100644 --- a/tests/cputestdata/x86-host-invtsc+host-model.xml +++ b/tests/cputestdata/x86-host-invtsc+host-model.xml @@ -1,4 +1,4 @@ - + SandyBridge Intel diff --git a/tests/cputestdata/x86-host-passthrough-features.xml b/tests/cputestdata/x86-host-passthrough-features.xml new file mode 100644 index 0000000000..dc2b775617 --- /dev/null +++ b/tests/cputestdata/x86-host-passthrough-features.xml @@ -0,0 +1,4 @@ + + + +