qemu: Update guest CPU def in live XML

Storing the updated CPU definition in the live domain definition saves
us from having to update it over and over when we need it. Not to
mention that we will soon further update the CPU definition according to
QEMU once it's started.

A highly wanted side effect of this patch, libvirt will pass all CPU
features explicitly specified in domain XML to QEMU, even those that are
already included in the host model.

This patch should fix the following bugs:
    https://bugzilla.redhat.com/show_bug.cgi?id=1207095
    https://bugzilla.redhat.com/show_bug.cgi?id=1339680
    https://bugzilla.redhat.com/show_bug.cgi?id=1371039
    https://bugzilla.redhat.com/show_bug.cgi?id=1373849
    https://bugzilla.redhat.com/show_bug.cgi?id=1375524
    https://bugzilla.redhat.com/show_bug.cgi?id=1377913

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
This commit is contained in:
Jiri Denemark 2016-06-22 15:53:48 +02:00
parent 7f127ded65
commit 7ce711a30e
11 changed files with 152 additions and 223 deletions

View File

@ -6539,62 +6539,18 @@ static int
qemuBuildCpuModelArgStr(virQEMUDriverPtr driver,
const virDomainDef *def,
virBufferPtr buf,
virQEMUCapsPtr qemuCaps,
bool *hasHwVirt,
bool migrating)
virQEMUCapsPtr qemuCaps)
{
int ret = -1;
size_t i;
virCPUDefPtr host = NULL;
virCPUDefPtr guest = NULL;
virCPUDefPtr cpu = NULL;
virCPUDefPtr featCpu = NULL;
size_t ncpus = 0;
char **cpus = NULL;
virCPUDataPtr data = NULL;
const char *preferred;
virCapsPtr caps = NULL;
bool compareAgainstHost = ((def->virtType == VIR_DOMAIN_VIRT_KVM ||
def->cpu->mode != VIR_CPU_MODE_CUSTOM) &&
def->cpu->mode != VIR_CPU_MODE_HOST_PASSTHROUGH);
virCPUDefPtr cpu = def->cpu;
if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
goto cleanup;
host = caps->host.cpu;
if (!(cpu = virCPUDefCopy(def->cpu)))
goto cleanup;
if (cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
!migrating &&
virCPUUpdate(def->os.arch, cpu, host) < 0)
goto cleanup;
if (compareAgainstHost &&
cpuGuestData(host, cpu, &data, NULL) == VIR_CPU_COMPARE_ERROR)
goto cleanup;
/* Only 'svm' requires --enable-nesting. The nested
* 'vmx' patches now simply hook off the CPU features
*/
if ((def->os.arch == VIR_ARCH_X86_64 || def->os.arch == VIR_ARCH_I686) &&
compareAgainstHost) {
int hasSVM = virCPUDataCheckFeature(data, "svm");
if (hasSVM < 0)
goto cleanup;
*hasHwVirt = hasSVM > 0 ? true : false;
}
if ((cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) ||
((cpu->mode == VIR_CPU_MODE_HOST_MODEL) &&
ARCH_IS_PPC64(def->os.arch))) {
if (def->virtType != VIR_DOMAIN_VIRT_KVM) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("CPU mode '%s' is only supported with kvm"),
virCPUModeTypeToString(cpu->mode));
goto cleanup;
}
switch ((virCPUMode) cpu->mode) {
case VIR_CPU_MODE_HOST_PASSTHROUGH:
virBufferAddLit(buf, "host");
if (def->os.arch == VIR_ARCH_ARMV7L &&
@ -6606,69 +6562,55 @@ qemuBuildCpuModelArgStr(virQEMUDriverPtr driver,
"aarch64 host"));
goto cleanup;
}
virBufferAddLit(buf, ",aarch64=off");
}
break;
if (ARCH_IS_PPC64(def->os.arch) &&
cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
def->cpu->model != NULL) {
virBufferAsprintf(buf, ",compat=%s", def->cpu->model);
case VIR_CPU_MODE_HOST_MODEL:
if (ARCH_IS_PPC64(def->os.arch)) {
virBufferAddLit(buf, "host");
if (cpu->model)
virBufferAsprintf(buf, ",compat=%s", cpu->model);
} else {
featCpu = cpu;
}
} else {
if (VIR_ALLOC(guest) < 0)
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected host-model CPU for %s architecture"),
virArchToString(def->os.arch));
goto cleanup;
if (VIR_STRDUP(guest->vendor_id, cpu->vendor_id) < 0)
goto cleanup;
if (compareAgainstHost) {
guest->arch = host->arch;
if (cpu->match == VIR_CPU_MATCH_MINIMUM)
preferred = host->model;
else
preferred = cpu->model;
guest->type = VIR_CPU_TYPE_GUEST;
guest->fallback = cpu->fallback;
if (virQEMUCapsGetCPUDefinitions(qemuCaps, &cpus, &ncpus) < 0)
goto cleanup;
if (cpuDecode(guest, data,
(const char **)cpus, ncpus, preferred) < 0)
goto cleanup;
} else {
guest->arch = def->os.arch;
if (VIR_STRDUP(guest->model, cpu->model) < 0)
goto cleanup;
}
virBufferAdd(buf, guest->model, -1);
if (guest->vendor_id)
virBufferAsprintf(buf, ",vendor=%s", guest->vendor_id);
featCpu = guest;
break;
case VIR_CPU_MODE_CUSTOM:
virBufferAdd(buf, cpu->model, -1);
break;
case VIR_CPU_MODE_LAST:
break;
}
if (featCpu) {
for (i = 0; i < featCpu->nfeatures; i++) {
char sign;
if (featCpu->features[i].policy == VIR_CPU_FEATURE_DISABLE)
sign = '-';
else
sign = '+';
if (cpu->vendor_id)
virBufferAsprintf(buf, ",vendor=%s", cpu->vendor_id);
virBufferAsprintf(buf, ",%c%s", sign, featCpu->features[i].name);
for (i = 0; i < cpu->nfeatures; i++) {
switch ((virCPUFeaturePolicy) cpu->features[i].policy) {
case VIR_CPU_FEATURE_FORCE:
case VIR_CPU_FEATURE_REQUIRE:
virBufferAsprintf(buf, ",+%s", cpu->features[i].name);
break;
case VIR_CPU_FEATURE_DISABLE:
case VIR_CPU_FEATURE_FORBID:
virBufferAsprintf(buf, ",-%s", cpu->features[i].name);
break;
case VIR_CPU_FEATURE_OPTIONAL:
case VIR_CPU_FEATURE_LAST:
break;
}
}
ret = 0;
cleanup:
virObjectUnref(caps);
cpuDataFree(data);
virCPUDefFree(guest);
virCPUDefFree(cpu);
virStringFreeListCount(cpus, ncpus);
return ret;
}
@ -6676,8 +6618,7 @@ static int
qemuBuildCpuCommandLine(virCommandPtr cmd,
virQEMUDriverPtr driver,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps,
bool migrating)
virQEMUCapsPtr qemuCaps)
{
virArch hostarch = virArchFromHost();
char *cpu = NULL;
@ -6695,10 +6636,28 @@ qemuBuildCpuCommandLine(virCommandPtr cmd,
if (def->cpu &&
(def->cpu->mode != VIR_CPU_MODE_CUSTOM || def->cpu->model)) {
if (qemuBuildCpuModelArgStr(driver, def, &buf, qemuCaps,
&hasHwVirt, migrating) < 0)
if (qemuBuildCpuModelArgStr(driver, def, &buf, qemuCaps) < 0)
goto cleanup;
have_cpu = true;
/* Only 'svm' requires --enable-nesting. The nested 'vmx' patches now
* simply hook off the CPU features. */
if (ARCH_IS_X86(def->os.arch) &&
def->virtType == VIR_DOMAIN_VIRT_KVM) {
virCPUDefPtr cpuDef = NULL;
if (def->cpu->mode == VIR_CPU_MODE_CUSTOM)
cpuDef = def->cpu;
else if (def->cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH)
cpuDef = virQEMUCapsGetHostModel(qemuCaps);
if (cpuDef) {
int svm = virCPUCheckFeature(def->os.arch, cpuDef, "svm");
if (svm < 0)
goto cleanup;
hasHwVirt = svm > 0;
}
}
} else {
/*
* Need to force a 32-bit guest CPU type if
@ -9430,7 +9389,7 @@ qemuBuildCommandLine(virQEMUDriverPtr driver,
if (qemuBuildMachineCommandLine(cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildCpuCommandLine(cmd, driver, def, qemuCaps, !!migrateURI) < 0)
if (qemuBuildCpuCommandLine(cmd, driver, def, qemuCaps) < 0)
goto error;
if (qemuBuildDomainLoaderCommandLine(cmd, def, qemuCaps) < 0)

View File

@ -3399,14 +3399,8 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver,
/* Update guest CPU requirements according to host CPU */
if ((flags & VIR_DOMAIN_XML_UPDATE_CPU) &&
def->cpu &&
(def->cpu->mode != VIR_CPU_MODE_CUSTOM || def->cpu->model)) {
if (!caps->host.cpu ||
!caps->host.cpu->model) {
virReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("cannot get host CPU capabilities"));
goto cleanup;
}
(def->cpu->mode != VIR_CPU_MODE_CUSTOM ||
def->cpu->model)) {
if (virCPUUpdate(def->os.arch, def->cpu, caps->host.cpu) < 0)
goto cleanup;
}
@ -3542,10 +3536,13 @@ char *qemuDomainFormatXML(virQEMUDriverPtr driver,
{
virDomainDefPtr def;
if ((flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef)
if ((flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef) {
def = vm->newDef;
else
} else {
def = vm->def;
if (virDomainObjIsActive(vm))
flags &= ~VIR_DOMAIN_XML_UPDATE_CPU;
}
return qemuDomainDefFormatXML(driver, def, flags);
}

View File

@ -4443,9 +4443,6 @@ qemuProcessStartValidateGraphics(virDomainObjPtr vm)
}
}
if (qemuProcessStartValidateGuestCPU(vm, qemuCaps, caps, flags) < 0)
return -1;
return 0;
}
@ -4479,107 +4476,6 @@ qemuProcessStartValidateXML(virQEMUDriverPtr driver,
}
static int
qemuProcessStartValidateGuestCPU(virDomainObjPtr vm,
virQEMUCapsPtr qemuCaps,
virCapsPtr caps,
unsigned int flags)
{
int ret = -1;
virCPUDefPtr host = NULL;
virCPUDefPtr cpu = NULL;
size_t ncpus = 0;
char **cpus = NULL;
bool noTSX = false;
virCPUCompareResult cmp;
virCPUDataPtr data = NULL;
virCPUDataPtr hostData = NULL;
char *compare_msg = NULL;
if (!vm->def->cpu ||
(vm->def->cpu->mode == VIR_CPU_MODE_CUSTOM &&
!vm->def->cpu->model))
return 0;
if ((vm->def->virtType != VIR_DOMAIN_VIRT_KVM &&
vm->def->cpu->mode == VIR_CPU_MODE_CUSTOM) ||
vm->def->cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH)
return 0;
host = caps->host.cpu;
if (virQEMUCapsGetCPUDefinitions(qemuCaps, &cpus, &ncpus) < 0)
goto cleanup;
if (!host || !host->model || ncpus == 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("CPU specification not supported by hypervisor"));
goto cleanup;
}
if (!(cpu = virCPUDefCopy(vm->def->cpu)))
goto cleanup;
if (cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
flags & VIR_QEMU_PROCESS_START_NEW &&
virCPUUpdate(vm->def->os.arch, cpu, host) < 0)
goto cleanup;
cmp = cpuGuestData(host, cpu, &data, &compare_msg);
switch (cmp) {
case VIR_CPU_COMPARE_INCOMPATIBLE:
if (cpuEncode(host->arch, host, NULL, &hostData,
NULL, NULL, NULL, NULL) == 0 &&
(!virCPUDataCheckFeature(hostData, "hle") ||
!virCPUDataCheckFeature(hostData, "rtm")) &&
(STREQ_NULLABLE(cpu->model, "Haswell") ||
STREQ_NULLABLE(cpu->model, "Broadwell")))
noTSX = true;
if (compare_msg) {
if (noTSX) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("guest and host CPU are not compatible: "
"%s; try using '%s-noTSX' CPU model"),
compare_msg, cpu->model);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("guest and host CPU are not compatible: "
"%s"),
compare_msg);
}
} else {
if (noTSX) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("guest CPU is not compatible with host "
"CPU; try using '%s-noTSX' CPU model"),
cpu->model);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("guest CPU is not compatible with host "
"CPU"));
}
}
/* fall through */
case VIR_CPU_COMPARE_ERROR:
goto cleanup;
default:
break;
}
ret = 0;
cleanup:
VIR_FREE(compare_msg);
cpuDataFree(data);
cpuDataFree(hostData);
virCPUDefFree(cpu);
virStringFreeListCount(cpus, ncpus);
return ret;
}
/**
* qemuProcessStartValidate:
* @vm: domain object
@ -5052,6 +4948,76 @@ qemuProcessSetupHotpluggableVcpus(virQEMUDriverPtr driver,
}
static int
qemuProcessUpdateGuestCPU(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virCapsPtr caps,
unsigned int flags)
{
int ret = -1;
size_t nmodels = 0;
char **models = NULL;
if (!def->cpu)
return 0;
/* nothing to do if only topology part of CPU def is used */
if (def->cpu->mode == VIR_CPU_MODE_CUSTOM && !def->cpu->model)
return 0;
/* Old libvirt added host CPU model to host-model CPUs for migrations,
* while new libvirt just turns host-model into custom mode. We need
* to fix the mode to maintain backward compatibility and to avoid
* the CPU model to be replaced in virCPUUpdate.
*/
if (!(flags & VIR_QEMU_PROCESS_START_NEW) &&
ARCH_IS_X86(def->os.arch) &&
def->cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
def->cpu->model) {
def->cpu->mode = VIR_CPU_MODE_CUSTOM;
}
if (!virQEMUCapsIsCPUModeSupported(qemuCaps, caps, def->virtType,
def->cpu->mode)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("CPU mode '%s' for %s %s domain on %s host is not "
"supported by hypervisor"),
virCPUModeTypeToString(def->cpu->mode),
virArchToString(def->os.arch),
virDomainVirtTypeToString(def->virtType),
virArchToString(caps->host.arch));
return -1;
}
/* nothing to update for host-passthrough */
if (def->cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH)
return 0;
/* custom CPUs in TCG mode don't need to be compared to host CPU */
if (def->virtType != VIR_DOMAIN_VIRT_QEMU ||
def->cpu->mode != VIR_CPU_MODE_CUSTOM) {
if (virCPUCompare(caps->host.arch, virQEMUCapsGetHostModel(qemuCaps),
def->cpu, true) < 0)
return -1;
}
if (virCPUUpdate(def->os.arch, def->cpu,
virQEMUCapsGetHostModel(qemuCaps)) < 0)
goto cleanup;
if (virQEMUCapsGetCPUDefinitions(qemuCaps, &models, &nmodels) < 0 ||
virCPUTranslate(def->os.arch, def->cpu, models, nmodels) < 0)
goto cleanup;
def->cpu->fallback = VIR_CPU_FALLBACK_FORBID;
ret = 0;
cleanup:
virStringFreeListCount(models, nmodels);
return ret;
}
/**
* qemuProcessPrepareDomain
*
@ -5162,6 +5128,10 @@ qemuProcessPrepareDomain(virConnectPtr conn,
priv->monStart = 0;
priv->gotShutdown = false;
VIR_DEBUG("Updating guest CPU definition");
if (qemuProcessUpdateGuestCPU(vm->def, priv->qemuCaps, caps, flags) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(nodeset);

View File

@ -8,7 +8,7 @@ QEMU_AUDIO_DRV=none \
-name QEMUGuest1 \
-S \
-M pc \
-cpu Haswell \
-cpu Haswell,+rtm,+hle \
-m 214 \
-smp 6,sockets=6,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \

View File

@ -8,7 +8,8 @@ QEMU_AUDIO_DRV=none \
-name QEMUGuest1 \
-S \
-M pc \
-cpu core2duo,+ds,+ht,+tm,+ds_cpl,+xtpr,+3dnowext,+lahf_lm,-nx \
-cpu core2duo,+ds,+ht,+tm,+ds_cpl,+xtpr,+3dnowext,+lahf_lm,-nx,-cx16,-tm2,-pbe,\
-ss,-sse4a,-wdt \
-m 214 \
-smp 6,sockets=6,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \

View File

@ -8,7 +8,8 @@ QEMU_AUDIO_DRV=none \
-name QEMUGuest1 \
-S \
-M pc \
-cpu core2duo,+ds,+ht,+tm,+ds_cpl,+xtpr,+3dnowext,+lahf_lm,-nx \
-cpu core2duo,+ds,+ht,+tm,+ds_cpl,+xtpr,+3dnowext,+lahf_lm,-nx,-cx16,-tm2,-pbe,\
-ss,-sse4a,-wdt \
-m 214 \
-smp 6,sockets=6,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \

View File

@ -8,7 +8,7 @@ QEMU_AUDIO_DRV=none \
-name QEMUGuest1 \
-S \
-M pc \
-cpu Penryn,-sse4.1 \
-cpu Penryn,-sse4.1,-sse4.2,-popcnt,-aes \
-m 214 \
-smp 6,sockets=6,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \

View File

@ -9,7 +9,7 @@ QEMU_AUDIO_DRV=none \
-S \
-M pc \
-cpu Haswell,+vme,+ds,+acpi,+ss,+ht,+tm,+pbe,+dtes64,+monitor,+ds_cpl,+vmx,\
+smx,+est,+tm2,+xtpr,+pdcm,+osxsave,+f16c,+rdrand,+pdpe1gb,+abm \
+smx,+est,+tm2,+xtpr,+pdcm,+osxsave,+f16c,+rdrand,+pdpe1gb,+abm,+lahf_lm \
-m 214 \
-smp 6,sockets=6,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \

View File

@ -9,7 +9,7 @@ QEMU_AUDIO_DRV=none \
-S \
-M pc \
-cpu Penryn,+vme,+ds,+acpi,+ss,+ht,+tm,+pbe,+monitor,+ds_cpl,+vmx,+est,+tm2,\
+xtpr,-sse4.1 \
+xtpr,-sse4.1,+cx16,+lahf_lm \
-m 214 \
-smp 6,sockets=6,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \

View File

@ -9,7 +9,7 @@ QEMU_AUDIO_DRV=none \
-S \
-M pc \
-cpu core2duo,+ds,+acpi,+ss,+ht,+tm,+pbe,+ds_cpl,+vmx,+est,+tm2,+cx16,+xtpr,\
+lahf_lm,-syscall,-nx,-lm \
+lahf_lm,-syscall,-nx,-lm,-svm \
-m 214 \
-smp 6,sockets=6,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \

View File

@ -8,7 +8,8 @@ QEMU_AUDIO_DRV=none \
-name QEMUGuest1 \
-S \
-M pc \
-cpu core2duo,+ds,+acpi,+ht,+tm,+ds_cpl,+vmx,+est,+xtpr,+3dnowext,+lahf_lm,-nx \
-cpu core2duo,+ds,+acpi,+ht,+tm,+ds_cpl,+vmx,+est,+xtpr,+3dnowext,+lahf_lm,-nx,\
-cx16,-tm2,-pbe,-ss,-sse4a,-wdt \
-m 214 \
-smp 6,sockets=6,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \