[PATCH 0/5] Introduce getHost support for ARM cpus

Introduce getHost support for ARM cpus. First add some common ARM server models to cpu_map, then add some helper methods to parse and load them. The 'lscpu' command from 'util-linux' provides very good data for cpu information, we can parse them directly. Zhenyu Zheng (5): cpu_map: Introduce arm CPU models cpu: Introduce virCPUarmData to virCPUData cpu: Introduce arm related structs cpu: Add helper functions to parse vendor and model for ARM cpu: Introduce getHost supoort for ARM src/cpu/cpu.h | 2 + src/cpu/cpu_arm.c | 412 +++++++++++++++++++++++++++++- src/cpu/cpu_arm_data.h | 32 +++ src/cpu_map/Makefile.inc.am | 7 + src/cpu_map/arm_Falkor.xml | 15 ++ src/cpu_map/arm_Kunpeng-920.xml | 23 ++ src/cpu_map/arm_ThunderX299xx.xml | 15 ++ src/cpu_map/arm_cortex-a53.xml | 15 ++ src/cpu_map/arm_cortex-a57.xml | 14 + src/cpu_map/arm_cortex-a72.xml | 14 + src/cpu_map/arm_vendors.xml | 14 + src/cpu_map/index.xml | 16 ++ 12 files changed, 577 insertions(+), 2 deletions(-) create mode 100644 src/cpu/cpu_arm_data.h create mode 100644 src/cpu_map/arm_Falkor.xml create mode 100644 src/cpu_map/arm_Kunpeng-920.xml create mode 100644 src/cpu_map/arm_ThunderX299xx.xml create mode 100644 src/cpu_map/arm_cortex-a53.xml create mode 100644 src/cpu_map/arm_cortex-a57.xml create mode 100644 src/cpu_map/arm_cortex-a72.xml create mode 100644 src/cpu_map/arm_vendors.xml -- 2.26.0.windows.1

From: Zhenyu Zheng <zheng.zhenyu@outlook.com> Introduce vendors and models information for ARM arch, these will be used for virConnectGetCapabilities on ARM arch. Signed-off-by: Zhenyu Zheng <zhengzhenyulixi@gmail.com> --- src/cpu_map/Makefile.inc.am | 7 +++++++ src/cpu_map/arm_Falkor.xml | 15 +++++++++++++++ src/cpu_map/arm_Kunpeng-920.xml | 23 +++++++++++++++++++++++ src/cpu_map/arm_ThunderX299xx.xml | 15 +++++++++++++++ src/cpu_map/arm_cortex-a53.xml | 15 +++++++++++++++ src/cpu_map/arm_cortex-a57.xml | 14 ++++++++++++++ src/cpu_map/arm_cortex-a72.xml | 14 ++++++++++++++ src/cpu_map/arm_vendors.xml | 14 ++++++++++++++ src/cpu_map/index.xml | 16 ++++++++++++++++ 9 files changed, 133 insertions(+) create mode 100644 src/cpu_map/arm_Falkor.xml create mode 100644 src/cpu_map/arm_Kunpeng-920.xml create mode 100644 src/cpu_map/arm_ThunderX299xx.xml create mode 100644 src/cpu_map/arm_cortex-a53.xml create mode 100644 src/cpu_map/arm_cortex-a57.xml create mode 100644 src/cpu_map/arm_cortex-a72.xml create mode 100644 src/cpu_map/arm_vendors.xml diff --git a/src/cpu_map/Makefile.inc.am b/src/cpu_map/Makefile.inc.am index be64c9a0d4..2e987d1969 100644 --- a/src/cpu_map/Makefile.inc.am +++ b/src/cpu_map/Makefile.inc.am @@ -3,6 +3,13 @@ cpumapdir = $(pkgdatadir)/cpu_map cpumap_DATA = \ cpu_map/arm_features.xml \ + cpu_map/arm_vendors.xml \ + cpu_map/arm_cortex-a53.xml \ + cpu_map/arm_cortex-a57.xml \ + cpu_map/arm_cortex-a72.xml \ + cpu_map/arm_Falkor.xml \ + cpu_map/arm_Kunpeng-920.xml \ + cpu_map/arm_ThunderX299xx.xml \ cpu_map/index.xml \ cpu_map/ppc64_vendors.xml \ cpu_map/ppc64_POWER7.xml \ diff --git a/src/cpu_map/arm_Falkor.xml b/src/cpu_map/arm_Falkor.xml new file mode 100644 index 0000000000..5a8ba53644 --- /dev/null +++ b/src/cpu_map/arm_Falkor.xml @@ -0,0 +1,15 @@ +<cpus> + <model name='Falkor'> + <vendor name='Qualcomm'/> + <feature name="fp"/> + <feature name="asimd"/> + <feature name="evtstrm"/> + <feature name="aes"/> + <feature name="pmull"/> + <feature name="sha1"/> + <feature name="sha2"/> + <feature name="crc32"/> + <feature name="cpuid"/> + <feature name="asimdrdm"/> + </model> +</cpus> diff --git a/src/cpu_map/arm_Kunpeng-920.xml b/src/cpu_map/arm_Kunpeng-920.xml new file mode 100644 index 0000000000..374e12cda6 --- /dev/null +++ b/src/cpu_map/arm_Kunpeng-920.xml @@ -0,0 +1,23 @@ +<cpus> + <model name='Kunpeng-920'> + <vendor name='HiSilicon'/> + <feature name="fp"/> + <feature name="asimd"/> + <feature name="evtstrm"/> + <feature name="aes"/> + <feature name="pmull"/> + <feature name="sha1"/> + <feature name="sha2"/> + <feature name="crc32"/> + <feature name="atomics"/> + <feature name="fphp"/> + <feature name="asimdhp"/> + <feature name="cpuid"/> + <feature name="asimdrdm"/> + <feature name="jscvt"/> + <feature name="fcma"/> + <feature name="dcpop"/> + <feature name="asimddp"/> + <feature name="asimdfhm"/> + </model> +</cpus> diff --git a/src/cpu_map/arm_ThunderX299xx.xml b/src/cpu_map/arm_ThunderX299xx.xml new file mode 100644 index 0000000000..0357273738 --- /dev/null +++ b/src/cpu_map/arm_ThunderX299xx.xml @@ -0,0 +1,15 @@ +<cpus> + <model name='ThunderX299xx'> + <vendor name='Cavium'/> + <feature name="fp"/> + <feature name="asimd"/> + <feature name="evtstrm"/> + <feature name="aes"/> + <feature name="pmull"/> + <feature name="sha1"/> + <feature name="sha2"/> + <feature name="crc32"/> + <feature name="cpuid"/> + <feature name="asimdrdm"/> + </model> +</cpus> diff --git a/src/cpu_map/arm_cortex-a53.xml b/src/cpu_map/arm_cortex-a53.xml new file mode 100644 index 0000000000..3375e3f67d --- /dev/null +++ b/src/cpu_map/arm_cortex-a53.xml @@ -0,0 +1,15 @@ +<cpus> + <model name='cortex-a53'> + <vendor name='ARM'/> + <feature name="fp"/> + <feature name="asimd"/> + <feature name="evtstrm"/> + <feature name="aes"/> + <feature name="pmull"/> + <feature name="sha1"/> + <feature name="sha2"/> + <feature name="crc32"/> + <feature name="cpuid"/> + <feature name="asimdrdm"/> + </model> +</cpus> diff --git a/src/cpu_map/arm_cortex-a57.xml b/src/cpu_map/arm_cortex-a57.xml new file mode 100644 index 0000000000..ae9c838e99 --- /dev/null +++ b/src/cpu_map/arm_cortex-a57.xml @@ -0,0 +1,14 @@ +<cpus> + <model name='cortex-a57'> + <vendor name='ARM'/> + <feature name="fp"/> + <feature name="asimd"/> + <feature name="evtstrm"/> + <feature name="aes"/> + <feature name="pmull"/> + <feature name="sha1"/> + <feature name="sha2"/> + <feature name="crc32"/> + <feature name="cpuid"/> + </model> +</cpus> diff --git a/src/cpu_map/arm_cortex-a72.xml b/src/cpu_map/arm_cortex-a72.xml new file mode 100644 index 0000000000..5c46d32da2 --- /dev/null +++ b/src/cpu_map/arm_cortex-a72.xml @@ -0,0 +1,14 @@ +<cpus> + <model name='cortex-a72'> + <vendor name='ARM'/> + <feature name="fp"/> + <feature name="asimd"/> + <feature name="evtstrm"/> + <feature name="aes"/> + <feature name="pmull"/> + <feature name="sha1"/> + <feature name="sha2"/> + <feature name="crc32"/> + <feature name="cpuid"/> + </model> +</cpus> diff --git a/src/cpu_map/arm_vendors.xml b/src/cpu_map/arm_vendors.xml new file mode 100644 index 0000000000..703c2112b1 --- /dev/null +++ b/src/cpu_map/arm_vendors.xml @@ -0,0 +1,14 @@ +<cpus> + <vendor name="ARM" value="0x41"/> + <vendor name="Broadcom" value="0x42"/> + <vendor name="Cavium" value="0x43"/> + <vendor name="DigitalEquipment" value="0x44"/> + <vendor name="HiSilicon" value="0x48"/> + <vendor name="Infineon" value="0x49"/> + <vendor name="Freescale" value="0x4D"/> + <vendor name="NVIDIA" value="0x4E"/> + <vendor name="APM" value="0x50"/> + <vendor name="Qualcomm" value="0x51"/> + <vendor name="Marvell" value="0x56"/> + <vendor name="Intel" value="0x69"/> +</cpus> diff --git a/src/cpu_map/index.xml b/src/cpu_map/index.xml index 50b030de29..49a261f8f8 100644 --- a/src/cpu_map/index.xml +++ b/src/cpu_map/index.xml @@ -86,5 +86,21 @@ <arch name='arm'> <include filename='arm_features.xml'/> + <include filename='arm_vendors.xml'/> + + <!-- ARM-based CPU models --> + <include filename='arm_cortex-a53.xml'/> + <include filename='arm_cortex-a57.xml'/> + <include filename='arm_cortex-a72.xml'/> + + <!-- Qualcomm-based CPU models --> + <include filename='arm_Falkor.xml'/> + + <!-- Cavium-based CPU models --> + <include filename='arm_ThunderX299xx.xml'/> + + <!-- HiSilicon-based CPU models --> + <include filename='arm_Kunpeng-920.xml'/> + </arch> </cpus> -- 2.26.0.windows.1

