cpu: Rework cpuUpdate

The reworked API is now called virCPUUpdate and it should change the
provided CPU definition into a one which can be consumed by the QEMU
command line builder:

    - host-passthrough remains unchanged
    - host-model is turned into custom CPU with a model and features
      copied from host
    - custom CPU with minimum match is converted similarly to host-model
    - optional features are updated according to host's CPU

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
This commit is contained in:
Jiri Denemark 2016-06-23 15:27:07 +02:00
parent dc4542dea5
commit 3b6be3c0c5
19 changed files with 228 additions and 246 deletions

View File

@ -43,6 +43,7 @@ src/conf/virchrdev.c
src/conf/virdomainobjlist.c
src/conf/virsecretobj.c
src/cpu/cpu.c
src/cpu/cpu_arm.c
src/cpu/cpu_map.c
src/cpu/cpu_ppc64.c
src/cpu/cpu_x86.c

View File

@ -579,38 +579,71 @@ cpuBaseline(virCPUDefPtr *cpus,
/**
* cpuUpdate:
* virCPUUpdate:
*
* @guest: guest CPU definition
* @arch: CPU architecture
* @guest: guest CPU definition to be updated
* @host: host CPU definition
*
* Updates @guest CPU definition according to @host CPU. This is required to
* support guest CPU definition which are relative to host CPU, such as CPUs
* with VIR_CPU_MODE_CUSTOM and optional features or VIR_CPU_MATCH_MINIMUM, or
* CPUs with non-custom mode (VIR_CPU_MODE_HOST_MODEL,
* VIR_CPU_MODE_HOST_PASSTHROUGH).
* support guest CPU definitions specified relatively to host CPU, such as
* CPUs with VIR_CPU_MODE_CUSTOM and optional features or
* VIR_CPU_MATCH_MINIMUM, or CPUs with VIR_CPU_MODE_HOST_MODEL.
* When the guest CPU was not specified relatively, the function does nothing
* and returns success.
*
* Returns 0 on success, -1 on error.
*/
int
cpuUpdate(virCPUDefPtr guest,
const virCPUDef *host)
virCPUUpdate(virArch arch,
virCPUDefPtr guest,
const virCPUDef *host)
{
struct cpuArchDriver *driver;
VIR_DEBUG("guest=%p, host=%p", guest, host);
VIR_DEBUG("arch=%s, guest=%p mode=%s model=%s, host=%p model=%s",
virArchToString(arch), guest, virCPUModeTypeToString(guest->mode),
NULLSTR(guest->model), host, NULLSTR(host ? host->model : NULL));
if ((driver = cpuGetSubDriver(host->arch)) == NULL)
if (!(driver = cpuGetSubDriver(arch)))
return -1;
if (driver->update == NULL) {
if (guest->mode == VIR_CPU_MODE_HOST_PASSTHROUGH)
return 0;
if (guest->mode == VIR_CPU_MODE_CUSTOM &&
guest->match != VIR_CPU_MATCH_MINIMUM) {
size_t i;
bool optional = false;
for (i = 0; i < guest->nfeatures; i++) {
if (guest->features[i].policy == VIR_CPU_FEATURE_OPTIONAL) {
optional = true;
break;
}
}
if (!optional)
return 0;
}
/* We get here if guest CPU is either
* - host-model
* - custom with minimum match
* - custom with optional features
*/
if (!driver->update) {
virReportError(VIR_ERR_NO_SUPPORT,
_("cannot update guest CPU data for %s architecture"),
virArchToString(host->arch));
_("cannot update guest CPU for %s architecture"),
virArchToString(arch));
return -1;
}
return driver->update(guest, host);
if (driver->update(guest, host) < 0)
return -1;
VIR_DEBUG("model=%s", NULLSTR(guest->model));
return 0;
}

View File

@ -87,8 +87,8 @@ typedef virCPUDefPtr
unsigned int flags);
typedef int
(*cpuArchUpdate) (virCPUDefPtr guest,
const virCPUDef *host);
(*virCPUArchUpdate)(virCPUDefPtr guest,
const virCPUDef *host);
typedef int
(*cpuArchHasFeature) (const virCPUData *data,
@ -114,7 +114,7 @@ struct cpuArchDriver {
cpuArchNodeData nodeData;
cpuArchGuestData guestData;
cpuArchBaseline baseline;
cpuArchUpdate update;
virCPUArchUpdate update;
cpuArchHasFeature hasFeature;
cpuArchDataFormat dataFormat;
cpuArchDataParse dataParse;
@ -182,9 +182,10 @@ cpuBaseline (virCPUDefPtr *cpus,
ATTRIBUTE_NONNULL(1);
int
cpuUpdate (virCPUDefPtr guest,
virCPUUpdate(virArch arch,
virCPUDefPtr guest,
const virCPUDef *host)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
ATTRIBUTE_NONNULL(2);
int
cpuHasFeature(const virCPUData *data,

View File

@ -43,15 +43,41 @@ armDataFree(virCPUDataPtr data)
VIR_FREE(data);
}
static int
armUpdate(virCPUDefPtr guest,
const virCPUDef *host)
virCPUarmUpdate(virCPUDefPtr guest,
const virCPUDef *host)
{
int ret = -1;
virCPUDefPtr updated = NULL;
if (guest->mode != VIR_CPU_MODE_HOST_MODEL)
return 0;
if (!host) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unknown host CPU model"));
goto cleanup;
}
if (!(updated = virCPUDefCopyWithoutModel(guest)))
goto cleanup;
updated->mode = VIR_CPU_MODE_CUSTOM;
if (virCPUDefCopyModel(updated, host, true) < 0)
goto cleanup;
virCPUDefStealModel(guest, updated);
guest->mode = VIR_CPU_MODE_CUSTOM;
guest->match = VIR_CPU_MATCH_EXACT;
virCPUDefFreeModel(guest);
return virCPUDefCopyModel(guest, host, true);
ret = 0;
cleanup:
virCPUDefFree(updated);
return ret;
}
static virCPUCompareResult
armGuestData(virCPUDefPtr host ATTRIBUTE_UNUSED,
virCPUDefPtr guest ATTRIBUTE_UNUSED,
@ -104,6 +130,6 @@ struct cpuArchDriver cpuDriverArm = {
.nodeData = NULL,
.guestData = armGuestData,
.baseline = armBaseline,
.update = armUpdate,
.update = virCPUarmUpdate,
.hasFeature = NULL,
};

View File

@ -751,27 +751,21 @@ ppc64DriverGuestData(virCPUDefPtr host,
}
static int
ppc64DriverUpdate(virCPUDefPtr guest,
const virCPUDef *host)
virCPUppc64Update(virCPUDefPtr guest,
const virCPUDef *host ATTRIBUTE_UNUSED)
{
switch ((virCPUMode) guest->mode) {
case VIR_CPU_MODE_HOST_PASSTHROUGH:
/*
* - host-passthrough doesn't even get here
* - host-model is used for host CPU running in a compatibility mode and
* it needs to remain unchanged
* - custom doesn't support any optional features, there's nothing to
* update
*/
if (guest->mode == VIR_CPU_MODE_CUSTOM)
guest->match = VIR_CPU_MATCH_EXACT;
guest->fallback = VIR_CPU_FALLBACK_FORBID;
virCPUDefFreeModel(guest);
return virCPUDefCopyModel(guest, host, true);
case VIR_CPU_MODE_HOST_MODEL:
case VIR_CPU_MODE_CUSTOM:
return 0;
case VIR_CPU_MODE_LAST:
break;
}
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected CPU mode: %d"), guest->mode);
return -1;
return 0;
}
static virCPUDefPtr
@ -915,7 +909,7 @@ struct cpuArchDriver cpuDriverPPC64 = {
.nodeData = ppc64DriverNodeData,
.guestData = ppc64DriverGuestData,
.baseline = ppc64DriverBaseline,
.update = ppc64DriverUpdate,
.update = virCPUppc64Update,
.hasFeature = NULL,
.getModels = ppc64DriverGetModels,
};

View File

@ -1122,40 +1122,6 @@ x86ModelFromCPU(const virCPUDef *cpu,
}
static int
x86ModelSubtractCPU(virCPUx86ModelPtr model,
const virCPUDef *cpu,
virCPUx86MapPtr map)
{
virCPUx86ModelPtr cpu_model;
size_t i;
if (!(cpu_model = x86ModelFind(map, cpu->model))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown CPU model %s"),
cpu->model);
return -1;
}
x86DataSubtract(&model->data, &cpu_model->data);
for (i = 0; i < cpu->nfeatures; i++) {
virCPUx86FeaturePtr feature;
if (!(feature = x86FeatureFind(map, cpu->features[i].name))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown CPU feature %s"),
cpu->features[i].name);
return -1;
}
x86DataSubtract(&model->data, &feature->data);
}
return 0;
}
static virCPUx86CompareResult
x86ModelCompare(virCPUx86ModelPtr model1,
virCPUx86ModelPtr model2)
@ -2533,23 +2499,85 @@ x86Baseline(virCPUDefPtr *cpus,
static int
x86UpdateCustom(virCPUDefPtr guest,
x86UpdateHostModel(virCPUDefPtr guest,
const virCPUDef *host,
virCPUx86MapPtr map)
{
virCPUDefPtr updated = NULL;
size_t i;
int ret = -1;
if (!(updated = virCPUDefCopyWithoutModel(host)))
goto cleanup;
/* Remove non-migratable features by default */
updated->type = VIR_CPU_TYPE_GUEST;
updated->mode = VIR_CPU_MODE_CUSTOM;
if (virCPUDefCopyModel(updated, host, true) < 0)
goto cleanup;
i = 0;
while (i < updated->nfeatures) {
if (x86FeatureIsMigratable(updated->features[i].name, map) &&
STRNEQ(updated->features[i].name, "cmt") &&
STRNEQ(updated->features[i].name, "mbm_total") &&
STRNEQ(updated->features[i].name, "mbm_local")) {
i++;
} else {
VIR_FREE(updated->features[i].name);
VIR_DELETE_ELEMENT_INPLACE(updated->features, i, updated->nfeatures);
}
}
if (guest->vendor_id) {
VIR_FREE(updated->vendor_id);
if (VIR_STRDUP(updated->vendor_id, guest->vendor_id) < 0)
goto cleanup;
}
for (i = 0; i < guest->nfeatures; i++) {
if (virCPUDefUpdateFeature(updated,
guest->features[i].name,
guest->features[i].policy) < 0)
goto cleanup;
}
virCPUDefStealModel(guest, updated);
guest->mode = VIR_CPU_MODE_CUSTOM;
guest->match = VIR_CPU_MATCH_EXACT;
ret = 0;
cleanup:
virCPUDefFree(updated);
return ret;
}
static int
virCPUx86Update(virCPUDefPtr guest,
const virCPUDef *host)
{
virCPUx86ModelPtr model = NULL;
virCPUx86MapPtr map;
int ret = -1;
size_t i;
virCPUx86MapPtr map;
virCPUx86ModelPtr host_model = NULL;
if (!(map = virCPUx86GetMap()) ||
!(host_model = x86ModelFromCPU(host, map, VIR_CPU_FEATURE_REQUIRE)))
if (!host) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unknown host CPU model"));
return -1;
}
if (!(map = virCPUx86GetMap()))
return -1;
if (!(model = x86ModelFromCPU(host, map, -1)))
goto cleanup;
for (i = 0; i < guest->nfeatures; i++) {
if (guest->features[i].policy == VIR_CPU_FEATURE_OPTIONAL) {
int supported = x86FeatureInData(guest->features[i].name,
&host_model->data, map);
&model->data, map);
if (supported < 0)
goto cleanup;
else if (supported)
@ -2559,106 +2587,18 @@ x86UpdateCustom(virCPUDefPtr guest,
}
}
if (guest->match == VIR_CPU_MATCH_MINIMUM) {
guest->match = VIR_CPU_MATCH_EXACT;
if (x86ModelSubtractCPU(host_model, guest, map) ||
x86DataToCPUFeatures(guest, VIR_CPU_FEATURE_REQUIRE,
&host_model->data, map))
goto cleanup;
}
ret = 0;
if (guest->mode == VIR_CPU_MODE_HOST_MODEL ||
guest->match == VIR_CPU_MATCH_MINIMUM)
ret = x86UpdateHostModel(guest, host, map);
else
ret = 0;
cleanup:
x86ModelFree(host_model);
x86ModelFree(model);
return ret;
}
static int
x86UpdateHostModel(virCPUDefPtr guest,
const virCPUDef *host,
bool passthrough)
{
virCPUDefPtr oldguest = NULL;
virCPUx86MapPtr map;
size_t i;
int ret = -1;
if (!(map = virCPUx86GetMap()))
goto cleanup;
/* update the host model according to the desired configuration */
if (!(oldguest = virCPUDefCopy(guest)))
goto cleanup;
virCPUDefFreeModel(guest);
if (virCPUDefCopyModel(guest, host, true) < 0)
goto cleanup;
if (oldguest->vendor_id) {
VIR_FREE(guest->vendor_id);
if (VIR_STRDUP(guest->vendor_id, oldguest->vendor_id) < 0)
goto cleanup;
}
/* Remove non-migratable features and CMT related features which QEMU
* knows nothing about.
* Note: this only works as long as no CPU model contains non-migratable
* features directly */
i = 0;
while (i < guest->nfeatures) {
if (x86FeatureIsMigratable(guest->features[i].name, map) &&
STRNEQ(guest->features[i].name, "cmt") &&
STRNEQ(guest->features[i].name, "mbm_total") &&
STRNEQ(guest->features[i].name, "mbm_local")) {
i++;
} else {
VIR_FREE(guest->features[i].name);
VIR_DELETE_ELEMENT_INPLACE(guest->features, i, guest->nfeatures);
}
}
for (i = 0; !passthrough && i < oldguest->nfeatures; i++) {
if (virCPUDefUpdateFeature(guest,
oldguest->features[i].name,
oldguest->features[i].policy) < 0)
goto cleanup;
}
ret = 0;
cleanup:
virCPUDefFree(oldguest);
return ret;
}
static int
x86Update(virCPUDefPtr guest,
const virCPUDef *host)
{
switch ((virCPUMode) guest->mode) {
case VIR_CPU_MODE_CUSTOM:
return x86UpdateCustom(guest, host);
case VIR_CPU_MODE_HOST_MODEL:
guest->match = VIR_CPU_MATCH_EXACT;
return x86UpdateHostModel(guest, host, false);
case VIR_CPU_MODE_HOST_PASSTHROUGH:
guest->match = VIR_CPU_MATCH_MINIMUM;
return x86UpdateHostModel(guest, host, true);
case VIR_CPU_MODE_LAST:
break;
}
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected CPU mode: %d"), guest->mode);
return -1;
}
static int
x86HasFeature(const virCPUData *data,
const char *name)
@ -2716,7 +2656,7 @@ struct cpuArchDriver cpuDriverX86 = {
#endif
.guestData = x86GuestData,
.baseline = x86Baseline,
.update = x86Update,
.update = virCPUx86Update,
.hasFeature = x86HasFeature,
.dataFormat = x86CPUDataFormat,
.dataParse = x86CPUDataParse,

View File

@ -981,7 +981,7 @@ cpuGetModels;
cpuGuestData;
cpuHasFeature;
cpuNodeData;
cpuUpdate;
virCPUUpdate;
# cpu/cpu_x86.h

View File

@ -6568,7 +6568,7 @@ qemuBuildCpuModelArgStr(virQEMUDriverPtr driver,
if (cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
!migrating &&
cpuUpdate(cpu, host) < 0)
virCPUUpdate(def->os.arch, cpu, host) < 0)
goto cleanup;
if (compareAgainstHost &&

View File

@ -3407,7 +3407,7 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver,
goto cleanup;
}
if (cpuUpdate(def->cpu, caps->host.cpu) < 0)
if (virCPUUpdate(def->os.arch, def->cpu, caps->host.cpu) < 0)
goto cleanup;
}

View File

@ -4522,7 +4522,7 @@ qemuProcessStartValidateGuestCPU(virDomainObjPtr vm,
if (cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
flags & VIR_QEMU_PROCESS_START_NEW &&
cpuUpdate(cpu, host) < 0)
virCPUUpdate(vm->def->os.arch, cpu, host) < 0)
goto cleanup;
cmp = cpuGuestData(host, cpu, &data, &compare_msg);

View File

@ -398,7 +398,7 @@ cpuTestUpdate(const void *arg)
!(cpu = cpuTestLoadXML(data->arch, data->name)))
goto cleanup;
if (cpuUpdate(cpu, host) < 0)
if (virCPUUpdate(host->arch, cpu, host) < 0)
goto cleanup;
if (virAsprintf(&result, "%s+%s", data->host, data->name) < 0)
@ -622,11 +622,14 @@ mymain(void)
host "/" cpu " (" #result ")", \
host, cpu, NULL, 0, NULL, 0, result)
#define DO_TEST_UPDATE_ONLY(arch, host, cpu) \
DO_TEST(arch, cpuTestUpdate, \
cpu " on " host, \
host, cpu, NULL, 0, NULL, 0, 0) \
#define DO_TEST_UPDATE(arch, host, cpu, result) \
do { \
DO_TEST(arch, cpuTestUpdate, \
cpu " on " host, \
host, cpu, NULL, 0, NULL, 0, 0); \
DO_TEST_UPDATE_ONLY(arch, host, cpu); \
DO_TEST_COMPARE(arch, host, host "+" cpu, result); \
} while (0)
@ -737,8 +740,9 @@ mymain(void)
DO_TEST_UPDATE("x86", "host", "guest", VIR_CPU_COMPARE_SUPERSET);
DO_TEST_UPDATE("x86", "host", "host-model", VIR_CPU_COMPARE_IDENTICAL);
DO_TEST_UPDATE("x86", "host", "host-model-nofallback", VIR_CPU_COMPARE_IDENTICAL);
DO_TEST_UPDATE("x86", "host", "host-passthrough", VIR_CPU_COMPARE_IDENTICAL);
DO_TEST_UPDATE("x86", "host-invtsc", "host-model", VIR_CPU_COMPARE_SUPERSET);
DO_TEST_UPDATE_ONLY("x86", "host", "host-passthrough");
DO_TEST_UPDATE_ONLY("x86", "host", "host-passthrough-features");
DO_TEST_UPDATE("ppc64", "host", "guest", VIR_CPU_COMPARE_IDENTICAL);
DO_TEST_UPDATE("ppc64", "host", "guest-nofallback", VIR_CPU_COMPARE_INCOMPATIBLE);

View File

@ -1,4 +1,4 @@
<cpu mode='host-model' match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='forbid'>Penryn</model>
<vendor>Intel</vendor>
<topology sockets='1' cores='2' threads='1'/>

View File

@ -1,4 +1,4 @@
<cpu mode='host-model' match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>Penryn</model>
<vendor>Intel</vendor>
<feature policy='require' name='dca'/>

View File

@ -0,0 +1,4 @@
<cpu mode='host-passthrough'>
<feature policy='disable' name='dca'/>
<feature policy='force' name='vmx'/>
</cpu>

View File

@ -1,18 +1 @@
<cpu mode='host-passthrough' match='minimum'>
<model>Penryn</model>
<vendor>Intel</vendor>
<feature policy='require' name='dca'/>
<feature policy='require' name='xtpr'/>
<feature policy='require' name='tm2'/>
<feature policy='require' name='est'/>
<feature policy='require' name='vmx'/>
<feature policy='require' name='ds_cpl'/>
<feature policy='require' name='monitor'/>
<feature policy='require' name='pbe'/>
<feature policy='require' name='tm'/>
<feature policy='require' name='ht'/>
<feature policy='require' name='ss'/>
<feature policy='require' name='acpi'/>
<feature policy='require' name='ds'/>
<feature policy='require' name='vme'/>
</cpu>
<cpu mode='host-passthrough'/>

View File

@ -1,17 +1,18 @@
<cpu mode='custom' match='exact'>
<model fallback='allow'>Penryn</model>
<feature policy='require' name='vme'/>
<feature policy='require' name='ds'/>
<feature policy='require' name='acpi'/>
<feature policy='require' name='ss'/>
<feature policy='require' name='ht'/>
<feature policy='require' name='tm'/>
<feature policy='require' name='pbe'/>
<feature policy='require' name='monitor'/>
<feature policy='require' name='ds_cpl'/>
<feature policy='require' name='vmx'/>
<feature policy='require' name='est'/>
<feature policy='require' name='tm2'/>
<feature policy='require' name='xtpr'/>
<vendor>Intel</vendor>
<feature policy='require' name='dca'/>
<feature policy='require' name='xtpr'/>
<feature policy='require' name='tm2'/>
<feature policy='require' name='est'/>
<feature policy='require' name='vmx'/>
<feature policy='require' name='ds_cpl'/>
<feature policy='require' name='monitor'/>
<feature policy='require' name='pbe'/>
<feature policy='require' name='tm'/>
<feature policy='require' name='ht'/>
<feature policy='require' name='ss'/>
<feature policy='require' name='acpi'/>
<feature policy='require' name='ds'/>
<feature policy='require' name='vme'/>
</cpu>

View File

@ -1,27 +1,18 @@
<cpu mode='custom' match='exact'>
<model fallback='allow'>pentium3</model>
<feature policy='require' name='apic'/>
<feature policy='require' name='clflush'/>
<feature policy='require' name='ds'/>
<feature policy='require' name='acpi'/>
<feature policy='require' name='sse2'/>
<feature policy='require' name='ss'/>
<feature policy='require' name='ht'/>
<feature policy='require' name='tm'/>
<feature policy='require' name='pbe'/>
<feature policy='require' name='pni'/>
<feature policy='require' name='monitor'/>
<feature policy='require' name='ds_cpl'/>
<feature policy='require' name='vmx'/>
<feature policy='require' name='est'/>
<feature policy='require' name='tm2'/>
<feature policy='require' name='ssse3'/>
<feature policy='require' name='cx16'/>
<feature policy='require' name='xtpr'/>
<model fallback='allow'>Penryn</model>
<vendor>Intel</vendor>
<feature policy='require' name='dca'/>
<feature policy='require' name='sse4.1'/>
<feature policy='require' name='syscall'/>
<feature policy='require' name='nx'/>
<feature policy='require' name='lm'/>
<feature policy='require' name='lahf_lm'/>
<feature policy='require' name='xtpr'/>
<feature policy='require' name='tm2'/>
<feature policy='require' name='est'/>
<feature policy='require' name='vmx'/>
<feature policy='require' name='ds_cpl'/>
<feature policy='require' name='monitor'/>
<feature policy='require' name='pbe'/>
<feature policy='require' name='tm'/>
<feature policy='require' name='ht'/>
<feature policy='require' name='ss'/>
<feature policy='require' name='acpi'/>
<feature policy='require' name='ds'/>
<feature policy='require' name='vme'/>
</cpu>

View File

@ -1,4 +1,4 @@
<cpu mode='host-model' match='exact'>
<cpu mode='custom' match='exact'>
<model fallback='allow'>SandyBridge</model>
<vendor>Intel</vendor>
<feature policy='require' name='osxsave'/>

View File

@ -0,0 +1,4 @@
<cpu mode='host-passthrough'>
<feature policy='disable' name='dca'/>
<feature policy='force' name='vmx'/>
</cpu>