On Wed, Jul 22, 2026 at 13:52:12 -0500, Jaehoon Kim wrote:
Use the configured iothread poll-weight value when interacting with QEMU.
This wires the new setting into command line generation, monitor data parsing, driver-side parameter handling, and capability-based validation. Remove the break from the iothread validation loop because the loop now also checks poll-weight on every iothread.
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- include/libvirt/libvirt-domain.h | 26 +++++++++ src/qemu/qemu_command.c | 18 +++++- src/qemu/qemu_driver.c | 38 ++++++++++++ src/qemu/qemu_monitor.h | 2 + src/qemu/qemu_monitor_json.c | 18 ++++++ src/qemu/qemu_validate.c | 10 +++- ...othreads-ids-poll-weight.x86_64-11.0.0.err | 1 + ...threads-ids-poll-weight.x86_64-latest.args | 40 +++++++++++++ ...othreads-ids-poll-weight.x86_64-latest.xml | 58 +++++++++++++++++++ .../iothreads-ids-poll-weight.xml | 58 +++++++++++++++++++ tests/qemuxmlconftest.c | 2 + 11 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-11.0.0.err create mode 100644 tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/iothreads-ids-poll-weight.xml
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 5b67f8f897..f49aa1c136 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -2748,6 +2748,22 @@ int virDomainDelIOThread(virDomainPtr domain, */ # define VIR_DOMAIN_IOTHREAD_POLL_SHRINK "poll_shrink"
+/** + * VIR_DOMAIN_IOTHREAD_POLL_WEIGHT: + * + * This provides a shift value for the adaptive polling algorithm to control + * how much the most recent event interval affects the next polling duration + * calculation. Larger values decrease the weight of the current interval, + * enabling more gradual adjustments. Valid range is [0, 63]. A value of 0 + * lets the hypervisor select a default weight (typically 3, meaning the
Okay, so 0 is a special value. The parser doesn't treat it as a special value. With this impl it would mean that there are 2 manifestation of the same "hypervisor default" setting. One is if the 'weight' attribute is missing completely. Second one is if it's explicitly set to '0'. I'm not a fan of having 2 manifestations of the default but I don't have a better sugestion either.
+ * current interval contributes approximately 1/8 to the weighted average). + * + * Accepted type is VIR_TYPED_PARAM_UINT. + * + * Since: 12.6.0
This will have to be 12.7.0, the tree is in freeze for the 12.6.0 release already.
+ */ +# define VIR_DOMAIN_IOTHREAD_POLL_WEIGHT "poll_weight" + /** * VIR_DOMAIN_IOTHREAD_THREAD_POOL_MIN: * @@ -4330,6 +4346,16 @@ struct _virDomainStatsRecord { */ # define VIR_DOMAIN_STATS_IOTHREAD_SUFFIX_POLL_SHRINK ".poll-shrink"
+/** + * VIR_DOMAIN_STATS_IOTHREAD_SUFFIX_POLL_WEIGHT: + * + * Polling weight factor as an unsigned int. This shift value controls how + * much the most recent event interval affects adaptive polling calculations. + * A 0 (zero) indicates the hypervisor's default weight is used. + * + * Since: 12.6.0
ditto So here I presume the presence of this field will be based on whether qemu supports this feature (since the stats will be filled from the return from qemu) so 0 in fact can be shown even without config. That does make sense for the getter.
+ */ +# define VIR_DOMAIN_STATS_IOTHREAD_SUFFIX_POLL_WEIGHT ".poll-weight"
/** * VIR_DOMAIN_STATS_MEMORY_BANDWIDTH_MONITOR_COUNT: diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 95e2ea9a6b..b351059fc1 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7659,7 +7659,8 @@ qemuBuildMemCommandLine(virCommand *cmd,
static int qemuBuildIOThreadCommandLine(virCommand *cmd, - const virDomainDef *def) + const virDomainDef *def, + virQEMUCaps *qemuCaps) { size_t i;
@@ -7694,6 +7695,19 @@ qemuBuildIOThreadCommandLine(virCommand *cmd, NULL) < 0) return -1;
+ if (iothread->set_poll_weight) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_IOTHREAD_POLL_WEIGHT)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("poll-weight is not supported by this QEMU binary")); + return -1; + }
This check is dead code since qemuValidateDomainDefIOThreads is called before this happens in all cases.
+ + if (virJSONValueObjectAdd(&props, + "u:poll-weight", iothread->poll_weight, + NULL) < 0) + return -1; + } + if (qemuBuildObjectCommandlineFromJSON(cmd, props) < 0) return -1; }
[...]
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index bdc0cff66a..1afd6e83ba 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5261,6 +5261,11 @@ qemuDomainHotplugModIOThreadIDDef(virDomainIOThreadIDDef *def, def->set_poll_shrink = true; }
+ if (mondef.set_poll_weight) { + def->poll_weight = mondef.poll_weight; + def->set_poll_weight = true; + } + if (mondef.set_thread_pool_min) def->thread_pool_min = mondef.thread_pool_min;
@@ -5353,6 +5358,11 @@ qemuDomainHotplugDelIOThread(virDomainObj *vm, * necessary. If a 0 (zero) value is provided, QEMU resets the polling * interval to 0 (zero) allowing the poll-grow to manipulate the time. * + * - "poll-weight" - weight shift value used by the adaptive polling algorithm + * to determine how much the most recent event interval influences the + * next interval calculation. Accepted range is [0, 63]. If a 0 (zero) + * value is provided, QEMU uses its default weight. + * * QEMU keeps track of the polling time elapsed and may grow or shrink the * its polling interval based upon its heuristic algorithm. It is possible * that calculations determine that it has found a "sweet spot" and no @@ -5388,6 +5398,20 @@ qemuDomainIOThreadParseParams(virTypedParameterPtr params, if (rc == 1) iothread->set_poll_shrink = true;
+ if ((rc = virTypedParamsGetUInt(params, nparams, + VIR_DOMAIN_IOTHREAD_POLL_WEIGHT, + &iothread->poll_weight)) < 0) + return -1; + if (rc == 1) { + if (iothread->poll_weight > 63) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("poll-weight value %1$u is out of range [0, 63]"), + iothread->poll_weight); + return -1; + } + iothread->set_poll_weight = true; + } + if ((rc = virTypedParamsGetInt(params, nparams, VIR_DOMAIN_IOTHREAD_THREAD_POOL_MIN, &iothread->thread_pool_min)) < 0)
[...]
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c2afb580e4..e73be4e263 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1583,11 +1583,13 @@ struct _qemuMonitorIOThreadInfo { unsigned long long poll_max_ns; unsigned long long poll_grow; unsigned long long poll_shrink; + unsigned int poll_weight; int thread_pool_min; int thread_pool_max; bool set_poll_max_ns; bool set_poll_grow; bool set_poll_shrink; + bool set_poll_weight; bool set_thread_pool_min; bool set_thread_pool_max; }; diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 21f4d613b7..c88f74c10b 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -7202,6 +7202,11 @@ qemuMonitorJSONGetIOThreads(qemuMonitor *mon, virJSONValueObjectGetNumberUlong(child, "poll-shrink", &info->poll_shrink) == 0) info->poll_valid = true; + + /* poll-weight is optional, only present on newer QEMU */ + if (virJSONValueObjectGetNumberUint(child, "poll-weight", + &info->poll_weight) == 0) + info->set_poll_weight = true; }
*niothreads = n;
I'd prefer if this getter code changes are separated. The monitor can fetch the values and not use them. Maybe even the stats entries can be added before this is set.
@@ -7243,6 +7248,19 @@ qemuMonitorJSONSetIOThread(qemuMonitor *mon,
#undef VIR_IOTHREAD_SET_PROP_UL
+#define VIR_IOTHREAD_SET_PROP_UINT(propName, propVal) \ + if (iothreadInfo->set_##propVal) { \ + memset(&prop, 0, sizeof(prop)); \ + prop.type = QEMU_MONITOR_OBJECT_PROPERTY_UINT; \ + prop.val.ui = iothreadInfo->propVal; \ + if (qemuMonitorJSONSetObjectProperty(mon, path, propName, &prop) < 0) \ + return -1; \ + } + + VIR_IOTHREAD_SET_PROP_UINT("poll-weight", poll_weight); + +#undef VIR_IOTHREAD_SET_PROP_UINT + if (iothreadInfo->set_thread_pool_min && iothreadInfo->set_thread_pool_max) { int curr_max = -1; diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 439d4b1916..5a770a800f 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -500,9 +500,15 @@ qemuValidateDomainDefIOThreads(const virDomainDef *def, for (i = 0; i < def->niothreadids; i++) { virDomainIOThreadIDDef *iothread = def->iothreadids[i];
- if (iothread->thread_pool_min != -1 || iothread->thread_pool_max != -1) { + if (iothread->thread_pool_min != -1 || iothread->thread_pool_max != -1) needsThreadPoolCap = true; - break; + + /* poll-weight requires QEMU_CAPS_IOTHREAD_POLL_WEIGHT */ + if (iothread->set_poll_weight && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_IOTHREAD_POLL_WEIGHT)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("poll-weight is not supported by this QEMU binary")); + return -1; } }
[...]
diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index df0b257cbe..682aaad74f 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -2333,6 +2333,8 @@ mymain(void) DO_TEST_CAPS_LATEST("iothreads-ids"); DO_TEST_CAPS_LATEST("iothreads-ids-partial"); DO_TEST_CAPS_LATEST("iothreads-ids-pool-sizes"); + DO_TEST_CAPS_LATEST("iothreads-ids-poll-weight"); + DO_TEST_CAPS_VER_PARSE_ERROR("iothreads-ids-poll-weight", "11.0.0");
I'd not bother with the negative case that just depends on capability. A more interesting (but not strictly required) one would be with out of range value.