[libvirt] [PATCH] Enable support for nested SVM

This enables support for nested SVM using the regular CPU model/features block. If the CPU model or features include 'svm', then the '-enable-nesting' flag will be added to the QEMU command line. Latest out of tree patches for nested 'vmx', no longer require the '-enable-nesting' flag. They instead just look at the cpu features. Several of the models already include svm support, but QEMU was just masking out the svm bit silently. So this will enable SVM on such models * src/qemu/qemu_conf.h: flag for -enable-nesting * src/qemu/qemu_conf.c: Use -enable-nesting if VMX or SVM are in the CPUID * src/cpu/cpu.h, src/cpu/cpu.c: API to check for a named feature * src/cpu/cpu_x86.c: x86 impl of feature check * src/libvirt_private.syms: Add cpuHasFeature * src/qemuhelptest.c: Add nesting flag where required --- src/cpu/cpu.c | 24 ++++++++++++++++++++++++ src/cpu/cpu.h | 11 +++++++++++ src/cpu/cpu_x86.c | 29 +++++++++++++++++++++++++++++ src/libvirt_private.syms | 1 + src/qemu/qemu_conf.c | 21 +++++++++++++++++++-- src/qemu/qemu_conf.h | 1 + tests/qemuhelptest.c | 12 ++++++++---- 7 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index def6974..f509e1c 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -424,3 +424,27 @@ cpuUpdate(virCPUDefPtr guest, return driver->update(guest, host); } + +bool +cpuHasFeature(const char *arch, + const union cpuData *data, + const char *feature) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, data=%p, feature=%s", + arch, data, feature); + + if ((driver = cpuGetSubDriver(arch)) == NULL) + return -1; + + if (driver->hasFeature == NULL) { + virCPUReportError(VIR_ERR_NO_SUPPORT, + _("cannot check guest CPU data for %s architecture"), + arch); + return -1; + } + + return driver->hasFeature(data, feature); +} + diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index a745917..3a7b996 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -82,6 +82,10 @@ typedef int (*cpuArchUpdate) (virCPUDefPtr guest, const virCPUDefPtr host); +typedef bool +(*cpuArchHasFeature) (const union cpuData *data, + const char *feature); + struct cpuArchDriver { const char *name; @@ -95,6 +99,7 @@ struct cpuArchDriver { cpuArchGuestData guestData; cpuArchBaseline baseline; cpuArchUpdate update; + cpuArchHasFeature hasFeature; }; @@ -151,4 +156,10 @@ extern int cpuUpdate (virCPUDefPtr guest, const virCPUDefPtr host); +extern bool +cpuHasFeature(const char *arch, + const union cpuData *data, + const char *feature); + + #endif /* __VIR_CPU_H__ */ diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index 1937901..42349f0 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -1754,6 +1754,34 @@ cleanup: return ret; } +static bool x86HasFeature(const union cpuData *data, + const char *name) +{ + struct x86_map *map; + struct x86_feature *feature; + bool ret = false; + int i; + + if (!(map = x86LoadMap())) + return false; + + if (!(feature = x86FeatureFind(map, name))) + goto cleanup; + + for (i = 0 ; i < feature->ncpuid ; i++) { + struct cpuX86cpuid *cpuid; + + cpuid = x86DataCpuid(data, feature->cpuid[i].function); + if (cpuid && x86cpuidMatchMasked(cpuid, feature->cpuid + i)) { + ret = true; + goto cleanup; + } + } + +cleanup: + x86MapFree(map); + return ret; +} struct cpuArchDriver cpuDriverX86 = { .name = "x86", @@ -1771,4 +1799,5 @@ struct cpuArchDriver cpuDriverX86 = { .guestData = x86GuestData, .baseline = x86Baseline, .update = x86Update, + .hasFeature = x86HasFeature, }; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 301b0ef..0189183 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -90,6 +90,7 @@ cpuEncode; cpuGuestData; cpuNodeData; cpuUpdate; +cpuHasFeature; # cpu_conf.h diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 737b255..429c399 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1210,6 +1210,8 @@ static unsigned long long qemudComputeCmdFlags(const char *help, flags |= QEMUD_CMD_FLAG_NO_KVM_PIT; if (strstr(help, "-tdf")) flags |= QEMUD_CMD_FLAG_TDF; + if (strstr(help, "-enable-nesting")) + flags |= QEMUD_CMD_FLAG_NESTING; if (strstr(help, ",menu=on")) flags |= QEMUD_CMD_FLAG_BOOT_MENU; @@ -3503,7 +3505,8 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, const char *emulator, unsigned long long qemuCmdFlags, const struct utsname *ut, - char **opt) + char **opt, + bool *hasHwVirt) { const virCPUDefPtr host = driver->caps->host.cpu; virCPUDefPtr guest = NULL; @@ -3514,6 +3517,8 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, virBuffer buf = VIR_BUFFER_INITIALIZER; int i; + *hasHwVirt = false; + if (def->cpu && def->cpu->model) { if (qemudProbeCPUModels(emulator, qemuCmdFlags, ut->machine, &ncpus, &cpus) < 0) @@ -3555,6 +3560,12 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, if (cpuDecode(guest, data, cpus, ncpus, preferred) < 0) goto cleanup; + /* Only 'svm' requires --enable-nesting. The out-of-tree + * 'vmx' patches now simply hook off the CPU features + */ + *hasHwVirt = + cpuHasFeature(guest->arch, data, "svm"); + virBufferVSprintf(&buf, "%s", guest->model); for (i = 0; i < guest->nfeatures; i++) { char sign; @@ -3681,6 +3692,7 @@ int qemudBuildCommandLine(virConnectPtr conn, char *cpu; char *smp; int last_good_net = -1; + bool hasHwVirt = false; uname_normalize(&ut); @@ -3874,13 +3886,18 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG_LIT(def->os.machine); } - if (qemuBuildCpuArgStr(driver, def, emulator, qemuCmdFlags, &ut, &cpu) < 0) + if (qemuBuildCpuArgStr(driver, def, emulator, qemuCmdFlags, + &ut, &cpu, &hasHwVirt) < 0) goto error; if (cpu) { ADD_ARG_LIT("-cpu"); ADD_ARG_LIT(cpu); VIR_FREE(cpu); + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NESTING) && + hasHwVirt) + ADD_ARG_LIT("-enable-nesting"); } if (disableKQEMU) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 2c9e608..16f72f5 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -93,6 +93,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_NODEFCONFIG = (1LL << 37), /* -nodefconfig */ QEMUD_CMD_FLAG_BOOT_MENU = (1LL << 38), /* -boot menu=on support */ QEMUD_CMD_FLAG_ENABLE_KQEMU = (1LL << 39), /* -enable-kqemu flag */ + QEMUD_CMD_FLAG_NESTING = (1LL << 40), /* -enable-nesting (SVM/VMX) */ }; /* Main driver state */ diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index 56a49fd..d072cb0 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -171,7 +171,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_RTC_TD_HACK | QEMUD_CMD_FLAG_NO_HPET | QEMUD_CMD_FLAG_NO_KVM_PIT | - QEMUD_CMD_FLAG_TDF, + QEMUD_CMD_FLAG_TDF | + QEMUD_CMD_FLAG_NESTING, 10005, 1, 0); DO_TEST("kvm-86", QEMUD_CMD_FLAG_VNC_COLON | @@ -194,7 +195,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_RTC_TD_HACK | QEMUD_CMD_FLAG_NO_HPET | QEMUD_CMD_FLAG_NO_KVM_PIT | - QEMUD_CMD_FLAG_TDF, + QEMUD_CMD_FLAG_TDF | + QEMUD_CMD_FLAG_NESTING, 10050, 1, 0); DO_TEST("qemu-kvm-0.11.0-rc2", QEMUD_CMD_FLAG_VNC_COLON | @@ -221,7 +223,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_NO_HPET | QEMUD_CMD_FLAG_NO_KVM_PIT | QEMUD_CMD_FLAG_TDF | - QEMUD_CMD_FLAG_BOOT_MENU, + QEMUD_CMD_FLAG_BOOT_MENU | + QEMUD_CMD_FLAG_NESTING, 10092, 1, 0); DO_TEST("qemu-0.12.1", QEMUD_CMD_FLAG_VNC_COLON | @@ -277,7 +280,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_NO_HPET | QEMUD_CMD_FLAG_NO_KVM_PIT | QEMUD_CMD_FLAG_TDF | - QEMUD_CMD_FLAG_BOOT_MENU, + QEMUD_CMD_FLAG_BOOT_MENU | + QEMUD_CMD_FLAG_NESTING, 12003, 1, 0); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -- 1.7.2.3

