On Thu, Sep 04, 2025 at 16:46:55 +0200, Jiri Denemark via Devel wrote:
> From: Jiri Denemark <[email protected]>
>
> qom-list-get is a new QMP command (since QEMU 10.1) that combines
> qom-list for listing properties of a specified object with qom-get for
> getting a value of a given property. The new command provides an array
> of all properties and their values, which allows us to dramatically
> reduce the number of QMP commands we have to call when starting a domain
> to check which CPU features were actually enabled.
>
> A simple domain with no disk can now be started with only 15 QMP
> commands in about 200 ms compared to 485 commands and 400 ms startup
> time without this patch.
>
> https://issues.redhat.com/browse/RHEL-7038
>
> Signed-off-by: Jiri Denemark <[email protected]>
> ---
> src/qemu/qemu_capabilities.c | 2 +
> src/qemu/qemu_capabilities.h | 1 +
> src/qemu/qemu_monitor.c | 13 +++-
> src/qemu/qemu_monitor.h | 1 +
> src/qemu/qemu_monitor_json.c | 73 ++++++++++++++++---
> src/qemu/qemu_monitor_json.h | 7 +-
> src/qemu/qemu_process.c | 1 +
> .../caps_10.1.0_x86_64.xml | 1 +
> .../caps_10.2.0_x86_64.xml | 1 +
> ...=> get-guest-cpu-SierraForest-legacy.json} | 0
> ...> get-guest-cpu-SkylakeClient-legacy.json} | 0
> tests/qemumonitorjsontest.c | 18 +++--
> 12 files changed, 92 insertions(+), 26 deletions(-)
> rename tests/qemumonitorjsondata/{get-guest-cpu-SierraForest.json =>
> get-guest-cpu-SierraForest-legacy.json} (100%)
> rename tests/qemumonitorjsondata/{get-guest-cpu-SkylakeClient.json =>
> get-guest-cpu-SkylakeClient-legacy.json} (100%)
>
> diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
> index 688d100b01..b7174c657d 100644
> --- a/src/qemu/qemu_capabilities.c
> +++ b/src/qemu/qemu_capabilities.c
> @@ -742,6 +742,7 @@ VIR_ENUM_IMPL(virQEMUCaps,
> "amd-iommu.pci-id", /* QEMU_CAPS_AMD_IOMMU_PCI_ID */
> "usb-bot", /* QEMU_CAPS_DEVICE_USB_BOT */
> "tdx-guest", /* QEMU_CAPS_TDX_GUEST */
> + "qom-list-get", /* QEMU_CAPS_QOM_LIST_GET */
> );
>
>
> @@ -1256,6 +1257,7 @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = {
> { "query-stats-schemas", QEMU_CAPS_QUERY_STATS_SCHEMAS },
> { "display-reload", QEMU_CAPS_DISPLAY_RELOAD },
> { "blockdev-set-active", QEMU_CAPS_BLOCKDEV_SET_ACTIVE },
> + { "qom-list-get", QEMU_CAPS_QOM_LIST_GET },
> };
>
> struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = {
> diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
> index 8916973364..f50d908b3f 100644
> --- a/src/qemu/qemu_capabilities.h
> +++ b/src/qemu/qemu_capabilities.h
> @@ -723,6 +723,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for
> syntax-check */
> QEMU_CAPS_AMD_IOMMU_PCI_ID, /* amd-iommu.pci-id */
> QEMU_CAPS_DEVICE_USB_BOT, /* -device usb-bot */
> QEMU_CAPS_TDX_GUEST, /* -object tdx-guest,... */
> + QEMU_CAPS_QOM_LIST_GET, /* qom-list-get QMP command */
>
> QEMU_CAPS_LAST /* this must always be the last item */
> } virQEMUCapsFlags;
Preferrably introduce new capabilities in separate patch.
> diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
> index 0213bd5af8..176651eab4 100644
> --- a/src/qemu/qemu_monitor.c
> +++ b/src/qemu/qemu_monitor.c
> @@ -3673,6 +3673,8 @@ qemuMonitorSetDomainLog(qemuMonitor *mon,
> * qemuMonitorGetGuestCPU:
> * @mon: Pointer to the monitor
> * @arch: CPU architecture
> + * @qomListGet: QEMU supports getting list of features and their values using
> + * a single qom-list-get QMP command
> * @cpuQOMPath: QOM path of a CPU to probe
> * @translate: callback for translating CPU feature names from QEMU to
> libvirt
> * @opaque: data for @translate callback
> @@ -3687,13 +3689,16 @@ qemuMonitorSetDomainLog(qemuMonitor *mon,
> int
> qemuMonitorGetGuestCPU(qemuMonitor *mon,
> virArch arch,
> + bool qomListGet,
> const char *cpuQOMPath,
> qemuMonitorCPUFeatureTranslationCallback translate,
> virCPUData **enabled,
> virCPUData **disabled)
> {
> - VIR_DEBUG("arch=%s cpuQOMPath=%s translate=%p enabled=%p disabled=%p",
> - virArchToString(arch), cpuQOMPath, translate, enabled,
> disabled);
> + VIR_DEBUG("arch=%s qomListGet%d cpuQOMPath=%s translate=%p "
qomListGet=%d instead of qomListGet%d
> + "enabled=%p disabled=%p",
> + virArchToString(arch), qomListGet, cpuQOMPath, translate,
> + enabled, disabled);
>
> QEMU_CHECK_MONITOR(mon);
>
[...]
> @@ -6631,14 +6648,29 @@ qemuMonitorJSONGetCPUProperties(qemuMonitor *mon,
> virJSONValue *array;
> struct _qemuMonitorJSONCPUPropsFilterData filterData = {
> .mon = mon,
> + .values = qomListGet,
> .cpuQOMPath = cpuQOMPath,
> };
>
> *props = NULL;
>
> - if (!(cmd = qemuMonitorJSONMakeCommand("qom-list",
> - "s:path", cpuQOMPath,
> - NULL)))
> + if (qomListGet) {
> + g_autoptr(virJSONValue) paths = virJSONValueNewArray();
> + g_autoptr(virJSONValue) path =
> virJSONValueNewString(g_strdup(cpuQOMPath));
> +
> + if (virJSONValueArrayAppend(paths, &path) < 0)
> + return -1;
How about 'virJSONValueArrayAppendString'? instead of the manual
construction?
> +
> + cmd = qemuMonitorJSONMakeCommand("qom-list-get",
> + "a:paths", &paths,
> + NULL);
> + } else {
> + cmd = qemuMonitorJSONMakeCommand("qom-list",
> + "s:path", cpuQOMPath,
> + NULL);
> + }
[...]
> diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
> index f076e637ba..f17769f7fe 100644
> --- a/src/qemu/qemu_monitor_json.h
> +++ b/src/qemu/qemu_monitor_json.h
> @@ -558,15 +558,10 @@ int
> qemuMonitorJSONGetDeviceAliases(qemuMonitor *mon,
> char ***aliases);
>
> -int
> -qemuMonitorJSONGetGuestCPUx86(qemuMonitor *mon,
> - const char *cpuQOMPath,
> - virCPUData **data,
> - virCPUData **disabled);
> -
Leftover from 1/15?
> int
> qemuMonitorJSONGetGuestCPU(qemuMonitor *mon,
> virArch arch,
> + bool qomListGet,
> const char *cpuQOMPath,
> qemuMonitorCPUFeatureTranslationCallback
> translate,
> virCPUData **enabled,
[...]
> --- a/tests/qemumonitorjsontest.c
> +++ b/tests/qemumonitorjsontest.c
> @@ -2773,6 +2773,7 @@ testQemuMonitorJSONGetSEVInfo(const void *opaque)
>
> struct testQemuMonitorJSONGetGuestCPUData {
> const char *name;
> + bool qomListGet;
> virQEMUDriver driver;
> GHashTable *schema;
> };
> @@ -2791,8 +2792,9 @@ testQemuMonitorJSONGetGuestCPU(const void *opaque)
> g_autofree char *enabled = NULL;
> g_autofree char *disabled = NULL;
> bool failed = false;
> + const char *legacy = data->qomListGet ? "" : "-legacy";
>
> - fileJSON = g_strdup_printf("%s-%s.json", base, data->name);
> + fileJSON = g_strdup_printf("%s-%s%s.json", base, data->name, legacy);
> fileEnabled = g_strdup_printf("%s-%s-enabled.xml", base, data->name);
> fileDisabled = g_strdup_printf("%s-%s-disabled.xml", base, data->name);
>
> @@ -2802,6 +2804,7 @@ testQemuMonitorJSONGetGuestCPU(const void *opaque)
>
> if (qemuMonitorJSONGetGuestCPU(qemuMonitorTestGetMonitor(mon),
> VIR_ARCH_X86_64,
> + data->qomListGet,
> "/machine/unattached/device[0]",
> virQEMUCapsCPUFeatureFromQEMU,
> &dataEnabled, &dataDisabled) < 0)
> @@ -2884,11 +2887,14 @@ mymain(void)
> ret = -1; \
> } while (0)
>
> -#define DO_TEST_GET_GUEST_CPU(name) \
> +#define DO_TEST_GET_GUEST_CPU(name, qomListGet) \
> do { \
> struct testQemuMonitorJSONGetGuestCPUData data = { \
> - name, driver, qapiData.schema }; \
> - if (virTestRun("GetGuestCPU(" name ")", \
> + name, qomListGet, driver, qapiData.schema }; \
> + g_autofree char *label = NULL; \
> + label = g_strdup_printf("GetGuestCPU(%s, legacy=%s)", \
> + name, qomListGet ? "false" : "true"); \
Not converting it to a string can save you the ternary use.
With the caps separated and the nits addressed:
Reviewed-by: Peter Krempa <[email protected]>