[libvirt] [PATCH v1 2/3] libvirt: Implement CPU model driver for PowerPC
Li Zhang
zhlcindy at gmail.com
Fri Oct 12 07:54:53 UTC 2012
On Thu, Oct 11, 2012 at 11:12 PM, Michal Privoznik <mprivozn at redhat.com> wrote:
> On 09.10.2012 09:58, Li Zhang wrote:
>> Currently, the CPU model driver is not implemented for PowerPC.
>> Host's CPU information is needed to exposed to guests' XML file some
>> time.
>>
>> This patch is to implement the callback functions of CPU model driver.
>>
>> Signed-off-by: Li Zhang <zhlcindy at linux.vnet.ibm.com>
>> ---
>> src/cpu/cpu_map.xml | 14 ++
>> src/cpu/cpu_powerpc.c | 585 +++++++++++++++++++++++++++++++++++++++++++++++--
>> 2 files changed, 583 insertions(+), 16 deletions(-)
>>
>> diff --git a/src/cpu/cpu_map.xml b/src/cpu/cpu_map.xml
>> index 9a89e2e..affcce3 100644
>> --- a/src/cpu/cpu_map.xml
>> +++ b/src/cpu/cpu_map.xml
>> @@ -495,4 +495,18 @@
>> <feature name='fma4'/>
>> </model>
>> </arch>
>> + <arch name='ppc64'>
>> + <!-- vendor definitions -->
>> + <vendor name='IBM' string='PowerPC'/>
>> + <!-- IBM-based CPU models -->
>> + <model name='POWER7'>
>> + <vendor name='IBM'/>
>> + </model>
>> + <model name='POWER7_v2.1'>
>> + <vendor name='IBM'/>
>> + </model>
>> + <model name='POWER7_v2.3'>
>> + <vendor name='IBM'/>
>> + </model>
>> + </arch>
>> </cpus>
>> diff --git a/src/cpu/cpu_powerpc.c b/src/cpu/cpu_powerpc.c
>> index 1b77caf..3a88e68 100644
>> --- a/src/cpu/cpu_powerpc.c
>> +++ b/src/cpu/cpu_powerpc.c
>> @@ -20,18 +20,526 @@
>> * Authors:
>> * Anton Blanchard <anton at au.ibm.com>
>> * Prerna Saxena <prerna at linux.vnet.ibm.com>
>> + * Li Zhang <zhlcindy at linux.vnet.ibm.com>
>> */
>>
>> #include <config.h>
>> +#include <stdint.h>
>>
>> +#include "logging.h"
>> #include "memory.h"
>> +#include "util.h"
>> #include "cpu.h"
>>
>> +#include "cpu_map.h"
>> +#include "buf.h"
>>
>> #define VIR_FROM_THIS VIR_FROM_CPU
>>
>> +#define VENDOR_STRING_LENGTH 7
>> +
>> static const char *archs[] = { "ppc64" };
>>
>> +struct cpuPowerPC {
>> + const char *name;
>> + const char *vendor;
>> + 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 ppcVendor {
>> + char *name;
>> + struct ppcVendor *next;
>> +};
>> +
>> +struct ppcModel {
>> + char *name;
>> + const struct ppcVendor *vendor;
>> + union cpuData *data;
>> + struct ppcModel *next;
>> +};
>> +
>> +struct ppcMap {
>> + struct ppcVendor *vendors;
>> + struct ppcModel *models;
>> +};
>> +
>> +static int ConvertModelVendorFromPVR(char ***model, char ***vendor, uint32_t pvr)
>
> This line is too long. Moreover, we tend to prefix functions with the
> part of libvirt they live in. So this function should be
>
> static int
> cpuConvertModelVendorFromPVR(char ***model,
> char ***vendor,
> uint32_t pvr);
>
> or something like that.
Got it, I will modify it in next version.
Thanks.
>
>> +{
>> + int i = 0;
>> + while (cpu_defs[i].name != NULL) {
>> + if (cpu_defs[i].pvr == pvr) {
>> + **model = strdup(cpu_defs[i].name);
>> + **vendor = strdup(cpu_defs[i].vendor);
>> + return 0;
>> + }
>> + i ++;
>> + }
>
> Yeah, we've used 'while' in this way many times. But for() loop - while
> being totally exchangeable in this case is better.
Thanks, I will modify it.
>
>> +
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + _("Missing the definition of this model"));
>
> s/VIR_ERR_INTERNAL_ERROR,/VIR_ERR_INTERNAL_ERROR, "%s"/
Sorry for my mistake, I will fix this.
>
>> + return -1;
>> +}
>> +
>> +static int ConvertPVRFromModel(const char *model, uint32_t *pvr)
>> +{
>> + int i = 0;
>> + while (cpu_defs[i].name !=NULL) {
>> + if (STREQ(cpu_defs[i].name, model)) {
>> + *pvr = cpu_defs[i].pvr;
>> + return 0;
>> + }
>> + i ++;
>> + }
>> +
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + _("Missing the definition of this model"));
>
> ditto
>
>> + return -1;
>> +}
>
> Same applies for this function.
I see.
>
>> +
>> +static int cpuMatch(const union cpuData *data, char **cpu_model,
>> + char **cpu_vendor, const struct ppcModel *model)
>> +{
>> + int ret = 0;
>> +
>> + ret = ConvertModelVendorFromPVR(&cpu_model, &cpu_vendor, data->ppc.pvr);
>> +
>> + if (STREQ(model->name, *cpu_model) &&
>> + STREQ(model->vendor->name, *cpu_vendor))
>> + ret = 1;
>> +
>> + return ret;
>> +}
>> +
>> +
>> +static struct ppcModel *
>> +ppcModelNew(void)
>> +{
>> + struct ppcModel *model;
>> +
>> + if (VIR_ALLOC(model) < 0)
>> + return NULL;
>> +
>> + if (VIR_ALLOC(model->data) < 0) {
>> + VIR_FREE(model);
>> + return NULL;
>> + }
>> +
>> + return model;
>> +}
>> +
>> +static void
>> +ppcModelFree(struct ppcModel *model)
>> +{
>> + if (model == NULL)
>> + return;
>> +
>> + VIR_FREE(model->name);
>> +
>> + if (model->data)
>> + VIR_FREE(model->data);
>
> VIR_FREE(NULL) is just fine. so you don't need to test model->data.
I see, I will remove this assertion.
>
>> +
>> + VIR_FREE(model);
>> +}
>> +
>> +static struct ppcModel *
>> +ppcModelFind(const struct ppcMap *map,
>> + const char *name)
>> +{
>> + struct ppcModel *model;
>> +
>> + model = map->models;
>> + while (model != NULL) {
>> + if (STREQ(model->name, name))
>> + return model;
>> +
>> + model = model->next;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static struct ppcVendor *
>> +ppcVendorFind(const struct ppcMap *map,
>> + const char *name)
>> +{
>> + struct ppcVendor *vendor;
>> +
>> + vendor = map->vendors;
>> + while (vendor) {
>> + if (STREQ(vendor->name, name))
>> + return vendor;
>> +
>> + vendor = vendor->next;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static void
>> +ppcVendorFree(struct ppcVendor *vendor)
>> +{
>> + if (!vendor)
>> + return;
>> +
>> + VIR_FREE(vendor->name);
>> + VIR_FREE(vendor);
>> +}
>> +
>> +static int
>> +ppcVendorLoad(xmlXPathContextPtr ctxt,
>> + struct ppcMap *map)
>> +{
>> + struct ppcVendor *vendor = NULL;
>> + char *string = NULL;
>> + int ret = 0;
>> +
>> + if (VIR_ALLOC(vendor) < 0)
>> + goto no_memory;
>> +
>> + vendor->name = virXPathString("string(@name)", ctxt);
>
> I think attributes are always string, aren't they? But this doesn't hurt.
Yes, it should be string.
I think this way can make sure that this is a string.
>
>> + if (!vendor->name) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + "%s", _("Missing CPU vendor name"));
>> + goto ignore;
>> + }
>> +
>> + if (ppcVendorFind(map, vendor->name)) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + _("CPU vendor %s already defined"), vendor->name);
>> + goto ignore;
>> + }
>> +
>> + string = virXPathString("string(@string)", ctxt);
>> + if (!string) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + _("Missing vendor string for CPU vendor %s"), vendor->name);
>> + goto ignore;
>> + }
>> + if (strlen(string) != VENDOR_STRING_LENGTH) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + _("Invalid CPU vendor string '%s'"), string);
>> + goto ignore;
>> + }
>
> Does PowerPC equivalent of CPUID requires 7 characters at most?
There shouldn't be this limitation.
I will make this clear, and clean this code in next version.
>
>> + if (!map->vendors)
>> + map->vendors = vendor;
>> + else {
>> + vendor->next = map->vendors;
>> + map->vendors = vendor;
>> + }
>> +
>> +out:
>> + VIR_FREE(string);
>> +
>> + return ret;
>> +
>> +no_memory:
>> + virReportOOMError();
>> + ret = -1;
>
> we often initialize ret = -1; and when everything went okay, we set ret
> = 0; in this case that would be just before out: label. However, in this
> case out can be dropped and we can have the only error label. Having
> VIR_FREE(string) twice there is not a problem as it is single line.
>
Got it, thanks for your suggestions. I will clean this.
>> +ignore:
>> + ppcVendorFree(vendor);
>> + goto out;
>> +}
>> +
>> +static int
>> +ppcModelLoad(xmlXPathContextPtr ctxt,
>> + struct ppcMap *map)
>> +{
>> + xmlNodePtr *nodes = NULL;
>> + struct ppcModel *model;
>> + char *vendor = NULL;
>> + int ret = 0;
>> +
>> + if (!(model = ppcModelNew()))
>> + goto no_memory;
>> +
>> + model->name = virXPathString("string(@name)", ctxt);
>> + if (model->name == NULL) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + "%s", _("Missing CPU model name"));
>> + goto ignore;
>> + }
>> +
>> + ret = ConvertPVRFromModel(model->name, &model->data->ppc.pvr);
>> + if (ret < 0)
>> + goto ignore;
>> +
>> +
>> + 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 ignore;
>> + }
>> +
>> + if (!(model->vendor = ppcVendorFind(map, vendor))) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + _("Unknown vendor %s referenced by CPU model %s"),
>> + vendor, model->name);
>> + goto ignore;
>> + }
>> + }
>> +
>> + if (map->models == NULL)
>> + map->models = model;
>> + else {
>> + model->next = map->models;
>> + map->models = model;
>> + }
>> +
>> +out:
>> + VIR_FREE(vendor);
>> + VIR_FREE(nodes);
>> + return ret;
>> +
>> +no_memory:
>> + virReportOOMError();
>> + ret = -1;
>> +
>> +ignore:
>> + ppcModelFree(model);
>> + goto out;
>> +}
>
> see my comments to previous function.
>
Thanks.
>> +
>> +static int
>> +ppcMapLoadCallback(enum cpuMapElement element,
>> + xmlXPathContextPtr ctxt,
>> + void *data)
>> +{
>> + struct ppcMap *map = data;
>> +
>> + switch (element) {
>> + case CPU_MAP_ELEMENT_VENDOR:
>> + return ppcVendorLoad(ctxt, map);
>> + case CPU_MAP_ELEMENT_MODEL:
>> + return ppcModelLoad(ctxt, map);
>> + default:
>> + break;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void
>> +ppcMapFree(struct ppcMap *map)
>> +{
>> + if (map == NULL)
>> + return;
>> +
>> + while (map->models != NULL) {
>> + struct ppcModel *model = map->models;
>> + map->models = model->next;
>> + ppcModelFree(model);
>> + }
>> +
>> + while (map->vendors != NULL) {
>> + struct ppcVendor *vendor = map->vendors;
>> + map->vendors = vendor->next;
>> + ppcVendorFree(vendor);
>> + }
>> +
>> + VIR_FREE(map);
>> +}
>> +
>> +static struct ppcMap *
>> +ppcLoadMap(void)
>> +{
>> + struct ppcMap *map;
>> +
>> + if (VIR_ALLOC(map) < 0) {
>> + virReportOOMError();
>> + return NULL;
>> + }
>> +
>> + if (cpuMapLoad("ppc64", ppcMapLoadCallback, map) < 0)
>> + goto error;
>> +
>> + return map;
>> +
>> +error:
>> + ppcMapFree(map);
>> + return NULL;
>> +}
>> +
>> +static struct ppcModel *
>> +ppcModelCopy(const struct ppcModel *model)
>> +{
>> + struct ppcModel *copy;
>> +
>> + if (VIR_ALLOC(copy) < 0
>> + || VIR_ALLOC(copy->data) < 0
>> + || !(copy->name = strdup(model->name))){
>> + ppcModelFree(copy);
>> + return NULL;
>> + }
>> +
>> + copy->data->ppc.pvr = model->data->ppc.pvr;
>> + copy->vendor = model->vendor;
>> +
>> + return copy;
>> +}
>> +
>> +static struct ppcModel *
>> +ppcModelFromCPU(const virCPUDefPtr cpu,
>> + const struct ppcMap *map)
>> +{
>> + struct ppcModel *model = NULL;
>> +
>> + if ((model = ppcModelFind(map, cpu->model))) {
>> + if ((model = ppcModelCopy(model)) == NULL) {
>> + goto no_memory;
>> + }
>> + } else if (!(model = ppcModelNew())) {
>> + goto no_memory;
>> + }
>> +
>> + return model;
>> +
>> +no_memory:
>> + virReportOOMError();
>> + ppcModelFree(model);
>> +
>> + return NULL;
>> +}
>> +
>> +static virCPUCompareResult
>> +PowerPCCompare(virCPUDefPtr host,
>
> whitespace at the EOL
OK.
>
>> + virCPUDefPtr cpu)
>> +{
>> + if ((cpu->arch && STRNEQ(host->arch, cpu->arch)) ||
>> + STRNEQ(host->model, cpu->model))
>> + return VIR_CPU_COMPARE_INCOMPATIBLE;
>> +
>> + return VIR_CPU_COMPARE_IDENTICAL;
>> +}
>> +
>> +static int
>> +PowerPCDecode(virCPUDefPtr cpu,
>> + const union cpuData *data,
>> + const char **models,
>> + unsigned int nmodels,
>> + const char *preferred)
>> +{
>> + int ret = -1;
>> + struct ppcMap *map;
>> + const struct ppcModel *candidate;
>> + virCPUDefPtr cpuCandidate;
>> + virCPUDefPtr cpuModel = NULL;
>> + char *cpu_vendor = NULL;
>> + char *cpu_model = NULL;
>> + unsigned int i;
>> +
>> + if (data == NULL || (map = ppcLoadMap()) == NULL)
>> + return -1;
>> +
>> + candidate = map->models;
>> +
>> + while (candidate != NULL) {
>> + bool allowed = (models == NULL);
>> +
>> + for (i = 0; i < nmodels; i++) {
>> + if (models && models[i] && STREQ(models[i], candidate->name)) {
>> + allowed = true;
>> + break;
>> + }
>> + }
>> +
>> + if (!allowed) {
>> + if (preferred && STREQ(candidate->name, preferred)) {
>> + if (cpu->fallback != VIR_CPU_FALLBACK_ALLOW) {
>> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
>> + _("CPU model %s is not supported by hypervisor"),
>> + preferred);
>> + goto out;
>> + } else {
>> + VIR_WARN("Preferred CPU model %s not allowed by"
>> + " hypervisor; closest supported model will be"
>> + " used", preferred);
>> + }
>> + } else {
>> + VIR_DEBUG("CPU model %s not allowed by hypervisor; ignoring",
>> + candidate->name);
>> + }
>> + goto next;
>> + }
>> +
>> + if (VIR_ALLOC(cpuCandidate) < 0) {
>> + virReportOOMError();
>> + goto out;
>> + }
>> +
>> + cpuCandidate->model = strdup(candidate->name);
>> + cpuCandidate->vendor = strdup(candidate->vendor->name);
>> +
>> + if (preferred && STREQ(cpuCandidate->model, preferred)) {
>> + virCPUDefFree(cpuModel);
>> + cpuModel = cpuCandidate;
>> + 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;
>> + virCPUDefFree(cpuModel);
>> + cpuModel = cpuCandidate;
>> + break;
>> + }
>> +
>> + virCPUDefFree(cpuCandidate);
>> +
>> + next:
>> + candidate = candidate->next;
>> + }
>> +
>> + if (cpuModel == NULL) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + "%s", _("Cannot find suitable CPU model for given data"));
>> + goto out;
>> + }
>> +
>> + cpu->model = cpuModel->model;
>> + cpu->vendor = cpuModel->vendor;
>> + VIR_FREE(cpuModel);
>> +
>> + ret = 0;
>> +
>> +out:
>> + ppcMapFree(map);
>> + virCPUDefFree(cpuModel);
>> +
>> + return ret;
>> +}
>> +
>> +static uint32_t ppc_mfpvr(void)
>> +{
>> + uint32_t pvr;
>> + asm ("mfpvr %0"
>> + : "=r"(pvr));
>> + return pvr;
>> +}
>
> There is no such instruction on x86. So we need to make this function
> ppc only and introduce stub for other architectures.
OK, I think this driver is for ppc64.
For other architectures, it shouldn't be called.
>
>> +
>> +static void
>> +PowerPCDataFree(union cpuData *data)
>> +{
>> + if (data == NULL)
>> + return;
>> +
>> + VIR_FREE(data);
>> +}
>> +
>> static union cpuData *
>> PowerPCNodeData(void)
>> {
>> @@ -42,40 +550,85 @@ PowerPCNodeData(void)
>> return NULL;
>> }
>>
>> + data->ppc.pvr = ppc_mfpvr();
>> +
>> return data;
>> }
>>
>> -
>> static int
>> -PowerPCDecode(virCPUDefPtr cpu ATTRIBUTE_UNUSED,
>> - const union cpuData *data ATTRIBUTE_UNUSED,
>> - const char **models ATTRIBUTE_UNUSED,
>> - unsigned int nmodels ATTRIBUTE_UNUSED,
>> - const char *preferred ATTRIBUTE_UNUSED)
>> +PowerPCUpdate(virCPUDefPtr guest ATTRIBUTE_UNUSED,
>> + const virCPUDefPtr host ATTRIBUTE_UNUSED)
>> {
>> - return 0;
>> + return 0;
>> }
>> -
>> -static void
>> -PowerPCDataFree(union cpuData *data)
>> +static virCPUDefPtr
>> +PowerPCBaseline(virCPUDefPtr *cpus,
>> + unsigned int ncpus ATTRIBUTE_UNUSED,
>> + const char **models ATTRIBUTE_UNUSED,
>> + unsigned int nmodels ATTRIBUTE_UNUSED)
>> {
>> - if (data == NULL)
>> - return;
>> + struct ppcMap *map = NULL;
>> + struct ppcModel *base_model = NULL;
>> + virCPUDefPtr cpu = NULL;
>> + struct ppcModel *model = NULL;
>> + bool outputModel = true;
>> +
>> + if (!(map = ppcLoadMap())) {
>> + goto error;
>> + }
>> +
>> + if (!(base_model = ppcModelFromCPU(cpus[0], map))) {
>> + goto error;
>> + }
>> +
>> + if (VIR_ALLOC(cpu) < 0 ||
>> + !(cpu->arch = strdup(cpus[0]->arch)))
>> + goto no_memory;
>> + cpu->type = VIR_CPU_TYPE_GUEST;
>> + cpu->match = VIR_CPU_MATCH_EXACT;
>> +
>> + if (!cpus[0]->model) {
>> + outputModel = false;
>> + } else if (!(model = ppcModelFind(map, cpus[0]->model))) {
>> + virReportError(VIR_ERR_OPERATION_FAILED,
>> + _("Unknown CPU vendor %s"), cpus[0]->model);
>> + goto error;
>> + }
>> +
>> + base_model->data->ppc.pvr = model->data->ppc.pvr;
>> + if (PowerPCDecode(cpu, base_model->data, models, nmodels, NULL) < 0)
>> + goto error;
>> +
>> + if (!outputModel)
>> + VIR_FREE(cpu->model);
>> +
>> + VIR_FREE(cpu->arch);
>> +
>> +cleanup:
>> + ppcModelFree(base_model);
>> + ppcMapFree(map);
>>
>> - VIR_FREE(data);
>> + return cpu;
>> +no_memory:
>> + virReportOOMError();
>> +error:
>> + ppcModelFree(model);
>> + virCPUDefFree(cpu);
>> + cpu = NULL;
>> + goto cleanup;
>> }
>>
>> struct cpuArchDriver cpuDriverPowerPC = {
>> .name = "ppc64",
>> .arch = archs,
>> .narch = ARRAY_CARDINALITY(archs),
>> - .compare = NULL,
>> + .compare = PowerPCCompare,
>> .decode = PowerPCDecode,
>> .encode = NULL,
>> .free = PowerPCDataFree,
>> .nodeData = PowerPCNodeData,
>> .guestData = NULL,
>> - .baseline = NULL,
>> - .update = NULL,
>> + .baseline = PowerPCBaseline,
>> + .update = PowerPCUpdate,
>> .hasFeature = NULL,
>> };
>>
>
>
> Moreover, you need to add src/cpu/cpu_powerpc.c into po/POTFILES.in
>
> make syntax-check should have warned you about this.
Got it, I will add it in next version.
Thanks for your great comments.
>
> Michal
--
Best Regards
-Li
More information about the libvir-list
mailing list