Introduce virCPUarmData to virCPUData Signed-off-by: Zhenyu Zheng <zhengzhenyulixi@gmail.com> --- src/cpu/cpu.h | 2 ++ src/cpu/cpu_arm_data.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/cpu/cpu_arm_data.h diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index f779d2be17..ec22a183a1 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -27,6 +27,7 @@ #include "cpu_conf.h" #include "cpu_x86_data.h" #include "cpu_ppc64_data.h" +#include "cpu_arm_data.h" typedef struct _virCPUData virCPUData; @@ -36,6 +37,7 @@ struct _virCPUData { union { virCPUx86Data x86; virCPUppc64Data ppc64; + virCPUarmData arm; /* generic driver needs no data */ } data; }; diff --git a/src/cpu/cpu_arm_data.h b/src/cpu/cpu_arm_data.h new file mode 100644 index 0000000000..000a3c282f --- /dev/null +++ b/src/cpu/cpu_arm_data.h @@ -0,0 +1,32 @@ +/* + * cpu_arm_data.h: 64-bit arm CPU specific data + * + * Copyright (C) 2020. Huawei Technologies Co., Ltd. All rights reserved. + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#pragma once + + +#define VIR_CPU_ARM_DATA_INIT { 0 } + +typedef struct _virCPUarmData virCPUarmData; +struct _virCPUarmData { + char *vendor_id; + char *model_name; + char *features; +}; -- 2.26.0.windows.1

