diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f7386026c1..faab0e252a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -187,6 +187,7 @@ virCPUDefFormatBuf; virCPUDefFree; virCPUDefFreeModel; virCPUDefParseXML; +virCPUModeTypeToString; # datatypes.h diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 530bbb187d..e9ae44d325 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -145,9 +145,10 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "no-acpi", "fsdev-readonly", - "virtio-blk-pci.scsi", + "virtio-blk-pci.scsi", /* 80 */ "blk-sg-io", "drive-copy-on-read", + "cpu-host", ); struct qemu_feature_flags { @@ -1184,6 +1185,9 @@ qemuCapsComputeCmdFlags(const char *help, */ if (version >= 12000) qemuCapsSet(flags, QEMU_CAPS_PCI_ROMBAR); + + if (version >= 11000) + qemuCapsSet(flags, QEMU_CAPS_CPU_HOST); } /* We parse the output of 'qemu -help' to get the QEMU diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index ee9d5ab051..d69f854a2d 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -121,6 +121,7 @@ enum qemuCapsFlags { QEMU_CAPS_VIRTIO_BLK_SCSI = 80, /* virtio-blk-pci.scsi */ QEMU_CAPS_VIRTIO_BLK_SG_IO = 81, /* support for SG_IO commands, reportedly added in 0.11 */ QEMU_CAPS_DRIVE_COPY_ON_READ = 82, /* -drive copy-on-read */ + QEMU_CAPS_CPU_HOST = 83, /* support for -cpu host */ QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d2789263c8..b45f65f1ac 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3458,10 +3458,12 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, virBitmapPtr qemuCaps, const struct utsname *ut, char **opt, - bool *hasHwVirt) + bool *hasHwVirt, + bool migrating) { const virCPUDefPtr host = driver->caps->host.cpu; virCPUDefPtr guest = NULL; + virCPUDefPtr cpu = NULL; unsigned int ncpus = 0; const char **cpus = NULL; union cpuData *data = NULL; @@ -3471,7 +3473,21 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, *hasHwVirt = false; - if (def->cpu && def->cpu->model) { + if (def->cpu && + (def->cpu->mode != VIR_CPU_MODE_CUSTOM || def->cpu->model)) { + if (!(cpu = virCPUDefCopy(def->cpu))) + goto cleanup; + if (cpu->mode != VIR_CPU_MODE_CUSTOM && + !migrating && + cpuUpdate(cpu, host) < 0) + goto cleanup; + } + + if (cpu) { + virCPUCompareResult cmp; + const char *preferred; + int hasSVM; + if (host && qemuCapsProbeCPUModels(emulator, qemuCaps, host->arch, &ncpus, &cpus) < 0) @@ -3482,18 +3498,12 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, _("CPU specification not supported by hypervisor")); goto cleanup; } - } - if (ncpus > 0 && host) { - virCPUCompareResult cmp; - const char *preferred; - int hasSVM; - - cmp = cpuGuestData(host, def->cpu, &data); + cmp = cpuGuestData(host, cpu, &data); switch (cmp) { case VIR_CPU_COMPARE_INCOMPATIBLE: - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("guest CPU is not compatible with host CPU")); + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest CPU is not compatible with host CPU")); /* fall through */ case VIR_CPU_COMPARE_ERROR: goto cleanup; @@ -3502,39 +3512,55 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, break; } - if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(host->arch))) - goto no_memory; - - if (def->cpu->match == VIR_CPU_MATCH_MINIMUM) - preferred = host->model; - else - preferred = def->cpu->model; - - guest->type = VIR_CPU_TYPE_GUEST; - guest->fallback = def->cpu->fallback; - if (cpuDecode(guest, data, cpus, ncpus, preferred) < 0) - goto cleanup; - /* Only 'svm' requires --enable-nesting. The nested * 'vmx' patches now simply hook off the CPU features */ - hasSVM = cpuHasFeature(guest->arch, data, "svm"); + hasSVM = cpuHasFeature(host->arch, data, "svm"); if (hasSVM < 0) goto cleanup; *hasHwVirt = hasSVM > 0 ? true : false; - virBufferAdd(&buf, guest->model, -1); - for (i = 0; i < guest->nfeatures; i++) { - char sign; - if (guest->features[i].policy == VIR_CPU_FEATURE_DISABLE) - sign = '-'; - else - sign = '+'; + if (cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) { + const char *mode = virCPUModeTypeToString(cpu->mode); + if (!qemuCapsGet(qemuCaps, QEMU_CAPS_CPU_HOST)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("CPU mode '%s' is not supported by QEMU" + " binary"), mode); + goto cleanup; + } + if (def->virtType != VIR_DOMAIN_VIRT_KVM) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("CPU mode '%s' is only supported with kvm"), + mode); + goto cleanup; + } + virBufferAddLit(&buf, "host"); + } else { + if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(host->arch))) + goto no_memory; - virBufferAsprintf(&buf, ",%c%s", sign, guest->features[i].name); + 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 (cpuDecode(guest, data, cpus, ncpus, preferred) < 0) + goto cleanup; + + virBufferAdd(&buf, guest->model, -1); + for (i = 0; i < guest->nfeatures; i++) { + char sign; + if (guest->features[i].policy == VIR_CPU_FEATURE_DISABLE) + sign = '-'; + else + sign = '+'; + + virBufferAsprintf(&buf, ",%c%s", sign, guest->features[i].name); + } } - } - else { + } else { /* * Need to force a 32-bit guest CPU type if * @@ -3565,6 +3591,7 @@ cleanup: if (guest) cpuDataFree(guest->arch, data); virCPUDefFree(guest); + virCPUDefFree(cpu); if (cpus) { for (i = 0; i < ncpus; i++) @@ -3787,7 +3814,7 @@ qemuBuildCommandLine(virConnectPtr conn, virCommandAddArgList(cmd, "-M", def->os.machine, NULL); if (qemuBuildCpuArgStr(driver, def, emulator, qemuCaps, - &ut, &cpu, &hasHwVirt) < 0) + &ut, &cpu, &hasHwVirt, !!migrateFrom) < 0) goto error; if (cpu) { diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index 2b08af93ed..164707df56 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -330,7 +330,8 @@ mymain(void) QEMU_CAPS_NO_SHUTDOWN, QEMU_CAPS_PCI_ROMBAR, QEMU_CAPS_NO_ACPI, - QEMU_CAPS_VIRTIO_BLK_SG_IO); + QEMU_CAPS_VIRTIO_BLK_SG_IO, + QEMU_CAPS_CPU_HOST); DO_TEST("qemu-kvm-0.12.1.2-rhel60", 12001, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -378,7 +379,8 @@ mymain(void) QEMU_CAPS_NO_SHUTDOWN, QEMU_CAPS_PCI_ROMBAR, QEMU_CAPS_NO_ACPI, - QEMU_CAPS_VIRTIO_BLK_SG_IO); + QEMU_CAPS_VIRTIO_BLK_SG_IO, + QEMU_CAPS_CPU_HOST); DO_TEST("qemu-kvm-0.12.3", 12003, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -419,7 +421,8 @@ mymain(void) QEMU_CAPS_NO_SHUTDOWN, QEMU_CAPS_PCI_ROMBAR, QEMU_CAPS_NO_ACPI, - QEMU_CAPS_VIRTIO_BLK_SG_IO); + QEMU_CAPS_VIRTIO_BLK_SG_IO, + QEMU_CAPS_CPU_HOST); DO_TEST("qemu-kvm-0.13.0", 13000, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -476,7 +479,8 @@ mymain(void) QEMU_CAPS_NO_SHUTDOWN, QEMU_CAPS_PCI_ROMBAR, QEMU_CAPS_NO_ACPI, - QEMU_CAPS_VIRTIO_BLK_SG_IO); + QEMU_CAPS_VIRTIO_BLK_SG_IO, + QEMU_CAPS_CPU_HOST); DO_TEST("qemu-kvm-0.12.1.2-rhel61", 12001, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -530,7 +534,8 @@ mymain(void) QEMU_CAPS_PCI_ROMBAR, QEMU_CAPS_NO_ACPI, QEMU_CAPS_VIRTIO_BLK_SCSI, - QEMU_CAPS_VIRTIO_BLK_SG_IO); + QEMU_CAPS_VIRTIO_BLK_SG_IO, + QEMU_CAPS_CPU_HOST); DO_TEST("qemu-kvm-0.12.1.2-rhel62-beta", 12001, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -593,7 +598,8 @@ mymain(void) QEMU_CAPS_NO_ACPI, QEMU_CAPS_VIRTIO_BLK_SCSI, QEMU_CAPS_VIRTIO_BLK_SG_IO, - QEMU_CAPS_DRIVE_COPY_ON_READ); + QEMU_CAPS_DRIVE_COPY_ON_READ, + QEMU_CAPS_CPU_HOST); DO_TEST("qemu-1.0", 1000000, 0, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -659,7 +665,8 @@ mymain(void) QEMU_CAPS_NO_ACPI, QEMU_CAPS_FSDEV_READONLY, QEMU_CAPS_VIRTIO_BLK_SCSI, - QEMU_CAPS_VIRTIO_BLK_SG_IO); + QEMU_CAPS_VIRTIO_BLK_SG_IO, + QEMU_CAPS_CPU_HOST); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/qemuxml2argvdata/qemu-lib.sh b/tests/qemuxml2argvdata/qemu-lib.sh new file mode 100644 index 0000000000..ba19119910 --- /dev/null +++ b/tests/qemuxml2argvdata/qemu-lib.sh @@ -0,0 +1,50 @@ +candidates="/usr/bin/qemu-kvm + /usr/libexec/qemu-kvm + /usr/bin/qemu-system-x86_64 + /usr/bin/qemu" +qemu= +for candidate in $candidates; do + if test -x $candidate; then + qemu=$candidate + break + fi +done + +real_qemu() +{ + if test x$qemu != x; then + exec $qemu "$@" + else + return 1 + fi +} + +faked_machine() +{ + echo "pc" +} + +faked_cpu() +{ + cat < + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219100 + 219100 + 6 + + hvm + + + + + destroy + restart + destroy + + /./qemu-supported-cpus.sh + + diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model-nofallback.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model-nofallback.xml new file mode 100644 index 0000000000..c2ded11357 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model-nofallback.xml @@ -0,0 +1,21 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219100 + 219100 + 6 + + hvm + + + + + + + destroy + restart + destroy + + /./qemu-supported-cpus.sh + + diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model.args new file mode 100644 index 0000000000..cf7eb2ac81 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model.args @@ -0,0 +1,19 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +./qemu.sh \ +-S \ +-M pc \ +-cpu core2duo,+lahf_lm,+xtpr,+cx16,+tm2,+est,+vmx,+ds_cpl,+pbe,+tm,+ht,+ss,+acpi,+ds \ +-m 214 \ +-smp 6 \ +-nographic \ +-monitor unix:/tmp/test-monitor,server,nowait \ +-no-acpi \ +-boot n \ +-net none \ +-serial none \ +-parallel none \ +-usb diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model.xml new file mode 100644 index 0000000000..96b046cc78 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-model.xml @@ -0,0 +1,19 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219100 + 219100 + 6 + + hvm + + + + + destroy + restart + destroy + + /./qemu.sh + + diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-passthrough.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-passthrough.args new file mode 100644 index 0000000000..c63ecce629 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-passthrough.args @@ -0,0 +1,19 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +./qemu.sh \ +-S \ +-M pc \ +-cpu host \ +-m 214 \ +-smp 6 \ +-nographic \ +-monitor unix:/tmp/test-monitor,server,nowait \ +-no-acpi \ +-boot n \ +-net none \ +-serial none \ +-parallel none \ +-usb diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-passthrough.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-passthrough.xml new file mode 100644 index 0000000000..2d7502574c --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-host-passthrough.xml @@ -0,0 +1,19 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219100 + 219100 + 6 + + hvm + + + + + destroy + restart + destroy + + /./qemu.sh + + diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-qemu-host-passthrough.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-qemu-host-passthrough.xml new file mode 100644 index 0000000000..a7123ce77d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-qemu-host-passthrough.xml @@ -0,0 +1,19 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219100 + 219100 + 6 + + hvm + + + + + destroy + restart + destroy + + /./qemu.sh + + diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index e5161938da..d1834f59d3 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -690,6 +690,13 @@ mymain(void) DO_TEST("cpu-strict1", false, NONE); DO_TEST("cpu-numa1", false, NONE); DO_TEST("cpu-numa2", false, QEMU_CAPS_SMP_TOPOLOGY); + DO_TEST("cpu-host-model", false, NONE); + DO_TEST("cpu-host-model-fallback", false, NONE); + DO_TEST_FAILURE("cpu-host-model-nofallback", NONE); + DO_TEST("cpu-host-passthrough", false, QEMU_CAPS_KVM, QEMU_CAPS_CPU_HOST); + DO_TEST_FAILURE("cpu-host-passthrough", NONE); + DO_TEST_FAILURE("cpu-qemu-host-passthrough", + QEMU_CAPS_KVM, QEMU_CAPS_CPU_HOST); DO_TEST("memtune", false, QEMU_CAPS_NAME); DO_TEST("blkiotune", false, QEMU_CAPS_NAME);