On 2013年04月11日 17:50, Daniel P. Berrange wrote:
On Thu, Mar 14, 2013 at 02:54:21PM +0800, Li Zhang wrote:
> From: Li Zhang <zhlcindy(a)linux.vnet.ibm.com>
>
> This patch adds a CPU feature "powernv" identifying IBM Power
> processor that supports native hypervisor e.g. KVM. This can
> be used by virtualization management software to determine
> the CPU capabilities. PVR doesn't indicate whether it a
> host or a guest CPU. So, we use /proc/cpuinfo to get the
> platform information (PowerNV) and use that to set the
> "powernv" flag.
>
> Signed-off-by: Dipankar Sarma <dipankar(a)in.ibm.com>
> Signed-off-by: Li Zhang <zhlcindy(a)linux.vnet.ibm.com>
> ---
> src/cpu/cpu_map.xml | 9 ++
> src/cpu/cpu_powerpc.c | 349 ++++++++++++++++++++++++++++++++++++++----------
> src/cpu/cpu_ppc_data.h | 4 +
> src/util/sysinfo.c | 2 +-
> 4 files changed, 294 insertions(+), 70 deletions(-)
>
> diff --git a/src/cpu/cpu_map.xml b/src/cpu/cpu_map.xml
> index eb69a34..6b1f9b9 100644
> --- a/src/cpu/cpu_map.xml
> +++ b/src/cpu/cpu_map.xml
> @@ -587,14 +587,23 @@
> <arch name='ppc64'>
> <!-- vendor definitions -->
> <vendor name='IBM' string='PowerPC'/>
> + <feature name='powernv'> <!-- SYSTEMID_POWERNV -->
> + <systemid platform='0x00000001'/>
> + </feature>
> <!-- IBM-based CPU models -->
> <model name='POWER7'>
> + <feature name='powernv'/>
> + <systemid pvr='0x003f0200'/>
> <vendor name='IBM'/>
> </model>
> <model name='POWER7_v2.1'>
> + <feature name='powernv'/>
> + <systemid pvr='0x003f0201'/>
> <vendor name='IBM'/>
> </model>
> <model name='POWER7_v2.3'>
> + <feature name='powernv'/>
> + <systemid pvr='0x003f0203'/>
> <vendor name='IBM'/>
> </model>
> </arch>
> diff --git a/src/cpu/cpu_powerpc.c b/src/cpu/cpu_powerpc.c
> index ac10789..2135944 100644
> --- a/src/cpu/cpu_powerpc.c
> +++ b/src/cpu/cpu_powerpc.c
> @@ -33,6 +33,7 @@
>
> #include "cpu_map.h"
> #include "buf.h"
> +#include "sysinfo.h"
>
> #define VIR_FROM_THIS VIR_FROM_CPU
>
> @@ -44,16 +45,9 @@ struct cpuPowerPC {
> uint32_t pvr;
> };
>
> -static const struct cpuPowerPC cpu_defs[] = {
> - {"POWER7", "IBM", 0x003f0200},
> - {"POWER7_v2.1", "IBM", 0x003f0201},
> - {"POWER7_v2.3", "IBM", 0x003f0203},
> - {NULL, NULL, 0xffffffff}
> -};
> -
> -
> struct ppc_vendor {
> char *name;
> + uint32_t pvr;
> struct ppc_vendor *next;
> };
>
> @@ -64,64 +58,191 @@ struct ppc_model {
> struct ppc_model *next;
> };
>
> +struct ppc_feature {
> + char *name;
> + union cpuData *data;
> + struct ppc_feature *next;
> +};
> +
> struct ppc_map {
> struct ppc_vendor *vendors;
> + struct ppc_feature *features;
> struct ppc_model *models;
> };
>
> +static void
> +ppcDataSubtract(union cpuData *data1,
> + const union cpuData *data2)
> +{
> + data1->ppc.platform &= ~data2->ppc.platform;
> +}
> +
> +static bool
> +ppcDataIsSubset(const union cpuData *data,
> + const union cpuData *subset)
> +{
> + if (subset->ppc.platform &&
> + (data->ppc.platform & subset->ppc.platform) ==
subset->ppc.platform)
> + return true;
> +
> + return false;
> +}
> +
> +static void
> +PowerPCDataFree(union cpuData *data)
> +{
> + if (data == NULL)
> + return;
> + VIR_FREE(data);
> +}
> +
> +
> +static union cpuData *
> +ppcDataCopy(const union cpuData *data)
> +{
> + union cpuData *copy = NULL;
> +
> + if (VIR_ALLOC(copy) < 0) {
> + PowerPCDataFree(copy);
> + return NULL;
> + }
> + copy->ppc = data->ppc;
> + return copy;
> +}
> +
> +
> +/* also removes all detected features from data */
> static int
> -ConvertModelVendorFromPVR(char ***model,
> - char ***vendor,
> - uint32_t pvr)
> +ppcDataToCPUFeatures(virCPUDefPtr cpu,
> + int policy,
> + union cpuData *data,
> + const struct ppc_map *map)
> {
> - int i;
> + const struct ppc_feature *feature = map->features;
>
> - for (i = 0; cpu_defs[i].name; i++) {
> - if (cpu_defs[i].pvr == pvr) {
> - **model = strdup(cpu_defs[i].name);
> - **vendor = strdup(cpu_defs[i].vendor);
> - return 0;
> + while (feature != NULL) {
> + if (ppcDataIsSubset(data, feature->data)) {
> + ppcDataSubtract(data, feature->data);
> + if (virCPUDefAddFeature(cpu, feature->name, policy) < 0)
> + return -1;
> }
> + feature = feature->next;
> }
>
> - virReportError(VIR_ERR_INTERNAL_ERROR,
> - "%s", _("Missing the definition of this
model"));
> - return -1;
> + return 0;
> }
>
> -static int
> -ConvertPVRFromModel(const char *model,
> - uint32_t *pvr)
> +static struct ppc_feature *
> +ppcFeatureNew(void)
> {
> - int i;
> + struct ppc_feature *feature;
>
> - for (i = 0; cpu_defs[i].name; i++) {
> - if (STREQ(cpu_defs[i].name, model)) {
> - *pvr = cpu_defs[i].pvr;
> - return 0;
> - }
> + if (VIR_ALLOC(feature) < 0)
> + return NULL;
> +
> + if (VIR_ALLOC(feature->data) < 0) {
> + VIR_FREE(feature);
> + return NULL;
> + }
> +
> + return feature;
> +}
> +
> +
> +static void
> +ppcFeatureFree(struct ppc_feature *feature)
> +{
> + if (feature == NULL)
> + return;
> +
> + VIR_FREE(feature->name);
> + PowerPCDataFree(feature->data);
> + VIR_FREE(feature);
> +}
> +
> +
> +static struct ppc_feature *
> +ppcFeatureFind(const struct ppc_map *map,
> + const char *name)
> +{
> + struct ppc_feature *feature;
> +
> + feature = map->features;
> + while (feature != NULL) {
> + if (STREQ(feature->name, name))
> + return feature;
> +
> + feature = feature->next;
> }
>
> - virReportError(VIR_ERR_INTERNAL_ERROR,
> - "%s", _("Missing the definition of this
model"));
> - return -1;
> + return NULL;
> }
>
> static int
> -cpuMatch(const union cpuData *data,
> - char **cpu_model,
> - char **cpu_vendor,
> - const struct ppc_model *model)
> +ppcFeatureLoad(xmlXPathContextPtr ctxt,
> + struct ppc_map *map)
> {
> + xmlNodePtr *nodes = NULL;
> + xmlNodePtr ctxt_node = ctxt->node;
> + struct ppc_feature *feature;
> int ret = 0;
> + unsigned long platform = 0;
> + unsigned long pvr = 0;
> + int ret_platform;
> + int ret_pvr;
> + int n;
> +
> + if (!(feature = ppcFeatureNew()))
> + goto no_memory;
> +
> + feature->name = virXPathString("string(@name)", ctxt);
> + if (feature->name == NULL) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + "%s", _("Missing CPU feature name"));
> + goto ignore;
> + }
> +
> + if (ppcFeatureFind(map, feature->name)) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("CPU feature %s already defined"),
feature->name);
> + goto ignore;
> + }
> +
> + n = virXPathNodeSet("./systemid", ctxt, &nodes);
> + if (n < 0)
> + goto ignore;
> +
> + ctxt->node = nodes[0];
> + ret_platform = virXPathULongHex("string(@platform)", ctxt,
&platform);
> + ret_pvr = virXPathULongHex("string(@pvr)", ctxt, &pvr);
> + if (ret_platform < 0 && ret_pvr < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Invalid systemid in %s feature"),
feature->name);
> + goto ignore;
> + }
> + feature->data->ppc.platform = platform;
> + feature->data->ppc.pvr = pvr;
>
> - ret = ConvertModelVendorFromPVR(&cpu_model, &cpu_vendor,
data->ppc.pvr);
> + if (map->features == NULL)
> + map->features = feature;
> + else {
> + feature->next = map->features;
> + map->features = feature;
> + }
>
> - if (STREQ(model->name, *cpu_model) &&
> - STREQ(model->vendor->name, *cpu_vendor))
> - ret = 1;
> +out:
> + ctxt->node = ctxt_node;
> + VIR_FREE(nodes);
>
> return ret;
> +
> +no_memory:
> + virReportOOMError();
> + ret = -1;
> +
> +ignore:
> + ppcFeatureFree(feature);
> + goto out;
> }
>
>
> @@ -171,6 +292,66 @@ ppcModelFind(const struct ppc_map *map,
> return NULL;
> }
>
> +/* also removes bits corresponding to vendor string from data */
> +static const struct ppc_vendor *
> +ppcDataToVendor(union cpuData *data,
> + const struct ppc_map *map)
> +{
> + const struct ppc_vendor *vendor = map->vendors;
> +
> + while (vendor) {
> + if (data->ppc.pvr == vendor->pvr)
> + return vendor;
> + vendor = vendor->next;
> + }
> +
> + return NULL;
> +}
> +
> +
> +static virCPUDefPtr
> +ppcDataToCPU(const union cpuData *data,
> + const struct ppc_model *model,
> + const struct ppc_map *map)
> +{
> + virCPUDefPtr cpu;
> + union cpuData *copy = NULL;
> + union cpuData *modelData = NULL;
> + const struct ppc_vendor *vendor;
> +
> + if (VIR_ALLOC(cpu) < 0 ||
> + !(cpu->model = strdup(model->name)) ||
> + !(copy = ppcDataCopy(data)) ||
> + !(modelData = ppcDataCopy(model->data)))
> + goto no_memory;
> +
> + if ((vendor = ppcDataToVendor(copy, map)) &&
> + !(cpu->vendor = strdup(vendor->name)))
> + goto no_memory;
> +
> + ppcDataSubtract(copy, modelData);
> + ppcDataSubtract(modelData, data);
> +
> + /* because feature policy is ignored for host CPU */
> + cpu->type = VIR_CPU_TYPE_GUEST;
> +
> + if (ppcDataToCPUFeatures(cpu, VIR_CPU_FEATURE_REQUIRE, copy, map) ||
> + ppcDataToCPUFeatures(cpu, VIR_CPU_FEATURE_DISABLE, modelData, map))
> + goto error;
> +
> +cleanup:
> + PowerPCDataFree(modelData);
> + PowerPCDataFree(copy);
> + return cpu;
> +
> +no_memory:
> + virReportOOMError();
> +error:
> + virCPUDefFree(cpu);
> + cpu = NULL;
> + goto cleanup;
> +}
> +
> static struct ppc_vendor *
> ppcVendorFind(const struct ppc_map *map,
> const char *name)
> @@ -256,6 +437,9 @@ ppcModelLoad(xmlXPathContextPtr ctxt,
> xmlNodePtr *nodes = NULL;
> struct ppc_model *model;
> char *vendor = NULL;
> + unsigned long pvr = 0, platform = 0;
> + int ret_platform, ret_pvr;
> + int n;
> int ret = -1;
>
> if (!(model = ppcModelNew()))
> @@ -268,10 +452,20 @@ ppcModelLoad(xmlXPathContextPtr ctxt,
> goto ignore;
> }
>
> - ret = ConvertPVRFromModel(model->name, &model->data->ppc.pvr);
> - if (ret < 0)
> - goto ignore;
> + n = virXPathNodeSet("./systemid", ctxt, &nodes);
> + if (n < 0)
> + goto ignore;
>
> + ctxt->node = nodes[0];
> + ret_platform = virXPathULongHex("string(@platform)", ctxt,
&platform);
> + ret_pvr = virXPathULongHex("string(@pvr)", ctxt, &pvr);
> + if (ret_platform < 0 && ret_pvr < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Invalid systemid in %s model"),
model->name);
> + goto ignore;
> + }
> + model->data->ppc.platform = platform;
> + model->data->ppc.pvr = pvr;
>
> if (virXPathBoolean("boolean(./vendor)", ctxt)) {
> vendor = virXPathString("string(./vendor/@name)", ctxt);
> @@ -324,6 +518,8 @@ ppcMapLoadCallback(enum cpuMapElement element,
> return ppcVendorLoad(ctxt, map);
> case CPU_MAP_ELEMENT_MODEL:
> return ppcModelLoad(ctxt, map);
> + case CPU_MAP_ELEMENT_FEATURE:
> + return ppcFeatureLoad(ctxt, map);
> default:
> break;
> }
> @@ -385,6 +581,7 @@ ppcModelCopy(const struct ppc_model *model)
> }
>
> copy->data->ppc.pvr = model->data->ppc.pvr;
> + copy->data->ppc.platform = model->data->ppc.platform;
> copy->vendor = model->vendor;
>
> return copy;
> @@ -437,8 +634,6 @@ PowerPCDecode(virCPUDefPtr cpu,
> const struct ppc_model *candidate;
> virCPUDefPtr cpuCandidate;
> virCPUDefPtr cpuModel = NULL;
> - char *cpu_vendor = NULL;
> - char *cpu_model = NULL;
> unsigned int i;
>
> if (data == NULL || (map = ppcLoadMap()) == NULL)
> @@ -475,13 +670,30 @@ PowerPCDecode(virCPUDefPtr cpu,
> goto next;
> }
>
> - if (VIR_ALLOC(cpuCandidate) < 0) {
> - virReportOOMError();
> + if (!(cpuCandidate = ppcDataToCPU(data, candidate, map)))
> goto out;
> +
> + if (candidate->vendor && cpuCandidate->vendor &&
> + STRNEQ(candidate->vendor->name, cpuCandidate->vendor)) {
> + VIR_DEBUG("CPU vendor %s of model %s differs from %s;
ignoring",
> + candidate->vendor->name, candidate->name,
> + cpuCandidate->vendor);
> + virCPUDefFree(cpuCandidate);
> + goto next;
> }
>
> - cpuCandidate->model = strdup(candidate->name);
> - cpuCandidate->vendor = strdup(candidate->vendor->name);
> + if (cpu->type == VIR_CPU_TYPE_HOST) {
> + cpuCandidate->type = VIR_CPU_TYPE_HOST;
> + for (i = 0; i < cpuCandidate->nfeatures; i++) {
> + switch (cpuCandidate->features[i].policy) {
> + case VIR_CPU_FEATURE_DISABLE:
> + virCPUDefFree(cpuCandidate);
> + goto next;
> + default:
> + cpuCandidate->features[i].policy = -1;
> + }
> + }
> + }
>
> if (preferred && STREQ(cpuCandidate->model, preferred)) {
> virCPUDefFree(cpuModel);
> @@ -489,19 +701,12 @@ PowerPCDecode(virCPUDefPtr cpu,
> break;
> }
>
> - ret = cpuMatch(data, &cpu_model, &cpu_vendor, candidate);
> - if (ret < 0) {
> - VIR_FREE(cpuCandidate);
> - goto out;
> - } else if (ret == 1) {
> - cpuCandidate->model = cpu_model;
> - cpuCandidate->vendor = cpu_vendor;
> + if (cpuModel == NULL
> + || cpuModel->nfeatures > cpuCandidate->nfeatures) {
> virCPUDefFree(cpuModel);
> cpuModel = cpuCandidate;
> - break;
> - }
> -
> - virCPUDefFree(cpuCandidate);
> + }else
> + virCPUDefFree(cpuCandidate);
>
> next:
> candidate = candidate->next;
> @@ -515,6 +720,8 @@ PowerPCDecode(virCPUDefPtr cpu,
>
> cpu->model = cpuModel->model;
> cpu->vendor = cpuModel->vendor;
> + cpu->nfeatures = cpuModel->nfeatures;
> + cpu->features = cpuModel->features;
> VIR_FREE(cpuModel);
>
> ret = 0;
> @@ -537,19 +744,11 @@ static uint32_t ppc_mfpvr(void)
> }
> #endif
>
> -static void
> -PowerPCDataFree(union cpuData *data)
> -{
> - if (data == NULL)
> - return;
> -
> - VIR_FREE(data);
> -}
> -
> static union cpuData *
> PowerPCNodeData(void)
> {
> union cpuData *data;
> + virSysinfoDefPtr hostinfo;
>
> if (VIR_ALLOC(data) < 0) {
> virReportOOMError();
> @@ -561,6 +760,18 @@ PowerPCNodeData(void)
> data->ppc.pvr = ppc_mfpvr();
> #endif
>
> + hostinfo = virSysinfoRead();
> + if (hostinfo == NULL) {
> + VIR_FREE(data);
> + return NULL;
> + }
> +
> + data->ppc.platform &= ~VIR_CPU_PPC64_POWERNV;
> +
> + if (STREQ(hostinfo->system_family, "PowerNV"))
> + data->ppc.platform |= VIR_CPU_PPC64_POWERNV;
> + virSysinfoDefFree(hostinfo);
> +
> return data;
> }
>
> diff --git a/src/cpu/cpu_ppc_data.h b/src/cpu/cpu_ppc_data.h
> index 685332a..0e691ce 100644
> --- a/src/cpu/cpu_ppc_data.h
> +++ b/src/cpu/cpu_ppc_data.h
> @@ -28,6 +28,10 @@
>
> struct cpuPPCData {
> uint32_t pvr;
> + uint32_t platform; /* Bitflag indicating platform features */
> };
>
> +#define VIR_CPU_PPC64_NONE 0x0
> +#define VIR_CPU_PPC64_POWERNV 0x1
I'm not sure what to say about this. Superficially it looks ok, but I'm
not all that familiar with this code in libvirt, nor PPC. So I think I'd
prefer Jiri to look at this at least from the libvirt POV.
Sure, thanks.
Jiri, would you please help review this patch?
> diff --git a/src/util/sysinfo.c b/src/util/sysinfo.c
> index 6a5db80..5f98986 100644
> --- a/src/util/sysinfo.c
> +++ b/src/util/sysinfo.c
> @@ -234,7 +234,7 @@ virSysinfoRead(void) {
> if (VIR_ALLOC(ret) < 0)
> goto no_memory;
>
> - if (virFileReadAll(CPUINFO, 2048, &outbuf) < 0) {
> + if (virFileReadAll(CPUINFO, 8192, &outbuf) < 0) {
> virReportError(VIR_ERR_INTERNAL_ERROR,
> _("Failed to open %s"), CPUINFO);
> return NULL;
This change seems like it probably ought to be separate.
Daniel