[PATCH v4 0/3] qemu: add an API for "query-stats" QMP command

QEMU is gaining introspectable statistics which can be queried via the "query-stats" QMP command. This patchset aims to add an API for the same. The returned JSON for "query-stats" is an array of objects containing their own respective array of statistics. Patch 1 adds the API for "query-stats" and a function to deserialize the returned objects into respective hashtables. Patch 2 adds the "query-stats" to QEMU capabilities. Patch 3 uses the API to query the halt poll success time and the halt poll failure time. v3 -> v4 ======== Some changes based on past reviews by Mr. Martin and me [1/3]: - - clean up qemuMonitorQueryStatsProviderNew. - make relevant checks/changes to support the change above. - do NOT return the GPtrArray, instead just return the json object. (this helps retain the non-stat info returned which will be useful in the future patches). - add a function which deserializes value associated with the "stats" key in the JSON object (one of the objects returned in the JSON array above) into a GHashTable. [2/3]: - add a flag to for the capability in tests. [3/3]: - use a helper function to make things cleaner. v2 -> v3 ======== Sorry for the late patchset, I was under the impression I had sent it on Monday but apparently, I did not. [1/3]: - use a single enum for all the statistics. - use QEMU_MONITOR_QUERY_STATS_NAME_LAST as the sentinel value for the provider constructor. - relevant changes to enum values. [2/3]: - fix comment spacing [3/3] - better checks v1 -> v2 ======== I have been tinkering with the v1 patchset and have rewrote the v2 patches a couple of times. I believe the current patchset is still not perfect and would appreciate some reviews. I have another patch or two written but they do not make any significant changes to the current patchset. [1/3]: - use virBitmap instead of an array of strings for statistics. - add enums for the stat names and add qemuMonitorQueryStatsNameTypeToString to switch between the "ToString" functions based on the target type. - change qemuMonitorQueryStatsProviderNew to a variadic function that takes stat enum values with the sentinel value being -1. [2/3]: - No changes [3/3]: - Add relevant monitor related checks to check if the domain is active. - Acquire and release qemuMonitorObj lock before and after calling qemuMonitorQueryStats respectively. - Add the check for privileged access. Relevant QEMU patches can be found here: https://lore.kernel.org/all/20220530150714.756954-1-pbonzini@redhat.com/ This patchset is part of the 2022 GSOC contributor project. Amneesh Singh (3): qemu_monitor: add qemuMonitorQueryStats qemu_capabilities: add "query-stats" QMP command to the QEMU capabilities qemu_driver: use qemuMonitorQueryStats to extract halt poll time src/qemu/qemu_capabilities.c | 4 + src/qemu/qemu_capabilities.h | 3 + src/qemu/qemu_driver.c | 81 +++++++++++- src/qemu/qemu_monitor.c | 117 ++++++++++++++++++ src/qemu/qemu_monitor.h | 48 +++++++ src/qemu/qemu_monitor_json.c | 91 ++++++++++++++ src/qemu/qemu_monitor_json.h | 6 + .../caps_7.1.0.x86_64.xml | 1 + 8 files changed, 345 insertions(+), 6 deletions(-) -- 2.37.1

