
Hi Daniel, Thanks for thre review and reply, my first implementation was going to gather data from /proc/cpuinfo, but unlike X86, we can only get this kind of info: processor : 0 BogoMIPS : 200.00 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid CPU implementer : 0x43 CPU architecture: 8 CPU variant : 0x1 CPU part : 0x0a1 CPU revision : 1 so we have to perform some translation to perform human readable information, and I mentioned that 'lscpu' has done that too. So Andrea Bolognani suggested that maybe we can use it directly, to avoid re-implement the translation. Here is the discussion: https://www.redhat.com/archives/libvir-list/2020-March/msg00812.html BR, Zhenyu On Mon, Mar 30, 2020 at 7:27 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Fri, Mar 27, 2020 at 04:52:25PM +0800, Zhenyu Zheng wrote:
Introduce getHost support for ARM, use data from 'lscpu' cmd result. 'util-linux/lscpu' provided a very good translation of ARM cpu data start from release v2.32, use it directly to avoid re-implement the translation.
I'm a bit wary of this approach, because parsing the output of command line tools is generally fragile / liable to break in future releases, since few tools guarantee that their output format is stable for machine parsing.
How hard would it be to probe this directly in libvirt, as we do on x86 arch.
Signed-of-by: Zhenyu Zheng <zhengzhenyulixi@gmail.com> --- src/cpu/cpu_arm.c | 198 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 1 deletion(-)
diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c index 969025b5cf..30d9c0ae2e 100644 --- a/src/cpu/cpu_arm.c +++ b/src/cpu/cpu_arm.c @@ -22,8 +22,11 @@ #include <config.h>
#include "viralloc.h" +#include "virlog.h" #include "cpu.h" #include "cpu_map.h" +#include "vircommand.h" +#include "virfile.h" #include "virstring.h" #include "virxml.h" #include "cpu_map.h" @@ -31,6 +34,14 @@
#define VIR_FROM_THIS VIR_FROM_CPU
+VIR_LOG_INIT("cpu.cpu_arm"); + +static const char *lsCpuPath = "/usr/bin/lscpu"; + +#define LSCPU lsCpuPath +#define MAX_LSCPU_SIZE = (1024*1024) /* 1MB limit for lscpu output */ + + static const virArch archs[] = { VIR_ARCH_ARMV6L, VIR_ARCH_ARMV7B, @@ -464,13 +475,198 @@ virCPUarmValidateFeatures(virCPUDefPtr cpu) return 0; }
+static int +armCpuDataFromLsCpu(virCPUarmData *data) +{ + int ret = -1; + char *outbuf = NULL; + char *eol = NULL; + const char *cur; + virCommandPtr cmd = NULL; + g_autofree char *lscpu = NULL; + + if (!data) + return ret; + + lscpu = virFindFileInPath("lscpu"); + cmd = virCommandNew(lscpu); + virCommandSetOutputBuffer(cmd, &outbuf); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if ((cur = strstr(outbuf, "Vendor ID")) == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("there is no \"Vendor ID\" info in %s command
result"), LSCPU);
+ goto cleanup; + } + cur = strchr(cur, ':') + 1; + eol = strchr(cur, '\n'); + virSkipSpaces(&cur); + if (!eol) + goto cleanup; + + data->vendor_id = g_strndup(cur, eol - cur); + + if ((cur = strstr(outbuf, "Model name")) == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("there is no \"Model name\" info in %s command result"), LSCPU); + goto cleanup; + } + cur = strchr(cur, ':') + 1; + eol = strchr(cur, '\n'); + virSkipSpaces(&cur); + if (!eol) + goto cleanup; + + data->model_name = g_strndup(cur, eol - cur); + + if ((cur = strstr(outbuf, "Flags")) == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("there is no \"Flags\" info in %s command result"), LSCPU); + goto cleanup; + } + cur = strchr(cur, ':') + 1; + eol = strchr(cur, '\n'); + virSkipSpaces(&cur); + if (!eol) + goto cleanup; + + data->features = g_strndup(cur, eol - cur); + + ret = 0; + + cleanup: + virCommandFree(cmd); + VIR_FREE(outbuf); + return ret; +} + +static int +armCpuDataParseFeatures(virCPUDefPtr cpu, + const virCPUarmData *cpuData) +{ + int ret = -1; + size_t i; + char **features; + + if (!cpu || !cpuData) + return ret; + + if (!(features = virStringSplitCount(cpuData->features, " ", + 0, &cpu->nfeatures))) + return ret; + if (cpu->nfeatures) { + if (VIR_ALLOC_N(cpu->features, cpu->nfeatures) < 0) + goto error; + + for (i = 0; i < cpu->nfeatures; i++) { + cpu->features[i].policy = VIR_CPU_FEATURE_REQUIRE; + cpu->features[i].name = g_strdup(features[i]); + } + } + + ret = 0; + + cleanup: + virStringListFree(features); + return ret; + + error: + for (i = 0; i < cpu->nfeatures; i++) + VIR_FREE(cpu->features[i].name); + VIR_FREE(cpu->features); + cpu->nfeatures = 0; + goto cleanup; +} + +static int +armDecode(virCPUDefPtr cpu, + const virCPUarmData *cpuData, + virDomainCapsCPUModelsPtr models) +{ + virCPUarmMapPtr map; + virCPUarmModelPtr model; + virCPUarmVendorPtr vendor = NULL; + + if (!cpuData || !(map = virCPUarmGetMap())) + return -1; + + if (!(model = armModelFind(map, cpuData->model_name))) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Cannot find CPU model with name %s"), + cpuData->model_name); + return -1; + } + + if (!virCPUModelIsAllowed(model->name, models)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("CPU model %s is not supported by hypervisor"), + model->name); + return -1; + } + + cpu->model = g_strdup(model->name); + + if (cpuData->vendor_id && + !(vendor = armVendorFindByName(map, cpuData->vendor_id))) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Cannot find CPU vendor with vendor id %s"), + cpuData->vendor_id); + return -1; + } + + + if (vendor) + cpu->vendor = g_strdup(vendor->name); + + if (cpuData->features && + armCpuDataParseFeatures(cpu, cpuData) < 0) + return -1; + + return 0; +} + +static int +armDecodeCPUData(virCPUDefPtr cpu, + const virCPUData *data, + virDomainCapsCPUModelsPtr models) +{ + return armDecode(cpu, &data->data.arm, models); +} + +static int +virCPUarmGetHost(virCPUDefPtr cpu, + virDomainCapsCPUModelsPtr models) +{ + virCPUDataPtr cpuData = NULL; + int ret = -1; + + if (virCPUarmDriverInitialize() < 0) + goto cleanup; + + if (!(cpuData = virCPUDataNew(archs[0]))) + goto cleanup; + + if (armCpuDataFromLsCpu(&cpuData->data.arm) < 0) + goto cleanup; + + ret = armDecodeCPUData(cpu, cpuData, models); + + cleanup: + virCPUarmDataFree(cpuData); + return ret; +} + struct cpuArchDriver cpuDriverArm = { .name = "arm", .arch = archs, .narch = G_N_ELEMENTS(archs), .compare = virCPUarmCompare, - .decode = NULL, + .decode = armDecodeCPUData, .encode = NULL, + .dataFree = virCPUarmDataFree, + .getHost = virCPUarmGetHost, .baseline = virCPUarmBaseline, .update = virCPUarmUpdate, .validateFeatures = virCPUarmValidateFeatures, -- 2.26.0.windows.1
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|