Introduce arm related structs cpu_arm.c --- src/cpu/cpu_arm.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c index ee5802198f..2a05f43118 100644 --- a/src/cpu/cpu_arm.c +++ b/src/cpu/cpu_arm.c @@ -26,6 +26,8 @@ #include "cpu_map.h" #include "virstring.h" #include "virxml.h" +#include "cpu_map.h" +#include "cpu_arm.h" #define VIR_FROM_THIS VIR_FROM_CPU @@ -36,6 +38,21 @@ static const virArch archs[] = { VIR_ARCH_AARCH64, }; +typedef struct _virCPUarmVendor virCPUarmVendor; +typedef virCPUarmVendor *virCPUarmVendorPtr; +struct _virCPUarmVendor { + char *name; + unsigned long value; +}; + +typedef struct _virCPUarmModel virCPUarmModel; +typedef virCPUarmModel *virCPUarmModelPtr; +struct _virCPUarmModel { + char *name; + virCPUarmVendorPtr vendor; + virCPUarmData data; +}; + typedef struct _virCPUarmFeature virCPUarmFeature; typedef virCPUarmFeature *virCPUarmFeaturePtr; struct _virCPUarmFeature { @@ -64,9 +81,53 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPUarmFeature, virCPUarmFeatureFree); typedef struct _virCPUarmMap virCPUarmMap; typedef virCPUarmMap *virCPUarmMapPtr; struct _virCPUarmMap { + size_t nvendors; + virCPUarmVendorPtr *vendors; + size_t nmodels; + virCPUarmModelPtr *models; GPtrArray *features; }; +static void +virCPUarmDataClear(virCPUarmData *data) +{ + if (!data) + return; + + VIR_FREE(data->features); +} + +static void +virCPUarmDataFree(virCPUDataPtr cpuData) +{ + if (!cpuData) + return; + + virCPUarmDataClear(&cpuData->data.arm); + VIR_FREE(cpuData); +} + +static void +armModelFree(virCPUarmModelPtr model) +{ + if (!model) + return; + + virCPUarmDataClear(&model->data); + g_free(model->name); + g_free(model); +} + +static void +armVendorFree(virCPUarmVendorPtr vendor) +{ + if (!vendor) + return; + + g_free(vendor->name); + g_free(vendor); +} + static virCPUarmMapPtr virCPUarmMapNew(void) { @@ -86,6 +147,16 @@ virCPUarmMapFree(virCPUarmMapPtr map) { if (!map) return; + + size_t i; + + for (i = 0; i < map->nmodels; i++) + armModelFree(map->models[i]); + g_free(map->models); + + for (i = 0; i < map->nvendors; i++) + armVendorFree(map->vendors[i]); + g_free(map->vendors); g_ptr_array_free(map->features, TRUE); -- 2.26.0.windows.1

Add helper functions to parse vendor and model for ARM and use them as callback when load maps. Signed-of-by: Zhenyu Zheng <zhengzhenyulixi@gmail.com> --- src/cpu/cpu_arm.c | 143 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c index 2a05f43118..969025b5cf 100644 --- a/src/cpu/cpu_arm.c +++ b/src/cpu/cpu_arm.c @@ -181,6 +181,147 @@ virCPUarmMapFeatureFind(virCPUarmMapPtr map, return NULL; } +static virCPUarmVendorPtr +armVendorFindByID(virCPUarmMapPtr map, + unsigned long vendor_id) +{ + size_t i; + + for (i = 0; i < map->nvendors; i++) { + if (map->vendors[i]->value == vendor_id) + return map->vendors[i]; + } + + return NULL; +} + + +static virCPUarmVendorPtr +armVendorFindByName(virCPUarmMapPtr map, + const char *name) +{ + size_t i; + + for (i = 0; i < map->nvendors; i++) { + if (STREQ(map->vendors[i]->name, name)) + return map->vendors[i]; + } + + return NULL; +} + + +static int +armVendorParse(xmlXPathContextPtr ctxt, + const char *name, + void *data) +{ + virCPUarmMapPtr map = (virCPUarmMapPtr)data; + virCPUarmVendorPtr vendor = NULL; + int ret = -1; + + if (VIR_ALLOC(vendor) < 0) + return ret; + + vendor->name = g_strdup(name); + + if (armVendorFindByName(map, vendor->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU vendor %s already defined"), vendor->name); + goto cleanup; + } + + if (virXPathULongHex("string(@value)", ctxt, &vendor->value) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing CPU vendor value")); + goto cleanup; + } + + if (armVendorFindByID(map, vendor->value)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU vendor value 0x%2lx already defined"), vendor->value); + goto cleanup; + } + + if (VIR_APPEND_ELEMENT(map->vendors, map->nvendors, vendor) < 0) + goto cleanup; + + ret = 0; + + cleanup: + armVendorFree(vendor); + return ret; + +} + +static virCPUarmModelPtr +armModelFind(virCPUarmMapPtr map, + const char *name) +{ + size_t i; + + for (i = 0; i < map->nmodels; i++) { + if (STREQ(map->models[i]->name, name)) + return map->models[i]; + } + + return NULL; +} + +static int +armModelParse(xmlXPathContextPtr ctxt, + const char *name, + void *data) +{ + virCPUarmMapPtr map = (virCPUarmMapPtr)data; + virCPUarmModel *model; + xmlNodePtr *nodes = NULL; + char *vendor = NULL; + int ret = -1; + + if (VIR_ALLOC(model) < 0) + goto error; + + model->name = g_strdup(name); + + if (armModelFind(map, model->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU model %s already defined"), model->name); + goto error; + } + + if (virXPathBoolean("boolean(./vendor)", ctxt)) { + vendor = virXPathString("string(./vendor/@name)", ctxt); + if (!vendor) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid vendor element in CPU model %s"), + model->name); + goto error; + } + + if (!(model->vendor = armVendorFindByName(map, vendor))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown vendor %s referenced by CPU model %s"), + vendor, model->name); + goto error; + } + } + + if (VIR_APPEND_ELEMENT(map->models, map->nmodels, model) < 0) + goto error; + + ret = 0; + + cleanup: + VIR_FREE(vendor); + VIR_FREE(nodes); + return ret; + + error: + armModelFree(model); + goto cleanup; +} + static int virCPUarmMapFeatureParse(xmlXPathContextPtr ctxt G_GNUC_UNUSED, const char *name, @@ -210,7 +351,7 @@ virCPUarmLoadMap(void) map = virCPUarmMapNew(); - if (cpuMapLoad("arm", NULL, virCPUarmMapFeatureParse, NULL, map) < 0) + if (cpuMapLoad("arm", armVendorParse, virCPUarmMapFeatureParse, armModelParse, map) < 0) return NULL; return g_steal_pointer(&map); -- 2.26.0.windows.1

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. 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

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 :|

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 :|

On Mon, Mar 30, 2020 at 07:32:36PM +0800, Zhenyu Zheng wrote:
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
On x86 we get majority of info straight from calling the CPUID instruction, not /proc/cpuinfo, and use our XML data files in src/cpu_map to translate things into human readable names. I see you're adding XML data files with names in the earlier patches. Is it possible to add the hex values for the CPU implementer/architecture/variant/part to these XML files so we can directly map them in libvirt, in the same way we do for x86 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 :|
participants (3)
-
Daniel P. Berrangé
-
Zhenyu Zheng
-
Zhenyu Zheng