[libvirt] [PATCH v1 2/3] libvirt: Implement CPU model driver for PowerPC

Li Zhang zhlcindy at gmail.com
Tue Oct 9 07:58:57 UTC 2012


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)
+{
+    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 ++;
+    }
+
+    virReportError(VIR_ERR_INTERNAL_ERROR,
+                   _("Missing the definition of this model"));
+    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"));
+    return -1;
+}
+
+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(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);
+    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;
+    }
+    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;
+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;
+}
+
+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, 
+           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;
+}
+
+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,
 };
-- 
1.7.7.6




More information about the libvir-list mailing list