Related: https://gitlab.com/libvirt/libvirt/-/issues/276 This patch adds an API for the "query-stats" QMP command. The query returns a JSON containing the statistics based on the target, which can either be vCPU or VM, and the providers. The API deserializes the query result into an array of GHashMaps, which can later be used to extract all the query statistics. GHashMaps are used to avoid traversing the entire array to find the statistics you are looking for. This would be a singleton array if the target is a VM since the returned JSON is also a singleton array in that case. Signed-off-by: Amneesh Singh <natto@weirdnatto.in> --- src/qemu/qemu_monitor.c | 117 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 48 ++++++++++++++ src/qemu/qemu_monitor_json.c | 91 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 6 ++ 4 files changed, 262 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 4739810c9b..1488f7481f 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4297,3 +4297,120 @@ qemuMonitorGetMigrationBlockers(qemuMonitor *mon, return qemuMonitorJSONGetMigrationBlockers(mon, blockers); } + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsTarget, + QEMU_MONITOR_QUERY_STATS_TARGET_LAST, + "vm", + "vcpu", +); + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsName, + QEMU_MONITOR_QUERY_STATS_NAME_LAST, + "halt_poll_success_ns", + "halt_poll_fail_ns", +); + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsProvider, + QEMU_MONITOR_QUERY_STATS_PROVIDER_LAST, + "kvm", +); + + +void +qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider) +{ + virBitmapFree(provider->names); + g_free(provider); +} + + +qemuMonitorQueryStatsProvider * +qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsProviderType provider_type, + ...) +{ + qemuMonitorQueryStatsProvider *provider = g_new0(qemuMonitorQueryStatsProvider, 1); + qemuMonitorQueryStatsNameType stat; + va_list name_list; + + /* + * This can be lowered later in case of the enum getting quite large, hence + * the virBitmapSetExpand below which also incidently makes this function + * non-fallible. + */ + provider->names = virBitmapNew(QEMU_MONITOR_QUERY_STATS_NAME_LAST); + provider->type = provider_type; + + va_start(name_list, provider_type); + + while ((stat = va_arg(name_list, qemuMonitorQueryStatsNameType)) != + QEMU_MONITOR_QUERY_STATS_NAME_LAST) + virBitmapSetBitExpand(provider->names, stat); + + va_end(name_list); + + return provider; +} + + +virJSONValue * +qemuMonitorQueryStats(qemuMonitor *mon, + qemuMonitorQueryStatsTargetType target, + char **vcpus, + GPtrArray *providers) +{ + VIR_DEBUG("target=%u vcpus=%p providers=%p", target, vcpus, providers); + + QEMU_CHECK_MONITOR_NULL(mon); + + if (target != QEMU_MONITOR_QUERY_STATS_TARGET_VCPU && vcpus) + return NULL; + + return qemuMonitorJSONQueryStats(mon, target, vcpus, providers); +} + + +/** + * qemuMonitorExtractQueryStats: + * @info: One of the array members returned by qemuMonitorQueryStat + * + * Converts all the statistics into a GHashTable similar to virQEMU + * except only object with the key "value" is stored as the value i + * + * Returns NULL on failure. + */ +GHashTable * +qemuMonitorExtractQueryStats(virJSONValue *info) +{ + g_autoptr(GHashTable) hash_table = NULL; + virJSONValue *stats = NULL; + size_t i; + + if (!virJSONValueIsObject(info)) + return NULL; + + stats = virJSONValueObjectGetArray(info, "stats"); + hash_table = virHashNew(virJSONValueHashFree); + + for (i = 0; i < virJSONValueArraySize(stats); i++) { + virJSONValue *stat = virJSONValueArrayGet(stats, i); + virJSONValue *value = NULL; + const char *name = NULL; + + if (!virJSONValueIsObject(stat)) + continue; + + name = virJSONValueObjectGetString(stat, "name"); + if (!name) + continue; + + if (virJSONValueObjectRemoveKey(stat, "value", &value) < 0) + continue; + + virHashAddEntry(hash_table, name, value); + } + + return g_steal_pointer(&hash_table); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 78e2ebf0bd..63269e15bc 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1481,3 +1481,51 @@ qemuMonitorMigrateRecover(qemuMonitor *mon, int qemuMonitorGetMigrationBlockers(qemuMonitor *mon, char ***blockers); + +typedef enum { + QEMU_MONITOR_QUERY_STATS_TARGET_VM, + QEMU_MONITOR_QUERY_STATS_TARGET_VCPU, + QEMU_MONITOR_QUERY_STATS_TARGET_LAST, +} qemuMonitorQueryStatsTargetType; + +VIR_ENUM_DECL(qemuMonitorQueryStatsTarget); + +typedef enum { + QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_SUCCESS_NS, + QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_FAIL_NS, + QEMU_MONITOR_QUERY_STATS_NAME_LAST, +} qemuMonitorQueryStatsNameType; + +VIR_ENUM_DECL(qemuMonitorQueryStatsName); + +typedef enum { + QEMU_MONITOR_QUERY_STATS_PROVIDER_KVM, + QEMU_MONITOR_QUERY_STATS_PROVIDER_LAST, +} qemuMonitorQueryStatsProviderType; + +VIR_ENUM_DECL(qemuMonitorQueryStatsProvider); + +typedef struct _qemuMonitorQueryStatsProvider qemuMonitorQueryStatsProvider; +struct _qemuMonitorQueryStatsProvider { + qemuMonitorQueryStatsProviderType type; + virBitmap *names; +}; + +void +qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider); + +qemuMonitorQueryStatsProvider * +qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsProviderType provider_type, + ...); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMonitorQueryStatsProvider, + qemuMonitorQueryStatsProviderFree); + +virJSONValue * +qemuMonitorQueryStats(qemuMonitor *mon, + qemuMonitorQueryStatsTargetType target, + char **vcpus, + GPtrArray *providers); + +GHashTable * +qemuMonitorExtractQueryStats(virJSONValue *info); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d664e827dd..70fba50e6c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -8574,3 +8574,94 @@ qemuMonitorJSONMigrateRecover(qemuMonitor *mon, return qemuMonitorJSONCheckError(cmd, reply); } + + +/** + * qemuMonitorJSONQueryStats: + * @mon: monitor object + * @target: the target type for the query + * @vcpus: a list of vCPU QOM paths for filtering the statistics + * @providers: an array of providers to filter statistics + * + * @vcpus is a NULL terminated array of strings. @providers is a GPtrArray + * for qemuMonitorQueryStatsProvider. + * @vcpus and @providers are optional and can be NULL. + * + * Queries for the @target based statistics. + * Returns NULL on failure. + */ +virJSONValue * +qemuMonitorJSONQueryStats(qemuMonitor *mon, + qemuMonitorQueryStatsTargetType target, + char **vcpus, + GPtrArray *providers) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + g_autoptr(virJSONValue) vcpu_list = NULL; + g_autoptr(virJSONValue) provider_list = NULL; + + size_t i; + + if (providers) { + provider_list = virJSONValueNewArray(); + + for (i = 0; i < providers->len; i++) { + g_autoptr(virJSONValue) provider_obj = virJSONValueNewObject(); + qemuMonitorQueryStatsProvider *provider = providers->pdata[i]; + const char *type_str = qemuMonitorQueryStatsProviderTypeToString(provider->type); + virBitmap *names = provider->names; + int rc; + + rc = virJSONValueObjectAppendString(provider_obj, "provider", type_str); + + if (rc < 0) + return NULL; + + if (!virBitmapIsAllClear(names)) { + g_autoptr(virJSONValue) provider_names = virJSONValueNewArray(); + ssize_t curBit = -1; + + while ((curBit = virBitmapNextSetBit(names, curBit)) != -1) { + const char *name = qemuMonitorQueryStatsNameTypeToString(curBit); + + if (virJSONValueArrayAppendString(provider_names, name) < 0) + return NULL; + } + + rc = virJSONValueObjectAppend(provider_obj, "names", &provider_names); + + if (rc < 0) + return NULL; + } + + if (virJSONValueArrayAppend(provider_list, &provider_obj) < 0) + return NULL; + } + } + + if (vcpus) { + vcpu_list = virJSONValueNewArray(); + + for (i = 0; vcpus[i]; i++) + if (virJSONValueArrayAppendString(vcpu_list, vcpus[i]) < 0) + return NULL; + } + + cmd = qemuMonitorJSONMakeCommand("query-stats", + "s:target", qemuMonitorQueryStatsTargetTypeToString(target), + "A:vcpus", &vcpu_list, + "A:providers", &provider_list, + NULL); + + if (!cmd) + return NULL; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return NULL; + + if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) + return NULL; + + return virJSONValueObjectStealArray(reply, "return"); +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 5154c195c9..a53e6423df 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -812,3 +812,9 @@ qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon, int qemuMonitorJSONMigrateRecover(qemuMonitor *mon, const char *uri); + +virJSONValue * +qemuMonitorJSONQueryStats(qemuMonitor *mon, + qemuMonitorQueryStatsTargetType target, + char **vcpus, + GPtrArray *providers); -- 2.37.1

On Thu, Aug 18, 2022 at 08:47:18AM +0530, Amneesh Singh wrote:
Related: https://gitlab.com/libvirt/libvirt/-/issues/276
This patch adds an API for the "query-stats" QMP command.
The query returns a JSON containing the statistics based on the target, which can either be vCPU or VM, and the providers. The API deserializes the query result into an array of GHashMaps, which can later be used to extract all the query statistics. GHashMaps are used to avoid traversing the entire array to find the statistics you are looking for. This would be a singleton array if the target is a VM since the returned JSON is also a singleton array in that case.
Signed-off-by: Amneesh Singh <natto@weirdnatto.in> --- src/qemu/qemu_monitor.c | 117 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 48 ++++++++++++++ src/qemu/qemu_monitor_json.c | 91 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 6 ++ 4 files changed, 262 insertions(+)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 4739810c9b..1488f7481f 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4297,3 +4297,120 @@ qemuMonitorGetMigrationBlockers(qemuMonitor *mon,
return qemuMonitorJSONGetMigrationBlockers(mon, blockers); } + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsTarget, + QEMU_MONITOR_QUERY_STATS_TARGET_LAST, + "vm", + "vcpu", +); + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsName, + QEMU_MONITOR_QUERY_STATS_NAME_LAST, + "halt_poll_success_ns", + "halt_poll_fail_ns", +); + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsProvider, + QEMU_MONITOR_QUERY_STATS_PROVIDER_LAST, + "kvm", +); + + +void +qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider) +{ + virBitmapFree(provider->names); + g_free(provider); +} + + +qemuMonitorQueryStatsProvider * +qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsProviderType provider_type, + ...) +{ + qemuMonitorQueryStatsProvider *provider = g_new0(qemuMonitorQueryStatsProvider, 1); + qemuMonitorQueryStatsNameType stat; + va_list name_list; + + /* + * This can be lowered later in case of the enum getting quite large, hence + * the virBitmapSetExpand below which also incidently makes this function + * non-fallible. + */ + provider->names = virBitmapNew(QEMU_MONITOR_QUERY_STATS_NAME_LAST); + provider->type = provider_type; + + va_start(name_list, provider_type); + + while ((stat = va_arg(name_list, qemuMonitorQueryStatsNameType)) != + QEMU_MONITOR_QUERY_STATS_NAME_LAST) + virBitmapSetBitExpand(provider->names, stat); + + va_end(name_list); + + return provider; +} + + +virJSONValue * +qemuMonitorQueryStats(qemuMonitor *mon, + qemuMonitorQueryStatsTargetType target, + char **vcpus, + GPtrArray *providers) +{ + VIR_DEBUG("target=%u vcpus=%p providers=%p", target, vcpus, providers); + + QEMU_CHECK_MONITOR_NULL(mon); + + if (target != QEMU_MONITOR_QUERY_STATS_TARGET_VCPU && vcpus) + return NULL; + + return qemuMonitorJSONQueryStats(mon, target, vcpus, providers); +} + + +/** + * qemuMonitorExtractQueryStats: + * @info: One of the array members returned by qemuMonitorQueryStat + * + * Converts all the statistics into a GHashTable similar to virQEMU + * except only object with the key "value" is stored as the value i + * + * Returns NULL on failure. + */ +GHashTable * +qemuMonitorExtractQueryStats(virJSONValue *info) +{ + g_autoptr(GHashTable) hash_table = NULL; + virJSONValue *stats = NULL; + size_t i; + + if (!virJSONValueIsObject(info)) + return NULL; + + stats = virJSONValueObjectGetArray(info, "stats");
I added a check that stats is not NULL here. Reviewed-by: Martin Kletzander <mkletzan@redhat.com>

Related: https://gitlab.com/libvirt/libvirt/-/issues/276 Signed-off-by: Amneesh Singh <natto@weirdnatto.in> --- src/qemu/qemu_capabilities.c | 4 ++++ src/qemu/qemu_capabilities.h | 3 +++ tests/qemucapabilitiesdata/caps_7.1.0.x86_64.xml | 1 + 3 files changed, 8 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 4fca774425..f7986ef938 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -673,6 +673,9 @@ VIR_ENUM_IMPL(virQEMUCaps, "iothread.thread-pool-max", /* QEMU_CAPS_IOTHREAD_THREAD_POOL_MAX */ "usb-host.guest-resets-all", /* QEMU_CAPS_USB_HOST_GUESTS_RESETS_ALL */ "migration.blocked-reasons", /* QEMU_CAPS_MIGRATION_BLOCKED_REASONS */ + + /* 435 */ + "query-stats", /* QEMU_CAPS_QUERY_STATS */ ); @@ -1223,6 +1226,7 @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = { { "query-dirty-rate", QEMU_CAPS_QUERY_DIRTY_RATE }, { "sev-inject-launch-secret", QEMU_CAPS_SEV_INJECT_LAUNCH_SECRET }, { "calc-dirty-rate", QEMU_CAPS_CALC_DIRTY_RATE }, + { "query-stats", QEMU_CAPS_QUERY_STATS }, }; struct virQEMUCapsStringFlags virQEMUCapsMigration[] = { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index fc8bb6e2ab..cccb3659aa 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -653,6 +653,9 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_USB_HOST_GUESTS_RESETS_ALL, /* -device usb-host.guest-resets-all */ QEMU_CAPS_MIGRATION_BLOCKED_REASONS, /* query-migrate returns 'blocked-reasons */ + /* 435 */ + QEMU_CAPS_QUERY_STATS, /* accepts query-stats */ + QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_7.1.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_7.1.0.x86_64.xml index ad43385594..7f17b2360a 100644 --- a/tests/qemucapabilitiesdata/caps_7.1.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_7.1.0.x86_64.xml @@ -225,6 +225,7 @@ <flag name='iothread.thread-pool-max'/> <flag name='usb-host.guest-resets-all'/> <flag name='migration.blocked-reasons'/> + <flag name='query-stats'/> <version>7000050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100244</microcodeVersion> -- 2.37.1

On Thu, Aug 18, 2022 at 08:47:19AM +0530, Amneesh Singh wrote:
Related: https://gitlab.com/libvirt/libvirt/-/issues/276
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>

This patch uses qemuMonitorQueryStats to query "halt_poll_success_ns" and "halt_poll_fail_ns" for every vCPU. The respective values for each vCPU are then added together. Signed-off-by: Amneesh Singh <natto@weirdnatto.in> --- src/qemu/qemu_driver.c | 81 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 333225dd21..6e2bef26aa 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17769,15 +17769,84 @@ qemuDomainGetStatsCpuCgroup(virDomainObj *dom, return 0; } +static int +qemuDomainGetStatsCpuHaltPollTimeFromStats(virDomainObj *dom, + unsigned int privflags, + unsigned long long *haltPollSuccess, + unsigned long long *haltPollFail) +{ + qemuDomainObjPrivate *priv = dom->privateData; + qemuMonitorQueryStatsTargetType target = QEMU_MONITOR_QUERY_STATS_TARGET_VCPU; + qemuMonitorQueryStatsProvider *provider = NULL; + g_autoptr(GPtrArray) providers = NULL; + g_autoptr(virJSONValue) queried_stats = NULL; + const char *success_str = qemuMonitorQueryStatsNameTypeToString(QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_SUCCESS_NS); + const char *fail_str = qemuMonitorQueryStatsNameTypeToString(QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_FAIL_NS); + size_t i; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_STATS)) + return -1; + + if (!HAVE_JOB(privflags)) + return -1; + + provider = qemuMonitorQueryStatsProviderNew( + QEMU_MONITOR_QUERY_STATS_PROVIDER_KVM, + QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_SUCCESS_NS, + QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_FAIL_NS, + QEMU_MONITOR_QUERY_STATS_NAME_LAST); + + providers = g_ptr_array_new_full(1, (GDestroyNotify) qemuMonitorQueryStatsProviderFree); + g_ptr_array_add(providers, provider); + + qemuDomainObjEnterMonitor(dom); + queried_stats = qemuMonitorQueryStats(priv->mon, target, NULL, providers); + qemuDomainObjExitMonitor(dom); + + if (!queried_stats) + return -1; + + for (i = 0; i < virJSONValueArraySize(queried_stats); i++) { + unsigned long long curHaltPollSuccess, curHaltPollFail; + virJSONValue *success_obj, *fail_obj; + virJSONValue *obj = virJSONValueArrayGet(queried_stats, i); + g_autoptr(GHashTable) cur_table = qemuMonitorExtractQueryStats(obj); + + if (!cur_table) + return -1; + + success_obj = g_hash_table_lookup(cur_table, success_str); + fail_obj = g_hash_table_lookup(cur_table, fail_str); + + if (!success_obj || !fail_obj) + return -1; + + if (virJSONValueGetNumberUlong(success_obj, &curHaltPollSuccess) < 0 || + virJSONValueGetNumberUlong(fail_obj, &curHaltPollFail) < 0) + return -1; + + *haltPollSuccess += curHaltPollSuccess; + *haltPollFail += curHaltPollFail; + } + + return 0; +} + + static int qemuDomainGetStatsCpuHaltPollTime(virDomainObj *dom, - virTypedParamList *params) + virTypedParamList *params, + unsigned int privflags) { unsigned long long haltPollSuccess = 0; unsigned long long haltPollFail = 0; - pid_t pid = dom->pid; - if (virHostCPUGetHaltPollTime(pid, &haltPollSuccess, &haltPollFail) < 0) + if (!virDomainObjIsActive(dom)) + return 0; + + if (qemuDomainGetStatsCpuHaltPollTimeFromStats(dom, privflags, + &haltPollSuccess, &haltPollFail) < 0 && + virHostCPUGetHaltPollTime(dom->pid, &haltPollSuccess, &haltPollFail) < 0) return 0; if (virTypedParamListAddULLong(params, haltPollSuccess, "cpu.haltpoll.success.time") < 0 || @@ -17791,7 +17860,7 @@ static int qemuDomainGetStatsCpu(virQEMUDriver *driver, virDomainObj *dom, virTypedParamList *params, - unsigned int privflags G_GNUC_UNUSED) + unsigned int privflags) { if (qemuDomainGetStatsCpuCgroup(dom, params) < 0) return -1; @@ -17799,7 +17868,7 @@ qemuDomainGetStatsCpu(virQEMUDriver *driver, if (qemuDomainGetStatsCpuCache(driver, dom, params) < 0) return -1; - if (qemuDomainGetStatsCpuHaltPollTime(dom, params) < 0) + if (qemuDomainGetStatsCpuHaltPollTime(dom, params, privflags) < 0) return -1; return 0; @@ -18576,7 +18645,7 @@ static virQEMUCapsFlags queryDirtyRateRequired[] = { static struct qemuDomainGetStatsWorker qemuDomainGetStatsWorkers[] = { { qemuDomainGetStatsState, VIR_DOMAIN_STATS_STATE, false, NULL }, - { qemuDomainGetStatsCpu, VIR_DOMAIN_STATS_CPU_TOTAL, false, NULL }, + { qemuDomainGetStatsCpu, VIR_DOMAIN_STATS_CPU_TOTAL, true, NULL }, { qemuDomainGetStatsBalloon, VIR_DOMAIN_STATS_BALLOON, true, NULL }, { qemuDomainGetStatsVcpu, VIR_DOMAIN_STATS_VCPU, true, NULL }, { qemuDomainGetStatsInterface, VIR_DOMAIN_STATS_INTERFACE, false, NULL }, -- 2.37.1

On Thu, Aug 18, 2022 at 08:47:20AM +0530, Amneesh Singh wrote:
This patch uses qemuMonitorQueryStats to query "halt_poll_success_ns" and "halt_poll_fail_ns" for every vCPU. The respective values for each vCPU are then added together.
Signed-off-by: Amneesh Singh <natto@weirdnatto.in> --- src/qemu/qemu_driver.c | 81 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 6 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 333225dd21..6e2bef26aa 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17769,15 +17769,84 @@ qemuDomainGetStatsCpuCgroup(virDomainObj *dom, return 0; }
+static int +qemuDomainGetStatsCpuHaltPollTimeFromStats(virDomainObj *dom, + unsigned int privflags, + unsigned long long *haltPollSuccess, + unsigned long long *haltPollFail) +{ + qemuDomainObjPrivate *priv = dom->privateData; + qemuMonitorQueryStatsTargetType target = QEMU_MONITOR_QUERY_STATS_TARGET_VCPU; + qemuMonitorQueryStatsProvider *provider = NULL; + g_autoptr(GPtrArray) providers = NULL; + g_autoptr(virJSONValue) queried_stats = NULL; + const char *success_str = qemuMonitorQueryStatsNameTypeToString(QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_SUCCESS_NS); + const char *fail_str = qemuMonitorQueryStatsNameTypeToString(QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_FAIL_NS); + size_t i; +
I added a reset of the values to 0 here for good measure. Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
participants (2)
-
Amneesh Singh
-
Martin Kletzander