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