On 10/12/2010 04:46 AM, Daniel P. Berrange wrote:
This enables support for nested SVM using the regular CPU model/features block. If the CPU model or features include 'svm', then the '-enable-nesting' flag will be added to the QEMU command line. Latest out of tree patches for nested 'vmx', no longer require the '-enable-nesting' flag. They instead just look at the cpu features. Several of the models already include svm support, but QEMU was just masking out the svm bit silently. So this will enable SVM on such models
+ +bool +cpuHasFeature(const char *arch, + const union cpuData *data, + const char *feature) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, data=%p, feature=%s", + arch, data, feature); + + if ((driver = cpuGetSubDriver(arch)) == NULL) + return -1;
Ouch. -1 promotes to true.
+ + if (driver->hasFeature == NULL) { + virCPUReportError(VIR_ERR_NO_SUPPORT, + _("cannot check guest CPU data for %s architecture"), + arch); + return -1;
Likewise.
+ } + + return driver->hasFeature(data, feature); +}
You either need to change the return type to int and take a bool* parameter (return -1 on failure, 0 on success when *param was written to), or return an int value rather than a bool; since the caller needs to distinguish between feature-not-present and error-message-reported.
+ diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index a745917..3a7b996 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -82,6 +82,10 @@ typedef int (*cpuArchUpdate) (virCPUDefPtr guest, const virCPUDefPtr host);
+typedef bool +(*cpuArchHasFeature) (const union cpuData *data, + const char *feature);
But this typedef is okay. ...
+ for (i = 0 ; i< feature->ncpuid ; i++) { + struct cpuX86cpuid *cpuid; + + cpuid = x86DataCpuid(data, feature->cpuid[i].function); + if (cpuid&& x86cpuidMatchMasked(cpuid, feature->cpuid + i)) { + ret = true; + goto cleanup;
I probably would have used 'break' rather than 'goto cleanup', since it's shorter, but since you already have to have the label due to earlier code in the method, either way is fine.
+ } + } + +cleanup: + x86MapFree(map); + return ret; +}
+++ b/src/qemu/qemu_conf.c @@ -1210,6 +1210,8 @@ static unsigned long long qemudComputeCmdFlags(const char *help, flags |= QEMUD_CMD_FLAG_NO_KVM_PIT; if (strstr(help, "-tdf")) flags |= QEMUD_CMD_FLAG_TDF; + if (strstr(help, "-enable-nesting")) + flags |= QEMUD_CMD_FLAG_NESTING; if (strstr(help, ",menu=on")) flags |= QEMUD_CMD_FLAG_BOOT_MENU;
Any reason you didn't put the new feature at the end of the list, in enum order?
@@ -3555,6 +3560,12 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, if (cpuDecode(guest, data, cpus, ncpus, preferred)< 0) goto cleanup;
+ /* Only 'svm' requires --enable-nesting. The out-of-tree + * 'vmx' patches now simply hook off the CPU features
This comment will be out-of-date once the vmx patches are no longer out of tree. Should we just say: "Nested vmx support in qemu simply hooks off the CPU features" -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Tue, Oct 12, 2010 at 08:23:21AM -0600, Eric Blake wrote:
On 10/12/2010 04:46 AM, Daniel P. Berrange wrote:
This enables support for nested SVM using the regular CPU model/features block. If the CPU model or features include 'svm', then the '-enable-nesting' flag will be added to the QEMU command line. Latest out of tree patches for nested 'vmx', no longer require the '-enable-nesting' flag. They instead just look at the cpu features. Several of the models already include svm support, but QEMU was just masking out the svm bit silently. So this will enable SVM on such models
+ +bool +cpuHasFeature(const char *arch, + const union cpuData *data, + const char *feature) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, data=%p, feature=%s", + arch, data, feature); + + if ((driver = cpuGetSubDriver(arch)) == NULL) + return -1;
Ouch. -1 promotes to true.
+ + if (driver->hasFeature == NULL) { + virCPUReportError(VIR_ERR_NO_SUPPORT, + _("cannot check guest CPU data for %s architecture"), + arch); + return -1;
Likewise.
+ } + + return driver->hasFeature(data, feature); +}
You either need to change the return type to int and take a bool* parameter (return -1 on failure, 0 on success when *param was written to), or return an int value rather than a bool; since the caller needs to distinguish between feature-not-present and error-message-reported.
Yup, that's why I'm always a bit suspicious when a function returns a boolean, that said for an internal API it's less of a problem Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Eric Blake