[libvirt] [PATCH 1/3] cpuUpdate() for updating guest CPU according to host CPU

Jiri Denemark jdenemar at redhat.com
Fri Mar 26 13:11:34 UTC 2010


Useful mainly for migration. cpuUpdate changes guest CPU requirements in
the following way:

- match == "strict" || match == "exact"
    - optional features which are supported by host CPU are changed into
      required features
    - optional features which are not supported by host CPU are disabled
    - all other features remain untouched
- match == "minimum"
    - match is changed into "exact"
    - optional features and all features not mentioned in guest CPU
      specification which are supported by host CPU become required
      features
    - other optional features are disabled
    - oll other features remain untouched

This ensures that no feature will suddenly disappear from the guest
after migration.
---
 src/cpu/cpu.c            |   22 ++++++
 src/cpu/cpu.h            |    9 +++
 src/cpu/cpu_generic.c    |    1 +
 src/cpu/cpu_x86.c        |  161 +++++++++++++++++++++++++++++++++++++++++-----
 src/libvirt_private.syms |    1 +
 5 files changed, 177 insertions(+), 17 deletions(-)

diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c
index 183862a..4a1588d 100644
--- a/src/cpu/cpu.c
+++ b/src/cpu/cpu.c
@@ -403,3 +403,25 @@ cpuBaseline(virCPUDefPtr *cpus,
 
     return cpu;
 }
+
+
+int
+cpuUpdate(virCPUDefPtr guest,
+          const virCPUDefPtr host)
+{
+    struct cpuArchDriver *driver;
+
+    VIR_DEBUG("guest=%p, host=%p", guest, host);
+
+    if ((driver = cpuGetSubDriver(host->arch)) == NULL)
+        return -1;
+
+    if (driver->update == NULL) {
+        virCPUReportError(VIR_ERR_NO_SUPPORT,
+                _("cannot update guest CPU data for %s architecture"),
+                host->arch);
+        return -1;
+    }
+
+    return driver->update(guest, host);
+}
diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h
index a2d79fe..1494a7f 100644
--- a/src/cpu/cpu.h
+++ b/src/cpu/cpu.h
@@ -76,6 +76,10 @@ typedef virCPUDefPtr
                      const char **models,
                      unsigned int nmodels);
 
+typedef int
+(*cpuArchUpdate)    (virCPUDefPtr guest,
+                     const virCPUDefPtr host);
+
 
 struct cpuArchDriver {
     const char *name;
@@ -88,6 +92,7 @@ struct cpuArchDriver {
     cpuArchNodeData     nodeData;
     cpuArchGuestData    guestData;
     cpuArchBaseline     baseline;
+    cpuArchUpdate       update;
 };
 
 
@@ -138,4 +143,8 @@ cpuBaseline (virCPUDefPtr *cpus,
              const char **models,
              unsigned int nmodels);
 
+extern int
+cpuUpdate   (virCPUDefPtr guest,
+             const virCPUDefPtr host);
+
 #endif /* __VIR_CPU_H__ */
diff --git a/src/cpu/cpu_generic.c b/src/cpu/cpu_generic.c
index 113af2f..a39f262 100644
--- a/src/cpu/cpu_generic.c
+++ b/src/cpu/cpu_generic.c
@@ -219,4 +219,5 @@ struct cpuArchDriver cpuDriverGeneric = {
     .nodeData   = NULL,
     .guestData  = NULL,
     .baseline   = genericBaseline,
+    .update     = NULL,
 };
diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c
index 4bb556c..e49f7c0 100644
--- a/src/cpu/cpu_x86.c
+++ b/src/cpu/cpu_x86.c
@@ -249,6 +249,33 @@ x86DataFromModel(const struct x86_model *model)
 }
 
 
