From: Jiri Denemark Date: Mon, 25 Aug 2025 14:16:49 +0000 (+0200) Subject: qemu: Use qom-list-get for checking enabled CPU features X-Git-Tag: v11.8.0-rc1~93 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b21b21192316f47da6a600ccf3c28158cc8fedbb;p=thirdparty%2Flibvirt.git qemu: Use qom-list-get for checking enabled CPU features 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 Reviewed-by: Peter Krempa Reviewed-by: Ján Tomko --- diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 0213bd5af8..eec6905e9f 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 " + "enabled=%p disabled=%p", + virArchToString(arch), qomListGet, cpuQOMPath, translate, + enabled, disabled); QEMU_CHECK_MONITOR(mon); @@ -3701,8 +3706,8 @@ qemuMonitorGetGuestCPU(qemuMonitor *mon, if (disabled) *disabled = NULL; - return qemuMonitorJSONGetGuestCPU(mon, arch, cpuQOMPath, translate, - enabled, disabled); + return qemuMonitorJSONGetGuestCPU(mon, arch, qomListGet, cpuQOMPath, + translate, enabled, disabled); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 689a587ec6..750e7444fc 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1268,6 +1268,7 @@ typedef const char *(*qemuMonitorCPUFeatureTranslationCallback)(virArch arch, int qemuMonitorGetGuestCPU(qemuMonitor *mon, virArch arch, + bool qomListGet, const char *cpuQOMPath, qemuMonitorCPUFeatureTranslationCallback translate, virCPUData **enabled, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 76118ea664..7ef065ba45 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -6596,6 +6596,7 @@ qemuMonitorJSONGetDeviceAliases(qemuMonitor *mon, struct _qemuMonitorJSONCPUPropsFilterData { qemuMonitor *mon; + bool values; const char *cpuQOMPath; }; @@ -6604,17 +6605,32 @@ qemuMonitorJSONCPUPropsFilter(const char *name, virJSONValue *propData, void *opaque) { - qemuMonitorJSONObjectProperty prop = { .type = QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN }; struct _qemuMonitorJSONCPUPropsFilterData *data = opaque; + bool enabled = false; if (STRNEQ_NULLABLE(virJSONValueObjectGetString(propData, "type"), "bool")) return 1; - if (qemuMonitorJSONGetObjectProperty(data->mon, data->cpuQOMPath, - name, &prop) < 0) - return -1; + if (data->values) { + if (virJSONValueObjectGetBoolean(propData, "value", &enabled) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("property '%1$s' in reply data was missing value"), + name); + return -1; + } + } else { + qemuMonitorJSONObjectProperty prop = { + .type = QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN + }; + + if (qemuMonitorJSONGetObjectProperty(data->mon, data->cpuQOMPath, + name, &prop) < 0) + return -1; - if (!prop.val.b) + enabled = prop.val.b; + } + + if (!enabled) return 1; return 0; @@ -6623,6 +6639,7 @@ qemuMonitorJSONCPUPropsFilter(const char *name, static int qemuMonitorJSONGetCPUProperties(qemuMonitor *mon, + bool qomListGet, const char *cpuQOMPath, char ***props) { @@ -6631,14 +6648,28 @@ 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(); + + if (virJSONValueArrayAppendString(paths, cpuQOMPath) < 0) + return -1; + + cmd = qemuMonitorJSONMakeCommand("qom-list-get", + "a:paths", &paths, + NULL); + } else { + cmd = qemuMonitorJSONMakeCommand("qom-list", + "s:path", cpuQOMPath, + NULL); + } + + if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) @@ -6650,6 +6681,22 @@ qemuMonitorJSONGetCPUProperties(qemuMonitor *mon, if (!(array = qemuMonitorJSONGetReply(cmd, reply, VIR_JSON_TYPE_ARRAY))) return -1; + if (qomListGet) { + if (virJSONValueArraySize(array) != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'qom-list-get' returned unexpected number of paths")); + return -1; + } + + array = virJSONValueObjectGetArray(virJSONValueArrayGet(array, 0), + "properties"); + if (!array) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("reply data was missing 'properties' array")); + return -1; + } + } + return qemuMonitorJSONParsePropsList(array, qemuMonitorJSONCPUPropsFilter, &filterData, props); } @@ -6657,6 +6704,7 @@ qemuMonitorJSONGetCPUProperties(qemuMonitor *mon, static int qemuMonitorJSONGetCPUData(qemuMonitor *mon, + bool qomListGet, const char *cpuQOMPath, qemuMonitorCPUFeatureTranslationCallback translate, virCPUData *data) @@ -6664,7 +6712,7 @@ qemuMonitorJSONGetCPUData(qemuMonitor *mon, g_auto(GStrv) props = NULL; char **p; - if (qemuMonitorJSONGetCPUProperties(mon, cpuQOMPath, &props) < 0) + if (qemuMonitorJSONGetCPUProperties(mon, qomListGet, cpuQOMPath, &props) < 0) return -1; for (p = props; p && *p; p++) { @@ -6712,6 +6760,8 @@ qemuMonitorJSONGetCPUDataDisabled(qemuMonitor *mon, * qemuMonitorJSONGetGuestCPU: * @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 @@ -6726,6 +6776,7 @@ qemuMonitorJSONGetCPUDataDisabled(qemuMonitor *mon, int qemuMonitorJSONGetGuestCPU(qemuMonitor *mon, virArch arch, + bool qomListGet, const char *cpuQOMPath, qemuMonitorCPUFeatureTranslationCallback translate, virCPUData **enabled, @@ -6738,7 +6789,8 @@ qemuMonitorJSONGetGuestCPU(qemuMonitor *mon, !(cpuDisabled = virCPUDataNew(arch))) return -1; - if (qemuMonitorJSONGetCPUData(mon, cpuQOMPath, translate, cpuEnabled) < 0) + if (qemuMonitorJSONGetCPUData(mon, qomListGet, cpuQOMPath, + translate, cpuEnabled) < 0) return -1; if (disabled && diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 62050470e8..f17769f7fe 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -561,6 +561,7 @@ qemuMonitorJSONGetDeviceAliases(qemuMonitor *mon, int qemuMonitorJSONGetGuestCPU(qemuMonitor *mon, virArch arch, + bool qomListGet, const char *cpuQOMPath, qemuMonitorCPUFeatureTranslationCallback translate, virCPUData **enabled, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 2988ffb157..ead5bf3e48 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4592,6 +4592,7 @@ qemuProcessFetchGuestCPU(virDomainObj *vm, rc = qemuMonitorGetGuestCPU(priv->mon, vm->def->os.arch, + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QOM_LIST_GET), cpuQOMPath, virQEMUCapsCPUFeatureFromQEMU, &dataEnabled, &dataDisabled); diff --git a/tests/qemumonitorjsondata/get-guest-cpu-SierraForest.json b/tests/qemumonitorjsondata/get-guest-cpu-SierraForest-legacy.json similarity index 100% rename from tests/qemumonitorjsondata/get-guest-cpu-SierraForest.json rename to tests/qemumonitorjsondata/get-guest-cpu-SierraForest-legacy.json diff --git a/tests/qemumonitorjsondata/get-guest-cpu-SkylakeClient.json b/tests/qemumonitorjsondata/get-guest-cpu-SkylakeClient-legacy.json similarity index 100% rename from tests/qemumonitorjsondata/get-guest-cpu-SkylakeClient.json rename to tests/qemumonitorjsondata/get-guest-cpu-SkylakeClient-legacy.json diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index c3e83fc728..02684b538d 100644 --- 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,13 @@ 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=%d)", name, qomListGet); \ + if (virTestRun(label, \ testQemuMonitorJSONGetGuestCPU, \ &data) < 0) \ ret = -1; \ @@ -2984,8 +2989,8 @@ mymain(void) DO_TEST_CPU_INFO("s390", 2); - DO_TEST_GET_GUEST_CPU("SierraForest"); - DO_TEST_GET_GUEST_CPU("SkylakeClient"); + DO_TEST_GET_GUEST_CPU("SierraForest", false); + DO_TEST_GET_GUEST_CPU("SkylakeClient", false); #define DO_TEST_QAPI_QUERY(nme, qry, scc, rplobj) \