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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list