[libvirt] [PATCH 0/2] Make non-KVM machines work with QMP probing

This "series wannabe" fixes a problem with SW emulated machines not being able to be run with upstream qemu (no qemu-kvm + QMP probing). Libvirt currently sets QEMU_CAPS_KVM based on the fact that QEMU knows about 'query-kvm' QMP command. This not necessarily means that '-no-kvm' option needs to be supplied in order to disable KVM and that consequently leads to a non-startable domain when qemu doesn't know about the option. In order to fix this issue only with QMP, I thought about two possibilities, so... The first patch represents what's identical for both possibilities, whether the second one can be chosen from [2/2 v1] and [2/2 v2] (I didn't come up with better naming, sorry). Martin Kletzander (2): qemu: Enhance QMP detection of KVM state qemu: Fix SW emulated machines src/qemu/qemu_capabilities.c | 33 +++++++++++++++++++++++++++++- src/qemu/qemu_command.c | 6 ++++-- src/qemu/qemu_monitor.c | 23 +++++++++++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 5 +++++ 6 files changed, 116 insertions(+), 3 deletions(-) -- 1.7.12.4

When there is no 'qemu-kvm' binary and the emulator used for a machine is, for example, 'qemu-system-x86_64' that, by default, runs without kvm enabled, libvirt still supplies '-no-kvm' option to this process, even though it does not recognize such option (making the start of a domain fail in that case). This patch adds QMP querying for KVM state using 'query-kvm' state, but does not set any of QEMU_CAPS_KVM and QEMU_CAPS_ENABLE_KVM flags. That functionality is done in different patch in order to be able to compare two possibilities and chose the better one without looking at the part of the code that's exactly the same for both of them (this patch). --- src/qemu/qemu_capabilities.c | 23 +++++++++++++++++++++ src/qemu/qemu_monitor.c | 23 +++++++++++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 5 +++++ 5 files changed, 103 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 115a054..fe462e9 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -2026,6 +2026,27 @@ qemuCapsProbeQMPCPUDefinitions(qemuCapsPtr caps, } +static int +qemuCapsProbeQMPKVMState(qemuCapsPtr caps, + qemuMonitorPtr mon) +{ + bool enabled = false; + bool present = false; + + if (!qemuCapsGet(caps, QEMU_CAPS_KVM)) + return 0; + + if (qemuMonitorGetKVMState(mon, &enabled, &present) < 0) + return -1; + + /* Youre right, this code does nothing, you must have checked out + * some weird commit. Go back to your room and think about what + * you've done, young (wo)man. */ + + return 0; +} + + int qemuCapsProbeQMP(qemuCapsPtr caps, qemuMonitorPtr mon) { @@ -2318,6 +2339,8 @@ qemuCapsInitQMP(qemuCapsPtr caps, goto cleanup; if (qemuCapsProbeQMPCPUDefinitions(caps, mon) < 0) goto cleanup; + if (qemuCapsProbeQMPKVMState(caps, mon) < 0) + goto cleanup; ret = 0; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 2d9c44c..2ce52f6 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3183,6 +3183,29 @@ int qemuMonitorGetEvents(qemuMonitorPtr mon, } +int qemuMonitorGetKVMState(qemuMonitorPtr mon, + bool *enabled, + bool *present) +{ + VIR_DEBUG("mon=%p enabled=%p present=%p", + mon, enabled, present); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONGetKVMState(mon, enabled, present); +} + + int qemuMonitorGetObjectTypes(qemuMonitorPtr mon, char ***types) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 8856d9f..ccc3ab6 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -608,6 +608,10 @@ int qemuMonitorGetCommands(qemuMonitorPtr mon, int qemuMonitorGetEvents(qemuMonitorPtr mon, char ***events); +int qemuMonitorGetKVMState(qemuMonitorPtr mon, + bool *enabled, + bool *present); + int qemuMonitorGetObjectTypes(qemuMonitorPtr mon, char ***types); int qemuMonitorGetObjectProps(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index e495c0a..b32cec2 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4218,6 +4218,54 @@ cleanup: } +int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon, + bool *enabled, + bool *present) +{ + int ret; + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + virJSONValuePtr data = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-kvm", NULL))) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + if (qemuMonitorJSONHasError(reply, "CommandNotFound")) + goto cleanup; + + ret = qemuMonitorJSONCheckError(cmd, reply); + } + + if (ret < 0) + goto cleanup; + + ret = -1; + + if (!(data = virJSONValueObjectGet(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-kvm reply was missing return data")); + goto cleanup; + } + + if (virJSONValueObjectGetBoolean(data, "enabled", enabled) < 0 || + virJSONValueObjectGetBoolean(data, "present", present) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-kvm replied unexpected data")); + goto cleanup; + } + + ret = 0; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONGetObjectTypes(qemuMonitorPtr mon, char ***types) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 61127a7..1b83764 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -307,6 +307,11 @@ int qemuMonitorJSONGetEvents(qemuMonitorPtr mon, char ***events) ATTRIBUTE_NONNULL(2); +int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon, + bool *enabled, + bool *present) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + int qemuMonitorJSONGetObjectTypes(qemuMonitorPtr mon, char ***types) ATTRIBUTE_NONNULL(2); -- 1.7.12.4

On 10/28/2012 06:55 AM, Martin Kletzander wrote:
When there is no 'qemu-kvm' binary and the emulator used for a machine is, for example, 'qemu-system-x86_64' that, by default, runs without kvm enabled, libvirt still supplies '-no-kvm' option to this process, even though it does not recognize such option (making the start of a domain fail in that case).
This patch adds QMP querying for KVM state using 'query-kvm' state, but does not set any of QEMU_CAPS_KVM and QEMU_CAPS_ENABLE_KVM flags. That functionality is done in different patch in order to be able to compare two possibilities and chose the better one without looking at the part of the code that's exactly the same for both of them (this patch).
+static int +qemuCapsProbeQMPKVMState(qemuCapsPtr caps, + qemuMonitorPtr mon) +{ + bool enabled = false; + bool present = false; + + if (!qemuCapsGet(caps, QEMU_CAPS_KVM)) + return 0; + + if (qemuMonitorGetKVMState(mon, &enabled, &present) < 0) + return -1; + + /* Youre right, this code does nothing, you must have checked out + * some weird commit. Go back to your room and think about what + * you've done, young (wo)man. */
Since this cute comment disappears in either 2/2 approach, I would probably rather see this "series" squashed into a single commit when finally going upstream. That would also mean deleting the second paragraph of the commit message.
+int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon, + bool *enabled, + bool *present) +{ + int ret; + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + virJSONValuePtr data = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-kvm", NULL))) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + if (qemuMonitorJSONHasError(reply, "CommandNotFound")) + goto cleanup;
This relies on the caller to pre-initialize *enabled and *present to false; it might be better to explicitly repeat that setting here so that this function guarantees that the values are always correct on successful return even if the caller forgot to initialize. But right now, the only caller happens to pre-initialize, so it's not a show-stopper. ACK, once I pick which of the 2/2 variants I like best :) -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/31/2012 12:44 AM, Eric Blake wrote:
On 10/28/2012 06:55 AM, Martin Kletzander wrote:
When there is no 'qemu-kvm' binary and the emulator used for a machine is, for example, 'qemu-system-x86_64' that, by default, runs without kvm enabled, libvirt still supplies '-no-kvm' option to this process, even though it does not recognize such option (making the start of a domain fail in that case).
This patch adds QMP querying for KVM state using 'query-kvm' state, but does not set any of QEMU_CAPS_KVM and QEMU_CAPS_ENABLE_KVM flags. That functionality is done in different patch in order to be able to compare two possibilities and chose the better one without looking at the part of the code that's exactly the same for both of them (this patch).
+static int +qemuCapsProbeQMPKVMState(qemuCapsPtr caps, + qemuMonitorPtr mon) +{ + bool enabled = false; + bool present = false; + + if (!qemuCapsGet(caps, QEMU_CAPS_KVM)) + return 0; + + if (qemuMonitorGetKVMState(mon, &enabled, &present) < 0) + return -1; + + /* Youre right, this code does nothing, you must have checked out + * some weird commit. Go back to your room and think about what + * you've done, young (wo)man. */
Since this cute comment disappears in either 2/2 approach, I would probably rather see this "series" squashed into a single commit when finally going upstream. That would also mean deleting the second paragraph of the commit message.
+int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon, + bool *enabled, + bool *present) +{ + int ret; + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + virJSONValuePtr data = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-kvm", NULL))) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + if (qemuMonitorJSONHasError(reply, "CommandNotFound")) + goto cleanup;
This relies on the caller to pre-initialize *enabled and *present to false; it might be better to explicitly repeat that setting here so that this function guarantees that the values are always correct on successful return even if the caller forgot to initialize. But right now, the only caller happens to pre-initialize, so it's not a show-stopper.
ACK, once I pick which of the 2/2 variants I like best :)
I squashed both in, fixed the huge amount of typos, added the explicit setting to false for the bools in qemuMonitorJSONGetKVMState(), double checked and pushed. Thanks very much. Martin

This patch fixes building a command-line for QEMU machines without KVM acceleration and is based on following assumptions: - QEMU_CAPS_KVM flag means that QEMU is running KVM accelerated machines by default (without explicitely requesting that using a command-line option). It is the closest to the true according to the code with the only exception being the comment next to the flag, so it's fixed in this patch as well. - QEMU_CAPS_ENABLE_KVM flag means that QEMU is, by default, running without KVM acceleration and in case we need KVM acceleration it needs to be explicitely instructed to do so. This is partially true for the past (this option essentially means that QEMU recognizes the '-enable-kvm' option, even though it's almost the same). --- src/qemu/qemu_capabilities.c | 13 +++++++++---- src/qemu/qemu_capabilities.h | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index fe462e9..aad366d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -2039,9 +2039,15 @@ qemuCapsProbeQMPKVMState(qemuCapsPtr caps, if (qemuMonitorGetKVMState(mon, &enabled, &present) < 0) return -1; - /* Youre right, this code does nothing, you must have checked out - * some weird commit. Go back to your room and think about what - * you've done, young (wo)man. */ + /* Until now, the QEMU_CAPS_KVM was set according to the QEMU + * reporting the recongnition of 'query-kvm' QMP command, but the + * flag means whether the KVM is enabled by default and should be + * disabled in case we want SW emulated machine, so let's fix that + * if it's true. */ + if (!enabled) { + qemuCapsClear(caps, QEMU_CAPS_KVM); + qemuCapsSet(caps, QEMU_CAPS_ENABLE_KVM); + } return 0; } @@ -2177,7 +2183,6 @@ qemuCapsInitQMPBasic(qemuCapsPtr caps) qemuCapsSet(caps, QEMU_CAPS_DRIVE_SERIAL); qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_UNIX); qemuCapsSet(caps, QEMU_CAPS_CHARDEV); - qemuCapsSet(caps, QEMU_CAPS_ENABLE_KVM); qemuCapsSet(caps, QEMU_CAPS_MONITOR_JSON); qemuCapsSet(caps, QEMU_CAPS_BALLOON); qemuCapsSet(caps, QEMU_CAPS_DEVICE); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 988dfdb..9c5fd0f 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -45,7 +45,7 @@ enum qemuCapsFlags { QEMU_CAPS_MIGRATE_QEMU_TCP = 10, /* have qemu tcp migration */ QEMU_CAPS_MIGRATE_QEMU_EXEC = 11, /* have qemu exec migration */ QEMU_CAPS_DRIVE_CACHE_V2 = 12, /* cache= flag wanting new v2 values */ - QEMU_CAPS_KVM = 13, /* Whether KVM is compiled in */ + QEMU_CAPS_KVM = 13, /* Whether KVM is enabled by default */ QEMU_CAPS_DRIVE_FORMAT = 14, /* Is -drive format= avail */ QEMU_CAPS_VGA = 15, /* Is -vga avail */ -- 1.7.12.4

On 10/28/2012 06:55 AM, Martin Kletzander wrote:
This patch fixes building a command-line for QEMU machines without KVM acceleration and is based on following assumptions:
- QEMU_CAPS_KVM flag means that QEMU is running KVM accelerated machines by default (without explicitely requesting that using a
s/explicitely/explicitly/
command-line option). It is the closest to the true according to
s/true/truth/
the code with the only exception being the comment next to the flag, so it's fixed in this patch as well.
- QEMU_CAPS_ENABLE_KVM flag means that QEMU is, by default, running without KVM acceleration and in case we need KVM acceleration it needs to be explicitely instructed to do so. This is partially
s/explicitely/explicitly/
true for the past (this option essentially means that QEMU recognizes the '-enable-kvm' option, even though it's almost the same).
I've looked at both variants, and like this one better (if only because of upgrade scenarios, since we save qemu capability flags in an older libvirtd and then re-read those flags when restarting a newer libvirtd, and expect the flags to have the same meaning no matter whether the older or newer libvirtd was the original source for those flags).
--- src/qemu/qemu_capabilities.c | 13 +++++++++---- src/qemu/qemu_capabilities.h | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index fe462e9..aad366d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -2039,9 +2039,15 @@ qemuCapsProbeQMPKVMState(qemuCapsPtr caps, if (qemuMonitorGetKVMState(mon, &enabled, &present) < 0) return -1;
- /* Youre right, this code does nothing, you must have checked out - * some weird commit. Go back to your room and think about what - * you've done, young (wo)man. */ + /* Until now, the QEMU_CAPS_KVM was set according to the QEMU
Reads awkwardly. Maybe: The QEMU_CAPS_KVM flag was initially set according to...
+ * reporting the recongnition of 'query-kvm' QMP command, but the
s/recongnition/recognition/
+ * flag means whether the KVM is enabled by default and should be + * disabled in case we want SW emulated machine, so let's fix that + * if it's true. */ + if (!enabled) { + qemuCapsClear(caps, QEMU_CAPS_KVM); + qemuCapsSet(caps, QEMU_CAPS_ENABLE_KVM); + }
return 0; } @@ -2177,7 +2183,6 @@ qemuCapsInitQMPBasic(qemuCapsPtr caps) qemuCapsSet(caps, QEMU_CAPS_DRIVE_SERIAL); qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_UNIX); qemuCapsSet(caps, QEMU_CAPS_CHARDEV); - qemuCapsSet(caps, QEMU_CAPS_ENABLE_KVM); qemuCapsSet(caps, QEMU_CAPS_MONITOR_JSON); qemuCapsSet(caps, QEMU_CAPS_BALLOON); qemuCapsSet(caps, QEMU_CAPS_DEVICE); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 988dfdb..9c5fd0f 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -45,7 +45,7 @@ enum qemuCapsFlags { QEMU_CAPS_MIGRATE_QEMU_TCP = 10, /* have qemu tcp migration */ QEMU_CAPS_MIGRATE_QEMU_EXEC = 11, /* have qemu exec migration */ QEMU_CAPS_DRIVE_CACHE_V2 = 12, /* cache= flag wanting new v2 values */ - QEMU_CAPS_KVM = 13, /* Whether KVM is compiled in */ + QEMU_CAPS_KVM = 13, /* Whether KVM is enabled by default */
ACK with typos fixed; per my comments in 1/2, I'd squash this into that patch and push it all as one commit. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

This patch fixes building a command-line for QEMU machines without KVM acceleration and is based on following assumptions: - QEMU_CAPS_KVM flag means that QEMU is capable of running KVM accelerated machine (not that it knows about KVM at all, even though there is probably no QEMU that knows about KVM and isn't able to use it). It is not actually true for the past (it meant that QEMU has '-no-kvm' option, that it doesn't always know), but we can say this is what it means from now on without any harm being done. - QEMU_CAPS_ENABLE_KVM flag means that QEMU is, by default, running without KVM acceleration and in case we need KVM acceleration it needs to be explicitely instructed to do so. This is partially true for the past (this option essentially means that QEMU recognizes the '-enable-kvm' option, even though it's almost the same). --- src/qemu/qemu_capabilities.c | 16 ++++++++++++---- src/qemu/qemu_command.c | 6 ++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index fe462e9..a19d833 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -2039,9 +2039,18 @@ qemuCapsProbeQMPKVMState(qemuCapsPtr caps, if (qemuMonitorGetKVMState(mon, &enabled, &present) < 0) return -1; - /* Youre right, this code does nothing, you must have checked out - * some weird commit. Go back to your room and think about what - * you've done, young (wo)man. */ + if (!present) { + /* When KVM is not present, the flag shouldn't be set, but it + * was set earlier when we discovered that QEMU is capable of + * "query-kvm" QMP command (which doesn't mean anything, + * essentially). So let's fix that. */ + qemuCapsClear(caps, QEMU_CAPS_KVM); + } else if (!enabled) { + /* We shouldn't use -enable-kvm when kvm is enabled by + * default, but more importantly, we should be able to know + * when not using it launches a software emulated machine. */ + qemuCapsSet(caps, QEMU_CAPS_ENABLE_KVM); + } return 0; } @@ -2177,7 +2186,6 @@ qemuCapsInitQMPBasic(qemuCapsPtr caps) qemuCapsSet(caps, QEMU_CAPS_DRIVE_SERIAL); qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_UNIX); qemuCapsSet(caps, QEMU_CAPS_CHARDEV); - qemuCapsSet(caps, QEMU_CAPS_ENABLE_KVM); qemuCapsSet(caps, QEMU_CAPS_MONITOR_JSON); qemuCapsSet(caps, QEMU_CAPS_BALLOON); qemuCapsSet(caps, QEMU_CAPS_DEVICE); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 898c4c0..2f863d3 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4466,12 +4466,14 @@ qemuBuildCommandLine(virConnectPtr conn, case VIR_DOMAIN_VIRT_QEMU: if (qemuCapsGet(caps, QEMU_CAPS_KQEMU)) disableKQEMU = 1; - if (qemuCapsGet(caps, QEMU_CAPS_KVM)) + if (qemuCapsGet(caps, QEMU_CAPS_KVM) && + !qemuCapsGet(caps, QEMU_CAPS_ENABLE_KVM)) disableKVM = 1; break; case VIR_DOMAIN_VIRT_KQEMU: - if (qemuCapsGet(caps, QEMU_CAPS_KVM)) + if (qemuCapsGet(caps, QEMU_CAPS_KVM) && + !qemuCapsGet(caps, QEMU_CAPS_ENABLE_KVM)) disableKVM = 1; if (qemuCapsGet(caps, QEMU_CAPS_ENABLE_KQEMU)) { -- 1.7.12.4
participants (2)
-
Eric Blake
-
Martin Kletzander