+/* also removes all detected features from data */
+static int
+x86DataToCPUFeatures(virCPUDefPtr cpu,
+                     int policy,
+                     union cpuData *data,
+                     const struct x86_map *map)
+{
+    const struct x86_feature *feature = map->features;
+    struct cpuX86cpuid *cpuid;
+    unsigned int i;
+
+    while (feature != NULL) {
+        for (i = 0; i < feature->ncpuid; i++) {
+            if ((cpuid = x86DataCpuid(data, feature->cpuid[i].function))
+                && x86cpuidMatchMasked(cpuid, feature->cpuid + i)) {
+                x86cpuidClearBits(cpuid, feature->cpuid + i);
+                if (virCPUDefAddFeature(cpu, feature->name, policy) < 0)
+                    return -1;
+            }
+        }
+        feature = feature->next;
+    }
+
+    return 0;
+}
+
+
 static virCPUDefPtr
 x86DataToCPU(const union cpuData *data,
              const struct x86_model *model,
@@ -256,9 +283,7 @@ x86DataToCPU(const union cpuData *data,
 {
     virCPUDefPtr cpu;
     union cpuData *tmp = NULL;
-    struct cpuX86cpuid *cpuid;
-    const struct x86_feature *feature;
-    int i;
+    unsigned int i;
 
     if (VIR_ALLOC(cpu) < 0 ||
         (cpu->model = strdup(model->name)) == NULL ||
@@ -270,20 +295,8 @@ x86DataToCPU(const union cpuData *data,
                           model->cpuid + i);
     }
 
-    feature = map->features;
-    while (feature != NULL) {
-        for (i = 0; i < feature->ncpuid; i++) {
-            if ((cpuid = x86DataCpuid(tmp, feature->cpuid[i].function))
-                && x86cpuidMatchMasked(cpuid, feature->cpuid + i)) {
-                x86cpuidClearBits(cpuid, feature->cpuid + i);
-                if (virCPUDefAddFeature(cpu, feature->name,
-                                        VIR_CPU_FEATURE_REQUIRE) < 0)
-                    goto error;
-            }
-        }
-
-        feature = feature->next;
-    }
+    if (x86DataToCPUFeatures(cpu, VIR_CPU_FEATURE_REQUIRE, tmp, map))
+        goto error;
 
 cleanup:
     x86DataFree(tmp);
@@ -560,6 +573,29 @@ x86ModelMergeFeature(struct x86_model *model,
 }
 
 
+static bool
+x86ModelHasFeature(struct x86_model *model,
+                   const struct x86_feature *feature)
+{
+    unsigned int i;
+    struct cpuX86cpuid *cpuid;
+    struct cpuX86cpuid *model_cpuid;
+
+    if (feature == NULL)
+        return false;
+
+    for (i = 0; i < feature->ncpuid; i++) {
+        cpuid = feature->cpuid + i;
+        model_cpuid = x86cpuidFind(model->cpuid, model->ncpuid,
+                                   cpuid->function);
+        if (!x86cpuidMatchMasked(model_cpuid, cpuid))
+            return false;
+    }
+
+    return true;
+}
+
+
 static struct x86_model *
 x86ModelFromCPU(const virCPUDefPtr cpu,
                 const struct x86_map *map,
@@ -610,6 +646,47 @@ error:
 }
 
 
+static int
+x86ModelSubtractCPU(struct x86_model *model,
+                    const virCPUDefPtr cpu,
+                    const struct x86_map *map)
+{
+    const struct x86_model *cpu_model;
+    unsigned int i;
+
+    if (!(cpu_model = x86ModelFind(map, cpu->model))) {
+        virCPUReportError(VIR_ERR_INTERNAL_ERROR,
+                          _("Unknown CPU model %s"),
+                          cpu->model);
+        return -1;
+    }
+
+    x86ModelSubtract(model, cpu_model);
+
+    for (i = 0; i < cpu->nfeatures; i++) {
+        const struct x86_feature *feature;
+        unsigned int j;
+
+        if (!(feature = x86FeatureFind(map, cpu->features[i].name))) {
+            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
+                              _("Unknown CPU feature %s"),
+                              cpu->features[i].name);
+            return -1;
+        }
+
+        for (j = 0; j < feature->ncpuid; j++) {
+            struct cpuX86cpuid *cpuid;
+            cpuid = x86cpuidFind(model->cpuid, model->ncpuid,
+                                 feature->cpuid[j].function);
+            if (cpuid)
+                x86cpuidClearBits(cpuid, feature->cpuid + j);
+        }
+    }
+
+    return 0;
+}
+
+
 static enum compare_result
 x86ModelCompare(const struct x86_model *model1,
                 const struct x86_model *model2)
@@ -1277,6 +1354,55 @@ error:
 }
 
 
+static int
+x86Update(virCPUDefPtr guest,
+          const virCPUDefPtr host)
+{
+    int ret = -1;
+    unsigned int i;
+    struct x86_map *map;
+    struct x86_model *host_model = NULL;
+    union cpuData *data = NULL;
+
+    if (!(map = x86LoadMap()) ||
+        !(host_model = x86ModelFromCPU(host, map, 0)))
+        goto cleanup;
+
+    for (i = 0; i < guest->nfeatures; i++) {
+        if (guest->features[i].policy == VIR_CPU_FEATURE_OPTIONAL) {
+            const struct x86_feature *feature;
+            if (!(feature = x86FeatureFind(map, guest->features[i].name))) {
+                virCPUReportError(VIR_ERR_INTERNAL_ERROR,
+                                  _("Unknown CPU feature %s"),
+                                  guest->features[i].name);
+                goto cleanup;
+            }
+
+            if (x86ModelHasFeature(host_model, feature))
+                guest->features[i].policy = VIR_CPU_FEATURE_REQUIRE;
+            else
+                guest->features[i].policy = VIR_CPU_FEATURE_DISABLE;
+        }
+    }
+
+    if (guest->match == VIR_CPU_MATCH_MINIMUM) {
+        guest->match = VIR_CPU_MATCH_EXACT;
+        if (x86ModelSubtractCPU(host_model, guest, map)
+            || !(data = x86DataFromModel(host_model))
+            || x86DataToCPUFeatures(guest, VIR_CPU_FEATURE_REQUIRE, data, map))
+            goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    x86MapFree(map);
+    x86ModelFree(host_model);
+    x86DataFree(data);
+    return ret;
+}
+
+
 struct cpuArchDriver cpuDriverX86 = {
     .name = "x86",
     .arch = archs,
@@ -1292,4 +1418,5 @@ struct cpuArchDriver cpuDriverX86 = {
 #endif
     .guestData  = x86GuestData,
     .baseline   = x86Baseline,
+    .update     = x86Update,
 };
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index f1ad6db..9c16a6d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -81,6 +81,7 @@ cpuDecode;
 cpuEncode;
 cpuGuestData;
 cpuNodeData;
+cpuUpdate;
 
 
 # cpu_conf.h
-- 
1.7.0.3




More information about the libvir-list mailing list