From 57b8e156e898ccf878842c3cba1ed97a415cff6a Mon Sep 17 00:00:00 2001 From: Xu Yandong Date: Fri, 6 Sep 2019 11:27:33 +0800 Subject: [PATCH] cpu: fix cpu-compare and cpu-baseline for ARM CPU Signed-off-by: Xu Yandong --- src/cpu/cpu_arm.c | 241 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 234 insertions(+), 7 deletions(-) diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c index 0c8cd50..1d0d3b6 100644 --- a/src/cpu/cpu_arm.c +++ b/src/cpu/cpu_arm.c @@ -681,12 +681,18 @@ virCPUarmUpdate(virCPUDefPtr guest, static virCPUDefPtr virCPUarmBaseline(virCPUDefPtr *cpus, - unsigned int ncpus ATTRIBUTE_UNUSED, - virDomainCapsCPUModelsPtr models ATTRIBUTE_UNUSED, + unsigned int ncpus, + virDomainCapsCPUModelsPtr models, const char **features ATTRIBUTE_UNUSED, bool migratable ATTRIBUTE_UNUSED) { virCPUDefPtr cpu = NULL; + virCPUarmMapPtr map = NULL; + virCPUarmModelPtr model = NULL; + virCPUarmModelPtr baseModel = NULL; + virCPUarmVendorPtr vendor = NULL; + bool outputVendor = true; + size_t i; if (VIR_ALLOC(cpu) < 0 || VIR_STRDUP(cpu->model, cpus[0]->model) < 0) { @@ -694,27 +700,248 @@ virCPUarmBaseline(virCPUDefPtr *cpus, return NULL; } + cpu->arch = cpus[0]->arch; cpu->type = VIR_CPU_TYPE_GUEST; cpu->match = VIR_CPU_MATCH_EXACT; + cpu->fallback = VIR_CPU_FALLBACK_FORBID; + + if (!(map = virCPUarmGetMap())) + goto error; + + if (!(baseModel = armModelFromCPU(cpus[0], map))) + goto error; + + if (!cpus[0]->vendor) { + outputVendor = false; + } else if (!(vendor = armVendorFindByName(map, cpus[0]->vendor))) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Unknown CPU vendor %s"), cpus[0]->vendor); + goto error; + } + + for (i = 1; i < ncpus; i++) { + const char *vn = NULL; + + if (!(model = armModelFromCPU(cpus[i], map))) + goto error; + + if (cpus[i]->vendor) { + vn = cpus[i]->vendor; + } else { + outputVendor = false; + } + + if (vn) { + if (!vendor) { + if (!(vendor = armVendorFindByName(map, vn))) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Unknown CPU vendor %s"), vn); + goto error; + } + } else if (STRNEQ(vendor->name, vn)) { + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("CPU vendors do not match")); + goto error; + } + } + + armDataIntersect(&baseModel->data, &model->data); + armModelFree(model); + model = NULL; + } + + if (armDecode(cpu, &baseModel->data, models) < 0) + goto error; + + if (!outputVendor) + VIR_FREE(cpu->vendor); + + cpu->arch = VIR_ARCH_NONE; + + cleanup: + armModelFree(baseModel); return cpu; + + error: + armModelFree(model); + virCPUDefFree(cpu); + cpu = NULL; + goto cleanup; } + static virCPUCompareResult -virCPUarmCompare(virCPUDefPtr host ATTRIBUTE_UNUSED, - virCPUDefPtr cpu ATTRIBUTE_UNUSED, - bool failMessages ATTRIBUTE_UNUSED) +armCompute(virCPUDefPtr host, + virCPUDefPtr cpu, + virCPUDataPtr *guestData, + char **message) { - return VIR_CPU_COMPARE_IDENTICAL; + virCPUarmMapPtr map = NULL; + virCPUarmModelPtr hostModel = NULL; + virCPUarmModelPtr guestModel = NULL; + virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR; + virArch arch; + size_t i; + + if (cpu->arch != VIR_ARCH_NONE) { + bool found = false; + + for (i = 0; i < ARRAY_CARDINALITY(archs); i++) { + if (archs[i] == cpu->arch) { + found = true; + break; + } + } + + if (!found) { + VIR_DEBUG("CPU arch %s does not match host arch", + virArchToString(cpu->arch)); + if (message && + virAsprintf(message, + _("CPU arch %s does not match host arch"), + virArchToString(cpu->arch)) < 0) + goto cleanup; + + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto cleanup; + } + arch = cpu->arch; + } else { + arch = host->arch; + } + + if (cpu->vendor && + (!host->vendor || STRNEQ(cpu->vendor, host->vendor))) { + VIR_DEBUG("host CPU vendor does not match required CPU vendor %s", + cpu->vendor); + if (message && + virAsprintf(message, + _("host CPU vendor does not match required " + "CPU vendor %s"), + cpu->vendor) < 0) + goto cleanup; + + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto cleanup; + } + + if (!(map = virCPUarmGetMap())) + goto cleanup; + + /* Host CPU information */ + if (!(hostModel = armModelFromCPU(host, map))) + goto cleanup; + + if (cpu->type == VIR_CPU_TYPE_GUEST) { + /* Guest CPU information */ + switch (cpu->mode) { + case VIR_CPU_MODE_HOST_MODEL: + case VIR_CPU_MODE_HOST_PASSTHROUGH: + /* host-model and host-passthrough: + * the guest CPU is the same as the host */ + guestModel = armModelCopy(hostModel); + break; + + case VIR_CPU_MODE_CUSTOM: + /* custom: + * look up guest CPU information */ + guestModel = armModelFromCPU(cpu, map); + break; + } + } else { + /* Other host CPU information */ + guestModel = armModelFromCPU(cpu, map); + } + + if (!guestModel) + goto cleanup; + + if (STRNEQ(guestModel->name, hostModel->name)) { + VIR_DEBUG("host CPU model %s does not match required CPU model %s", + hostModel->name, guestModel->name); + if (message && + virAsprintf(message, + _("host CPU model %s does not match required " + "CPU model %s"), + hostModel->name, guestModel->name) < 0) + goto cleanup; + + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto cleanup; + } + + if (!armFeaturesIsSub(guestModel->data.features, hostModel->data.features)) { + VIR_DEBUG("guest CPU features '%s' is not subset of " + "host CPU features '%s'", + guestModel->data.features, hostModel->data.features); + if (message && + virAsprintf(message, + _("guest CPU features '%s' is not subset of " + "host CPU features '%s'"), + guestModel->data.features, + hostModel->data.features) < 0) + goto cleanup; + + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + goto cleanup; + } + + if (guestData && + !(*guestData = armMakeCPUData(arch, &guestModel->data))) + goto cleanup; + + ret = VIR_CPU_COMPARE_IDENTICAL; + + cleanup: + armModelFree(hostModel); + armModelFree(guestModel); + return ret; } +static virCPUCompareResult +virCPUarmCompare(virCPUDefPtr host, + virCPUDefPtr cpu, + bool failIncompatible) +{ + virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR; + char *message = NULL; + + if (!host || !host->model) { + if (failIncompatible) { + virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", + _("unknown host CPU")); + } else { + VIR_WARN("unknown host CPU"); + ret = VIR_CPU_COMPARE_INCOMPATIBLE; + } + return ret; + } + + ret = armCompute(host, cpu, NULL, &message); + + if (failIncompatible && ret == VIR_CPU_COMPARE_INCOMPATIBLE) { + ret = VIR_CPU_COMPARE_ERROR; + if (message) { + virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", message); + } else { + virReportError(VIR_ERR_CPU_INCOMPATIBLE, NULL); + } + } + VIR_FREE(message); + + return ret; +} + + struct cpuArchDriver cpuDriverArm = { .name = "arm", .arch = archs, .narch = ARRAY_CARDINALITY(archs), .compare = virCPUarmCompare, - .decode = NULL, + .decode = armDecodeCPUData, .encode = NULL, + .dataFree = virCPUarmDataFree, .baseline = virCPUarmBaseline, .update = virCPUarmUpdate, }; -- 2.19.1