[PATCH 0/6] qemu: add poll-weight support for iothreads
This series adds support for the QEMU iothread poll-weight property, which controls the weight of the most recent event interval in the adaptive polling duration calculation. The valid range is [0, 63]; a value of 0 lets QEMU use its default weight. The series is split by layer: 1) conf/schema support 2) qemu capability detection 3) qemu backend support + public API constants + xmlconf tests 4) virsh support + test driver + virshtest 5) docs: fix pre-existing typos in iothread poll element description 6) docs: formatdomain documentation for poll-weight The series was validated with: meson test -C build virschematest meson test -C build qemucapabilitiestest meson test -C build qemuxmlconftest meson test -C build virshtest meson test -C build genericxml2xmltest QEMU commits: 9fea80ddf0 qapi/iothread: introduce poll-weight parameter for aio-poll 9563d0b5e2 aio-poll: refine iothread polling using weighted handler intervals ed21d9d65c aio-poll: avoid unnecessary polling time computation Jaehoon Kim (6): conf: add iothread poll-weight XML support qemu: capabilities: add capability for iothread poll-weight qemu: support iothread poll-weight virsh: add support for iothread poll-weight docs: fix typos in iothread poll element description docs: formatdomain: document iothread poll-weight docs/formatdomain.rst | 14 +++-- docs/manpages/virsh.rst | 18 ++++-- include/libvirt/libvirt-domain.h | 26 +++++++++ src/conf/domain_conf.c | 14 ++++- src/conf/domain_conf.h | 2 + src/conf/schemas/domaincommon.rng | 7 +++ src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + 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 +++- src/test/test_driver.c | 12 ++++ tests/genericxml2xmlindata/iothreadids.xml | 2 +- .../caps_11.1.0_aarch64.xml | 1 + .../caps_11.1.0_x86_64.xml | 1 + ...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 + tests/virshtestdata/iothreads.in | 2 + tests/virshtestdata/iothreads.out | 18 ++++++ tools/virsh-domain.c | 18 ++++++ 25 files changed, 367 insertions(+), 16 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 -- 2.54.0
Add support for the iothread poll-weight XML attribute. Store the value in the internal iothread definition, parse it from the <poll/> element, and format it back into domain XML. Also extend the schema to accept the new attribute and validate the accepted range of [0, 63]. Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- src/conf/domain_conf.c | 14 +++++++++++++- src/conf/domain_conf.h | 2 ++ src/conf/schemas/domaincommon.rng | 7 +++++++ tests/genericxml2xmlindata/iothreadids.xml | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 17c4a57cd8..f69317c36f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -16984,7 +16984,9 @@ virDomainDefParseBootXML(xmlXPathContextPtr ctxt, * * <iothreads>4</iothreads> * <iothreadids> - * <iothread id='1' thread_pool_min="0" thread_pool_max="60"/> + * <iothread id='1' thread_pool_min="0" thread_pool_max="60"> + * <poll max='32000' grow='2' shrink='2' weight='3'/> + * </iothread> * <iothread id='3'/> * <iothread id='5'/> * <iothread id='7'/> @@ -17032,6 +17034,12 @@ virDomainIOThreadIDDefParseXML(xmlNodePtr node) return NULL; iothrid->set_poll_shrink = rc == 1; + + if ((rc = virXMLPropUInt(pollNode, "weight", 10, VIR_XML_PROP_NONE, + &iothrid->poll_weight)) < 0) + return NULL; + + iothrid->set_poll_weight = rc == 1; } return g_steal_pointer(&iothrid); @@ -29011,6 +29019,7 @@ virDomainDefIothreadShouldFormat(const virDomainDef *def) def->iothreadids[i]->set_poll_max_ns || def->iothreadids[i]->set_poll_grow || def->iothreadids[i]->set_poll_shrink || + def->iothreadids[i]->set_poll_weight || def->iothreadids[i]->thread_pool_min >= 0 || def->iothreadids[i]->thread_pool_max >= 0) return true; @@ -29084,6 +29093,9 @@ virDomainDefIOThreadsFormat(virBuffer *buf, if (iothread->set_poll_shrink) virBufferAsprintf(&pollAttrBuf, " shrink='%llu'", iothread->poll_shrink); + if (iothread->set_poll_weight) + virBufferAsprintf(&pollAttrBuf, " weight='%u'", iothread->poll_weight); + virXMLFormatElement(&iothreadChildBuf, "poll", &pollAttrBuf, NULL); virXMLFormatElement(&childrenBuf, "iothread", &attrBuf, &iothreadChildBuf); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 0c6c79c413..eebe55a093 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2902,6 +2902,8 @@ struct _virDomainIOThreadIDDef { bool set_poll_grow; unsigned long long poll_shrink; bool set_poll_shrink; + unsigned int poll_weight; + bool set_poll_weight; int thread_pool_min; int thread_pool_max; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 121e4e06a6..36743f81e4 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -990,6 +990,13 @@ <ref name="unsignedLong"/> </attribute> </optional> + <optional> + <attribute name="weight"> + <data type="unsignedInt"> + <param name="maxInclusive">63</param> + </data> + </attribute> + </optional> </element> </optional> </element> diff --git a/tests/genericxml2xmlindata/iothreadids.xml b/tests/genericxml2xmlindata/iothreadids.xml index 671a467295..e9814854eb 100644 --- a/tests/genericxml2xmlindata/iothreadids.xml +++ b/tests/genericxml2xmlindata/iothreadids.xml @@ -7,7 +7,7 @@ <iothreads>1</iothreads> <iothreadids> <iothread id='8' thread_pool_min='2147483647' thread_pool_max='2147483647'> - <poll max='9223372036854775807' grow='456' shrink='789'/> + <poll max='9223372036854775807' grow='456' shrink='789' weight='3'/> </iothread> </iothreadids> <os> -- 2.54.0
On Wed, Jul 22, 2026 at 13:52:10 -0500, Jaehoon Kim wrote:
Add support for the iothread poll-weight XML attribute.
Store the value in the internal iothread definition, parse it from the <poll/> element, and format it back into domain XML. Also extend the schema to accept the new attribute and validate the accepted range of [0, 63].
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- src/conf/domain_conf.c | 14 +++++++++++++- src/conf/domain_conf.h | 2 ++ src/conf/schemas/domaincommon.rng | 7 +++++++ tests/genericxml2xmlindata/iothreadids.xml | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-)
[...]
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 121e4e06a6..36743f81e4 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -990,6 +990,13 @@ <ref name="unsignedLong"/> </attribute> </optional> + <optional> + <attribute name="weight"> + <data type="unsignedInt"> + <param name="maxInclusive">63</param>
Defining the range in the XML schema is not sufficient as validation is not mandatory. You'll need to add an explicit validation check in the code (e.g. in virDomainDefValidateIOThreads) to actually enforce this. And at that point I'd maybe suggest removing the XML check, the RNG validator from libxml2 tends to have rather poor error messages.
On 7/29/2026 8:52 AM, Peter Krempa wrote:
On Wed, Jul 22, 2026 at 13:52:10 -0500, Jaehoon Kim wrote:
Add support for the iothread poll-weight XML attribute.
Store the value in the internal iothread definition, parse it from the <poll/> element, and format it back into domain XML. Also extend the schema to accept the new attribute and validate the accepted range of [0, 63].
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- src/conf/domain_conf.c | 14 +++++++++++++- src/conf/domain_conf.h | 2 ++ src/conf/schemas/domaincommon.rng | 7 +++++++ tests/genericxml2xmlindata/iothreadids.xml | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) [...]
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 121e4e06a6..36743f81e4 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -990,6 +990,13 @@ <ref name="unsignedLong"/> </attribute> </optional> + <optional> + <attribute name="weight"> + <data type="unsignedInt"> + <param name="maxInclusive">63</param> Defining the range in the XML schema is not sufficient as validation is not mandatory.
You'll need to add an explicit validation check in the code (e.g. in virDomainDefValidateIOThreads) to actually enforce this.
And at that point I'd maybe suggest removing the XML check, the RNG validator from libxml2 tends to have rather poor error messages.
Thanks for the feedback. I'll make the following changes in v2. 1.Add an explicit range check for poll-weight in virDomainDefValidateIOThreads in src/conf/domain_validate.c. 2.Simplify the RNG schema by replacing the maxInclusive constraint, since the range will be enforced during validation. Thanks, Jaehoon
Add a capability flag for the iothread poll-weight property. Detect support for the property from the QMP schema so that libvirt can conditionally expose and use poll-weight only with QEMU binaries that support it. Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_11.1.0_x86_64.xml | 1 + 4 files changed, 5 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index ebea050453..971663badc 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -768,6 +768,7 @@ VIR_ENUM_IMPL(virQEMUCaps, /* 495 */ "blockdev-mirror.target-is-zero", /* QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO */ "object-monitor-qmp", /* QEMU_CAPS_OBJECT_MONITOR_QMP */ + "iothread.poll-weight", /* QEMU_CAPS_IOTHREAD_POLL_WEIGHT */ ); @@ -1671,6 +1672,7 @@ static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] = { { "netdev_add/arg-type/+stream/reconnect-ms", QEMU_CAPS_NETDEV_STREAM_RECONNECT_MILISECONDS }, { "object-add/arg-type/+sev-guest/kernel-hashes", QEMU_CAPS_SEV_GUEST_KERNEL_HASHES }, { "object-add/arg-type/+iothread/thread-pool-max", QEMU_CAPS_IOTHREAD_THREAD_POOL_MAX }, + { "object-add/arg-type/+iothread/poll-weight", QEMU_CAPS_IOTHREAD_POLL_WEIGHT }, { "query-block/arg-type/flat", QEMU_CAPS_QUERY_BLOCK_FLAT }, { "query-display-options/ret-type/+egl-headless/rendernode", QEMU_CAPS_EGL_HEADLESS_RENDERNODE }, { "query-display-options/ret-type/+sdl", QEMU_CAPS_SDL }, diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index daec874a59..8f83eb8acd 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -742,6 +742,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 495 */ QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO, /* 'blockdev-mirror' supports 'target-is-zero' */ QEMU_CAPS_OBJECT_MONITOR_QMP, /* 'monitor-qmp' -object */ + QEMU_CAPS_IOTHREAD_POLL_WEIGHT, /* -object iothread.poll-weight */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml b/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml index b45dd83889..6c5c1770ad 100644 --- a/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml @@ -189,6 +189,7 @@ <flag name='query-block-flat'/> <flag name='blockdev-mirror.target-is-zero'/> <flag name='object-monitor-qmp'/> + <flag name='iothread.poll-weight'/> <version>11000090</version> <microcodeVersion>61700287</microcodeVersion> <package>v11.1.0-rc0</package> diff --git a/tests/qemucapabilitiesdata/caps_11.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_11.1.0_x86_64.xml index 7a4396c82b..98a6a7e083 100644 --- a/tests/qemucapabilitiesdata/caps_11.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_11.1.0_x86_64.xml @@ -223,6 +223,7 @@ <flag name='amd-iommu.xtsup'/> <flag name='blockdev-mirror.target-is-zero'/> <flag name='object-monitor-qmp'/> + <flag name='iothread.poll-weight'/> <version>11000090</version> <microcodeVersion>43100287</microcodeVersion> <package>v11.1.0-rc0</package> -- 2.54.0
On Wed, Jul 22, 2026 at 13:52:11 -0500, Jaehoon Kim wrote:
Add a capability flag for the iothread poll-weight property.
Detect support for the property from the QMP schema so that libvirt can conditionally expose and use poll-weight only with QEMU binaries that support it.
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_11.1.0_x86_64.xml | 1 + 4 files changed, 5 insertions(+)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
On 7/29/2026 8:53 AM, Peter Krempa wrote:
On Wed, Jul 22, 2026 at 13:52:11 -0500, Jaehoon Kim wrote:
Add a capability flag for the iothread poll-weight property.
Detect support for the property from the QMP schema so that libvirt can conditionally expose and use poll-weight only with QEMU binaries that support it.
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_11.1.0_x86_64.xml | 1 + 4 files changed, 5 insertions(+) Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Thank you for reviewing the patch. I'll add your Reviewed-by tag in v2. Thanks, Jaehoon
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 + * current interval contributes approximately 1/8 to the weighted average). + * + * Accepted type is VIR_TYPED_PARAM_UINT. + * + * Since: 12.6.0 + */ +# 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 + */ +# 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; + } + + if (virJSONValueObjectAdd(&props, + "u:poll-weight", iothread->poll_weight, + NULL) < 0) + return -1; + } + if (qemuBuildObjectCommandlineFromJSON(cmd, props) < 0) return -1; } @@ -10903,7 +10917,7 @@ qemuBuildCommandLine(virDomainObj *vm, if (qemuBuildSmpCommandLine(cmd, def, qemuCaps) < 0) return NULL; - if (qemuBuildIOThreadCommandLine(cmd, def) < 0) + if (qemuBuildIOThreadCommandLine(cmd, def, qemuCaps) < 0) return NULL; if (qemuBuildThrottleGroupCommandLine(cmd, def) < 0) 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) @@ -5586,6 +5610,13 @@ qemuDomainChgIOThread(virQEMUDriver *driver, if (qemuDomainIOThreadValidate(iothreaddef, iothread, true) < 0) goto endjob; + if (iothread.set_poll_weight && + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_IOTHREAD_POLL_WEIGHT)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("poll-weight is not supported by this QEMU binary")); + goto endjob; + } + if (qemuDomainHotplugModIOThread(vm, iothread) < 0) goto endjob; @@ -5716,6 +5747,8 @@ qemuDomainSetIOThreadParams(virDomainPtr dom, VIR_TYPED_PARAM_UNSIGNED, VIR_DOMAIN_IOTHREAD_POLL_SHRINK, VIR_TYPED_PARAM_UNSIGNED, + VIR_DOMAIN_IOTHREAD_POLL_WEIGHT, + VIR_TYPED_PARAM_UINT, VIR_DOMAIN_IOTHREAD_THREAD_POOL_MIN, VIR_TYPED_PARAM_INT, VIR_DOMAIN_IOTHREAD_THREAD_POOL_MAX, @@ -18380,6 +18413,11 @@ qemuDomainGetStatsIOThread(virQEMUDriver *driver G_GNUC_UNUSED, VIR_DOMAIN_STATS_IOTHREAD_PREFIX "%u" VIR_DOMAIN_STATS_IOTHREAD_SUFFIX_POLL_SHRINK, iothreads[i]->iothread_id); } + if (iothreads[i]->set_poll_weight) { + virTypedParamListAddUInt(params, iothreads[i]->poll_weight, + VIR_DOMAIN_STATS_IOTHREAD_PREFIX "%u" VIR_DOMAIN_STATS_IOTHREAD_SUFFIX_POLL_WEIGHT, + iothreads[i]->iothread_id); + } } for (i = 0; i < niothreads; i++) 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; @@ -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/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-11.0.0.err b/tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-11.0.0.err new file mode 100644 index 0000000000..1cc4109f27 --- /dev/null +++ b/tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-11.0.0.err @@ -0,0 +1 @@ +unsupported configuration: poll-weight is not supported by this QEMU binary diff --git a/tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-latest.args b/tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-latest.args new file mode 100644 index 0000000000..d73060ad46 --- /dev/null +++ b/tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-latest.args @@ -0,0 +1,40 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \ +-machine q35,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \ +-accel tcg \ +-cpu qemu64 \ +-m size=219136k \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \ +-overcommit mem-lock=off \ +-smp 4,sockets=4,cores=1,threads=1 \ +-object '{"qom-type":"iothread","id":"iothread1","poll-max-ns":123000,"poll-grow":456,"poll-shrink":789,"poll-weight":3}' \ +-object '{"qom-type":"iothread","id":"iothread2","poll-weight":0}' \ +-object '{"qom-type":"iothread","id":"iothread3","poll-max-ns":32000,"poll-weight":63}' \ +-object '{"qom-type":"iothread","id":"iothread4","poll-max-ns":32000,"poll-grow":2,"poll-shrink":2}' \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=@mon-fd@,server=on,wait=off \ +-object '{"qom-type":"monitor-qmp","id":"monitor","chardev":"charmonitor"}' \ +-rtc base=utc \ +-no-shutdown \ +-boot strict=on \ +-device '{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}' \ +-device '{"driver":"pcie-root-port","port":9,"chassis":2,"id":"pci.2","bus":"pcie.0","addr":"0x1.0x1"}' \ +-device '{"driver":"qemu-xhci","id":"usb","bus":"pci.1","addr":"0x0"}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-global ICH9-LPC.noreboot=off \ +-watchdog-action reset \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-latest.xml b/tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-latest.xml new file mode 100644 index 0000000000..1134d9ffdd --- /dev/null +++ b/tests/qemuxmlconfdata/iothreads-ids-poll-weight.x86_64-latest.xml @@ -0,0 +1,58 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>4</vcpu> + <iothreads>4</iothreads> + <iothreadids> + <iothread id='1'> + <poll max='123000' grow='456' shrink='789' weight='3'/> + </iothread> + <iothread id='2'> + <poll weight='0'/> + </iothread> + <iothread id='3'> + <poll max='32000' weight='63'/> + </iothread> + <iothread id='4'> + <poll max='32000' grow='2' shrink='2'/> + </iothread> + </iothreadids> + <os> + <type arch='x86_64' machine='q35'>hvm</type> + <boot dev='hd'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='pci' index='0' model='pcie-root'/> + <controller type='pci' index='1' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='1' port='0x8'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/> + </controller> + <controller type='pci' index='2' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='2' port='0x9'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='usb' index='0' model='qemu-xhci'> + <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> + </controller> + <controller type='sata' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/> + </controller> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <watchdog model='itco' action='reset'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxmlconfdata/iothreads-ids-poll-weight.xml b/tests/qemuxmlconfdata/iothreads-ids-poll-weight.xml new file mode 100644 index 0000000000..1134d9ffdd --- /dev/null +++ b/tests/qemuxmlconfdata/iothreads-ids-poll-weight.xml @@ -0,0 +1,58 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>4</vcpu> + <iothreads>4</iothreads> + <iothreadids> + <iothread id='1'> + <poll max='123000' grow='456' shrink='789' weight='3'/> + </iothread> + <iothread id='2'> + <poll weight='0'/> + </iothread> + <iothread id='3'> + <poll max='32000' weight='63'/> + </iothread> + <iothread id='4'> + <poll max='32000' grow='2' shrink='2'/> + </iothread> + </iothreadids> + <os> + <type arch='x86_64' machine='q35'>hvm</type> + <boot dev='hd'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='pci' index='0' model='pcie-root'/> + <controller type='pci' index='1' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='1' port='0x8'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/> + </controller> + <controller type='pci' index='2' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='2' port='0x9'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='usb' index='0' model='qemu-xhci'> + <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> + </controller> + <controller type='sata' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/> + </controller> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <watchdog model='itco' action='reset'/> + <memballoon model='none'/> + </devices> +</domain> 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"); DO_TEST_CAPS_LATEST("iothreads-disk"); DO_TEST_CAPS_LATEST("iothreads-virtio-scsi-pci"); DO_TEST_CAPS_LATEST("iothreads-virtio-scsi-mapping"); -- 2.54.0
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.
On 7/29/2026 9:20 AM, Peter Krempa 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
On Wed, Jul 22, 2026 at 13:52:12 -0500, Jaehoon Kim wrote: 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.
I will fix to 12.7.0 in v2.
+ */ +# 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.
Agreed. This check is redundant since qemuValidateDomainDefIOThreads already handles this case before command line generation. I'll remove it in v2.
+ + 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.
I'll split in v2. The monitor getter parsing and the stats reporting will be move into a separate patch ahead of the setter wiring.
@@ -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.
I will drop the capability based negative test in v2. Thanks, Jaehoon
Add virsh support for configuring and reporting iothread poll-weight. Document poll-weight in the virsh manpage and extend iothreadset so that poll-weight can be configured from the command line. Also add poll-weight support to the test driver and extend virshtest accordingly. Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/manpages/virsh.rst | 18 ++++++++++++------ src/test/test_driver.c | 12 ++++++++++++ tests/virshtestdata/iothreads.in | 2 ++ tests/virshtestdata/iothreads.out | 18 ++++++++++++++++++ tools/virsh-domain.c | 18 ++++++++++++++++++ 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index a10d29e0ea..24052d0cdb 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -2905,6 +2905,8 @@ not available for statistical purposes. growth is managed by the hypervisor. * ``iothread.<id>.poll-shrink`` - polling time shrink value. A value of (zero) indicates shrink is managed by hypervisor. +* ``iothread.<id>.poll-weight`` - polling weight shift value. A value of 0 + (zero) indicates the hypervisor's default weight is used. *--memory* returns: @@ -3498,8 +3500,8 @@ iothreadset :: iothreadset domain iothread_id [[--poll-max-ns ns] [--poll-grow factor] - [--poll-shrink divisor] [--thread-pool-min value] - [--thread-pool-max value]] + [--poll-shrink divisor] [--poll-weight factor] + [--thread-pool-min value] [--thread-pool-max value]] [[--config] [--live] | [--current]] Modifies an existing iothread of the domain using the specified @@ -3511,10 +3513,14 @@ reach the maximum polling time. If a 0 (zero) is provided, then the default factor will be used. The *--poll-shrink* is the quotient by which the current polling time will be reduced in order to get below the maximum polling interval. If a 0 (zero) is provided, then -the default quotient will be used. The polling values are purely dynamic -for a running guest. Saving, destroying, stopping, etc. the guest will -result in the polling values returning to hypervisor defaults at the -next start, restore, etc. +the default quotient will be used. The *--poll-weight* sets the weight +shift value for adaptive polling, determining how much the most recent +event interval affects the next polling duration calculation. Larger +values reduce the weight of recent events. Valid range is [0, 63]. +If omitted or set to 0, the hypervisor selects a default. +The polling values are purely dynamic for a running guest. Saving, +destroying, stopping, etc. the guest will result in the polling values +returning to hypervisor defaults at the next start, restore, etc. The *--thread-pool-min* and *--thread-pool-max* options then set lower and upper bound, respectively of number of threads in worker pool of given diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 536e291861..8546c3e251 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -189,6 +189,7 @@ struct _testIOThreadInfo { unsigned long long poll_max_ns; unsigned int poll_grow; unsigned int poll_shrink; + unsigned int poll_weight; }; static void @@ -749,6 +750,7 @@ testDomainGenerateIOThreadInfos(virDomainObj *obj) iothread.poll_max_ns = 32768; iothread.poll_grow = 0; iothread.poll_shrink = 0; + iothread.poll_weight = 0; g_array_append_val(priv->iothreads, iothread); } } @@ -9681,6 +9683,7 @@ testDomainAddIOThread(virDomainPtr dom, iothread.poll_max_ns = 32768; iothread.poll_grow = 0; iothread.poll_shrink = 0; + iothread.poll_weight = 0; g_array_append_val(priv->iothreads, iothread); @@ -9795,6 +9798,8 @@ testDomainIOThreadParseParams(virTypedParameterPtr params, VIR_TYPED_PARAM_UINT, VIR_DOMAIN_IOTHREAD_POLL_SHRINK, VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_IOTHREAD_POLL_WEIGHT, + VIR_TYPED_PARAM_UINT, NULL) < 0) return -1; @@ -9813,6 +9818,11 @@ testDomainIOThreadParseParams(virTypedParameterPtr params, &iothread->poll_shrink) < 0) return -1; + if (virTypedParamsGetUInt(params, nparams, + VIR_DOMAIN_IOTHREAD_POLL_WEIGHT, + &iothread->poll_weight) < 0) + return -1; + return 0; } @@ -9899,6 +9909,8 @@ testDomainGetStatsIOThread(virDomainObj *dom, "iothread.%u.poll-grow", iothread.iothread_id); virTypedParamListAddUInt(params, iothread.poll_shrink, "iothread.%u.poll-shrink", iothread.iothread_id); + virTypedParamListAddUInt(params, iothread.poll_weight, + "iothread.%u.poll-weight", iothread.iothread_id); } return 0; diff --git a/tests/virshtestdata/iothreads.in b/tests/virshtestdata/iothreads.in index 25ebcb5cda..b5aed83bba 100644 --- a/tests/virshtestdata/iothreads.in +++ b/tests/virshtestdata/iothreads.in @@ -7,6 +7,8 @@ iothreadinfo --domain fc4 domstats --domain fc4 iothreadset --domain fc4 --id 6 --poll-max-ns 100 --poll-shrink 10 --poll-grow 10 domstats --domain fc4 +iothreadset --domain fc4 --id 6 --poll-weight 3 +domstats --domain fc4 iothreadadd --domain fc5 --id 2 iothreadinfo --domain fc5 diff --git a/tests/virshtestdata/iothreads.out b/tests/virshtestdata/iothreads.out index 1e38733bcf..a51593d765 100644 --- a/tests/virshtestdata/iothreads.out +++ b/tests/virshtestdata/iothreads.out @@ -23,9 +23,11 @@ Domain: 'fc4' iothread.4.poll-max-ns=32768 iothread.4.poll-grow=0 iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 iothread.6.poll-max-ns=32768 iothread.6.poll-grow=0 iothread.6.poll-shrink=0 + iothread.6.poll-weight=0 Domain: 'fc4' @@ -35,9 +37,25 @@ Domain: 'fc4' iothread.4.poll-max-ns=32768 iothread.4.poll-grow=0 iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 iothread.6.poll-max-ns=100 iothread.6.poll-grow=10 iothread.6.poll-shrink=10 + iothread.6.poll-weight=0 + + +Domain: 'fc4' + state.state=1 + state.reason=0 + iothread.count=2 + iothread.4.poll-max-ns=32768 + iothread.4.poll-grow=0 + iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 + iothread.6.poll-max-ns=100 + iothread.6.poll-grow=10 + iothread.6.poll-shrink=10 + iothread.6.poll-weight=3 IOThread ID CPU Affinity diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index aa4f2a7a48..2e4923283b 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8270,6 +8270,11 @@ static const vshCmdOptDef opts_iothreadset[] = { .unwanted_positional = true, .help = N_("set the value for reduction of the IOThread polling time") }, + {.name = "poll-weight", + .type = VSH_OT_INT, + .unwanted_positional = true, + .help = N_("set the adaptive polling weight factor") + }, {.name = "thread-pool-min", .type = VSH_OT_INT, .unwanted_positional = true, @@ -8299,6 +8304,7 @@ cmdIOThreadSet(vshControl *ctl, const vshCmd *cmd) virTypedParameterPtr par; size_t npar = 0; unsigned long long poll_val; + unsigned int poll_weight; int thread_val; int rc; @@ -8335,6 +8341,18 @@ cmdIOThreadSet(vshControl *ctl, const vshCmd *cmd) if (rc > 0) virTypedParamListAddUnsigned(params, poll_val, VIR_DOMAIN_IOTHREAD_POLL_SHRINK); + if ((rc = vshCommandOptUInt(ctl, cmd, "poll-weight", &poll_weight)) < 0) + return false; + if (rc > 0) { + if (poll_weight > 63) { + vshError(ctl, _("poll-weight value %1$u is out of range [0, 63]"), + poll_weight); + return false; + } + + virTypedParamListAddUInt(params, poll_weight, VIR_DOMAIN_IOTHREAD_POLL_WEIGHT); + } + if ((rc = vshCommandOptInt(ctl, cmd, "thread-pool-min", &thread_val)) < 0) return false; if (rc > 0) -- 2.54.0
On Wed, Jul 22, 2026 at 13:52:13 -0500, Jaehoon Kim wrote:
Add virsh support for configuring and reporting iothread poll-weight.
Document poll-weight in the virsh manpage and extend iothreadset so that poll-weight can be configured from the command line. Also add poll-weight support to the test driver and extend virshtest accordingly.
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/manpages/virsh.rst | 18 ++++++++++++------ src/test/test_driver.c | 12 ++++++++++++ tests/virshtestdata/iothreads.in | 2 ++ tests/virshtestdata/iothreads.out | 18 ++++++++++++++++++ tools/virsh-domain.c | 18 ++++++++++++++++++ 5 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index a10d29e0ea..24052d0cdb 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -2905,6 +2905,8 @@ not available for statistical purposes. growth is managed by the hypervisor. * ``iothread.<id>.poll-shrink`` - polling time shrink value. A value of (zero) indicates shrink is managed by hypervisor. +* ``iothread.<id>.poll-weight`` - polling weight shift value. A value of 0 + (zero) indicates the hypervisor's default weight is used.
This hunk belongs to the patch that adds the stats gathering code since it starts being emitted at that point. This patch is adding the code to modify the setting.
*--memory* returns:
@@ -3498,8 +3500,8 @@ iothreadset ::
iothreadset domain iothread_id [[--poll-max-ns ns] [--poll-grow factor] - [--poll-shrink divisor] [--thread-pool-min value] - [--thread-pool-max value]] + [--poll-shrink divisor] [--poll-weight factor] + [--thread-pool-min value] [--thread-pool-max value]] [[--config] [--live] | [--current]]
Modifies an existing iothread of the domain using the specified @@ -3511,10 +3513,14 @@ reach the maximum polling time. If a 0 (zero) is provided, then the default factor will be used. The *--poll-shrink* is the quotient by which the current polling time will be reduced in order to get below the maximum polling interval. If a 0 (zero) is provided, then -the default quotient will be used. The polling values are purely dynamic -for a running guest. Saving, destroying, stopping, etc. the guest will -result in the polling values returning to hypervisor defaults at the -next start, restore, etc. +the default quotient will be used. The *--poll-weight* sets the weight +shift value for adaptive polling, determining how much the most recent +event interval affects the next polling duration calculation. Larger +values reduce the weight of recent events. Valid range is [0, 63]. +If omitted or set to 0, the hypervisor selects a default.
This is not accurate. If the --poll-weight parameter is not supplied this virsh command will not put the VIR_DOMAIN_IOTHREAD_POLL_WEIGHT parameter to the list of stuff to change. At that point the backend code will not modify this value rather than set it to the default.
+The polling values are purely dynamic for a running guest. Saving, +destroying, stopping, etc. the guest will result in the polling values +returning to hypervisor defaults at the next start, restore, etc.
The *--thread-pool-min* and *--thread-pool-max* options then set lower and upper bound, respectively of number of threads in worker pool of given diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 536e291861..8546c3e251 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -189,6 +189,7 @@ struct _testIOThreadInfo { unsigned long long poll_max_ns; unsigned int poll_grow; unsigned int poll_shrink; + unsigned int poll_weight; };
static void @@ -749,6 +750,7 @@ testDomainGenerateIOThreadInfos(virDomainObj *obj) iothread.poll_max_ns = 32768; iothread.poll_grow = 0; iothread.poll_shrink = 0; + iothread.poll_weight = 0; g_array_append_val(priv->iothreads, iothread); } } @@ -9681,6 +9683,7 @@ testDomainAddIOThread(virDomainPtr dom, iothread.poll_max_ns = 32768; iothread.poll_grow = 0; iothread.poll_shrink = 0; + iothread.poll_weight = 0;
g_array_append_val(priv->iothreads, iothread);
@@ -9795,6 +9798,8 @@ testDomainIOThreadParseParams(virTypedParameterPtr params, VIR_TYPED_PARAM_UINT, VIR_DOMAIN_IOTHREAD_POLL_SHRINK, VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_IOTHREAD_POLL_WEIGHT, + VIR_TYPED_PARAM_UINT, NULL) < 0) return -1;
@@ -9813,6 +9818,11 @@ testDomainIOThreadParseParams(virTypedParameterPtr params, &iothread->poll_shrink) < 0) return -1;
+ if (virTypedParamsGetUInt(params, nparams, + VIR_DOMAIN_IOTHREAD_POLL_WEIGHT, + &iothread->poll_weight) < 0) + return -1; + return 0; }
@@ -9899,6 +9909,8 @@ testDomainGetStatsIOThread(virDomainObj *dom, "iothread.%u.poll-grow", iothread.iothread_id); virTypedParamListAddUInt(params, iothread.poll_shrink, "iothread.%u.poll-shrink", iothread.iothread_id); + virTypedParamListAddUInt(params, iothread.poll_weight, + "iothread.%u.poll-weight", iothread.iothread_id); }
return 0;
Please separate the testr driver changes into a separate patch.
diff --git a/tests/virshtestdata/iothreads.in b/tests/virshtestdata/iothreads.in index 25ebcb5cda..b5aed83bba 100644 --- a/tests/virshtestdata/iothreads.in +++ b/tests/virshtestdata/iothreads.in @@ -7,6 +7,8 @@ iothreadinfo --domain fc4 domstats --domain fc4 iothreadset --domain fc4 --id 6 --poll-max-ns 100 --poll-shrink 10 --poll-grow 10 domstats --domain fc4 +iothreadset --domain fc4 --id 6 --poll-weight 3 +domstats --domain fc4
iothreadadd --domain fc5 --id 2 iothreadinfo --domain fc5 diff --git a/tests/virshtestdata/iothreads.out b/tests/virshtestdata/iothreads.out index 1e38733bcf..a51593d765 100644 --- a/tests/virshtestdata/iothreads.out +++ b/tests/virshtestdata/iothreads.out @@ -23,9 +23,11 @@ Domain: 'fc4' iothread.4.poll-max-ns=32768 iothread.4.poll-grow=0 iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 iothread.6.poll-max-ns=32768 iothread.6.poll-grow=0 iothread.6.poll-shrink=0 + iothread.6.poll-weight=0
Domain: 'fc4' @@ -35,9 +37,25 @@ Domain: 'fc4' iothread.4.poll-max-ns=32768 iothread.4.poll-grow=0 iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 iothread.6.poll-max-ns=100 iothread.6.poll-grow=10 iothread.6.poll-shrink=10 + iothread.6.poll-weight=0 + + +Domain: 'fc4' + state.state=1 + state.reason=0 + iothread.count=2 + iothread.4.poll-max-ns=32768 + iothread.4.poll-grow=0 + iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 + iothread.6.poll-max-ns=100 + iothread.6.poll-grow=10 + iothread.6.poll-shrink=10 + iothread.6.poll-weight=3
IOThread ID CPU Affinity
IIUC the fetching of the value will change this output right when the test driver code is changed, so those bits will need to be moved there. The last one where the weight is set will need to happen after that patch but also possibly in a separate commit.
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index aa4f2a7a48..2e4923283b 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8270,6 +8270,11 @@ static const vshCmdOptDef opts_iothreadset[] = { .unwanted_positional = true, .help = N_("set the value for reduction of the IOThread polling time") }, + {.name = "poll-weight", + .type = VSH_OT_INT, + .unwanted_positional = true, + .help = N_("set the adaptive polling weight factor") + }, {.name = "thread-pool-min", .type = VSH_OT_INT, .unwanted_positional = true, @@ -8299,6 +8304,7 @@ cmdIOThreadSet(vshControl *ctl, const vshCmd *cmd) virTypedParameterPtr par; size_t npar = 0; unsigned long long poll_val; + unsigned int poll_weight; int thread_val; int rc;
@@ -8335,6 +8341,18 @@ cmdIOThreadSet(vshControl *ctl, const vshCmd *cmd) if (rc > 0) virTypedParamListAddUnsigned(params, poll_val, VIR_DOMAIN_IOTHREAD_POLL_SHRINK);
+ if ((rc = vshCommandOptUInt(ctl, cmd, "poll-weight", &poll_weight)) < 0) + return false; + if (rc > 0) { + if (poll_weight > 63) { + vshError(ctl, _("poll-weight value %1$u is out of range [0, 63]"), + poll_weight); + return false; + } + + virTypedParamListAddUInt(params, poll_weight, VIR_DOMAIN_IOTHREAD_POLL_WEIGHT); + } + if ((rc = vshCommandOptInt(ctl, cmd, "thread-pool-min", &thread_val)) < 0) return false; if (rc > 0) -- 2.54.0
On 7/29/2026 9:31 AM, Peter Krempa wrote:
On Wed, Jul 22, 2026 at 13:52:13 -0500, Jaehoon Kim wrote:
Add virsh support for configuring and reporting iothread poll-weight.
Document poll-weight in the virsh manpage and extend iothreadset so that poll-weight can be configured from the command line. Also add poll-weight support to the test driver and extend virshtest accordingly.
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/manpages/virsh.rst | 18 ++++++++++++------ src/test/test_driver.c | 12 ++++++++++++ tests/virshtestdata/iothreads.in | 2 ++ tests/virshtestdata/iothreads.out | 18 ++++++++++++++++++ tools/virsh-domain.c | 18 ++++++++++++++++++ 5 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index a10d29e0ea..24052d0cdb 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -2905,6 +2905,8 @@ not available for statistical purposes. growth is managed by the hypervisor. * ``iothread.<id>.poll-shrink`` - polling time shrink value. A value of (zero) indicates shrink is managed by hypervisor. +* ``iothread.<id>.poll-weight`` - polling weight shift value. A value of 0 + (zero) indicates the hypervisor's default weight is used. This hunk belongs to the patch that adds the stats gathering code since it starts being emitted at that point.
This patch is adding the code to modify the setting.
I'll move to the stats patch in v2.
*--memory* returns:
@@ -3498,8 +3500,8 @@ iothreadset ::
iothreadset domain iothread_id [[--poll-max-ns ns] [--poll-grow factor] - [--poll-shrink divisor] [--thread-pool-min value] - [--thread-pool-max value]] + [--poll-shrink divisor] [--poll-weight factor] + [--thread-pool-min value] [--thread-pool-max value]] [[--config] [--live] | [--current]]
Modifies an existing iothread of the domain using the specified @@ -3511,10 +3513,14 @@ reach the maximum polling time. If a 0 (zero) is provided, then the default factor will be used. The *--poll-shrink* is the quotient by which the current polling time will be reduced in order to get below the maximum polling interval. If a 0 (zero) is provided, then -the default quotient will be used. The polling values are purely dynamic -for a running guest. Saving, destroying, stopping, etc. the guest will -result in the polling values returning to hypervisor defaults at the -next start, restore, etc. +the default quotient will be used. The *--poll-weight* sets the weight +shift value for adaptive polling, determining how much the most recent +event interval affects the next polling duration calculation. Larger +values reduce the weight of recent events. Valid range is [0, 63]. +If omitted or set to 0, the hypervisor selects a default.
This is not accurate. If the --poll-weight parameter is not supplied this virsh command will not put the VIR_DOMAIN_IOTHREAD_POLL_WEIGHT parameter to the list of stuff to change.
At that point the backend code will not modify this value rather than set it to the default.
Correct. If --poll-weight is omitted, the value is not changed. I'll fix the documentation in v2.
+The polling values are purely dynamic for a running guest. Saving, +destroying, stopping, etc. the guest will result in the polling values +returning to hypervisor defaults at the next start, restore, etc.
The *--thread-pool-min* and *--thread-pool-max* options then set lower and upper bound, respectively of number of threads in worker pool of given diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 536e291861..8546c3e251 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -189,6 +189,7 @@ struct _testIOThreadInfo { unsigned long long poll_max_ns; unsigned int poll_grow; unsigned int poll_shrink; + unsigned int poll_weight; };
static void @@ -749,6 +750,7 @@ testDomainGenerateIOThreadInfos(virDomainObj *obj) iothread.poll_max_ns = 32768; iothread.poll_grow = 0; iothread.poll_shrink = 0; + iothread.poll_weight = 0; g_array_append_val(priv->iothreads, iothread); } } @@ -9681,6 +9683,7 @@ testDomainAddIOThread(virDomainPtr dom, iothread.poll_max_ns = 32768; iothread.poll_grow = 0; iothread.poll_shrink = 0; + iothread.poll_weight = 0;
g_array_append_val(priv->iothreads, iothread);
@@ -9795,6 +9798,8 @@ testDomainIOThreadParseParams(virTypedParameterPtr params, VIR_TYPED_PARAM_UINT, VIR_DOMAIN_IOTHREAD_POLL_SHRINK, VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_IOTHREAD_POLL_WEIGHT, + VIR_TYPED_PARAM_UINT, NULL) < 0) return -1;
@@ -9813,6 +9818,11 @@ testDomainIOThreadParseParams(virTypedParameterPtr params, &iothread->poll_shrink) < 0) return -1;
+ if (virTypedParamsGetUInt(params, nparams, + VIR_DOMAIN_IOTHREAD_POLL_WEIGHT, + &iothread->poll_weight) < 0) + return -1; + return 0; }
@@ -9899,6 +9909,8 @@ testDomainGetStatsIOThread(virDomainObj *dom, "iothread.%u.poll-grow", iothread.iothread_id); virTypedParamListAddUInt(params, iothread.poll_shrink, "iothread.%u.poll-shrink", iothread.iothread_id); + virTypedParamListAddUInt(params, iothread.poll_weight, + "iothread.%u.poll-weight", iothread.iothread_id); }
return 0; Please separate the testr driver changes into a separate patch.
I will move the test driver changes into a separate patch in v2.
diff --git a/tests/virshtestdata/iothreads.in b/tests/virshtestdata/iothreads.in index 25ebcb5cda..b5aed83bba 100644 --- a/tests/virshtestdata/iothreads.in +++ b/tests/virshtestdata/iothreads.in @@ -7,6 +7,8 @@ iothreadinfo --domain fc4 domstats --domain fc4 iothreadset --domain fc4 --id 6 --poll-max-ns 100 --poll-shrink 10 --poll-grow 10 domstats --domain fc4 +iothreadset --domain fc4 --id 6 --poll-weight 3 +domstats --domain fc4
iothreadadd --domain fc5 --id 2 iothreadinfo --domain fc5 diff --git a/tests/virshtestdata/iothreads.out b/tests/virshtestdata/iothreads.out index 1e38733bcf..a51593d765 100644 --- a/tests/virshtestdata/iothreads.out +++ b/tests/virshtestdata/iothreads.out @@ -23,9 +23,11 @@ Domain: 'fc4' iothread.4.poll-max-ns=32768 iothread.4.poll-grow=0 iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 iothread.6.poll-max-ns=32768 iothread.6.poll-grow=0 iothread.6.poll-shrink=0 + iothread.6.poll-weight=0
Domain: 'fc4' @@ -35,9 +37,25 @@ Domain: 'fc4' iothread.4.poll-max-ns=32768 iothread.4.poll-grow=0 iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 iothread.6.poll-max-ns=100 iothread.6.poll-grow=10 iothread.6.poll-shrink=10 + iothread.6.poll-weight=0 + + +Domain: 'fc4' + state.state=1 + state.reason=0 + iothread.count=2 + iothread.4.poll-max-ns=32768 + iothread.4.poll-grow=0 + iothread.4.poll-shrink=0 + iothread.4.poll-weight=0 + iothread.6.poll-max-ns=100 + iothread.6.poll-grow=10 + iothread.6.poll-shrink=10 + iothread.6.poll-weight=3
IOThread ID CPU Affinity IIUC the fetching of the value will change this output right when the test driver code is changed, so those bits will need to be moved there.
The last one where the weight is set will need to happen after that patch but also possibly in a separate commit.
Understood, I will move the poll-weight=0 output changes caused by the test driver stats emission to the test driver patch. The poll-weight=3 output after the iothreadset call will be moved to a patch after that.
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index aa4f2a7a48..2e4923283b 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8270,6 +8270,11 @@ static const vshCmdOptDef opts_iothreadset[] = { .unwanted_positional = true, .help = N_("set the value for reduction of the IOThread polling time") }, + {.name = "poll-weight", + .type = VSH_OT_INT, + .unwanted_positional = true, + .help = N_("set the adaptive polling weight factor") + }, {.name = "thread-pool-min", .type = VSH_OT_INT, .unwanted_positional = true, @@ -8299,6 +8304,7 @@ cmdIOThreadSet(vshControl *ctl, const vshCmd *cmd) virTypedParameterPtr par; size_t npar = 0; unsigned long long poll_val; + unsigned int poll_weight; int thread_val; int rc;
@@ -8335,6 +8341,18 @@ cmdIOThreadSet(vshControl *ctl, const vshCmd *cmd) if (rc > 0) virTypedParamListAddUnsigned(params, poll_val, VIR_DOMAIN_IOTHREAD_POLL_SHRINK);
+ if ((rc = vshCommandOptUInt(ctl, cmd, "poll-weight", &poll_weight)) < 0) + return false; + if (rc > 0) { + if (poll_weight > 63) { + vshError(ctl, _("poll-weight value %1$u is out of range [0, 63]"), + poll_weight); + return false; + } + + virTypedParamListAddUInt(params, poll_weight, VIR_DOMAIN_IOTHREAD_POLL_WEIGHT); + } + if ((rc = vshCommandOptInt(ctl, cmd, "thread-pool-min", &thread_val)) < 0) return false; if (rc > 0) -- 2.54.0
Fix three pre-existing typos in the iothread poll sub-element description: - Remove spurious 'with' in 'poll with can be used to' - Add missing closing ')' after the poll-grow/shrink '0' note - Fix typo 'extensive' -> 'excessive' Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/formatdomain.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index a861f9f177..3ba981422f 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -842,13 +842,13 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` ``thread_pool_max`` which allow setting lower and upper boundary for number of worker threads for given IOThread. While the former can be value of zero, the latter can't. :since:`Since 8.5.0` - :since:`Since 9.4.0` an optional sub-element ``poll`` with can be used to + :since:`Since 9.4.0` an optional sub-element ``poll`` can be used to override the hypervisor-default interval of polling for the iothread before it switches back to events. The optional attribute ``max`` sets the maximum time polling should be used in nanoseconds. Setting ``max`` to ``0`` disables polling. Attributes ``grow`` and ``shrink`` override (or disable when set to - ``0`` the default steps for increasing/decreasing the polling interval if - the set interval is deemed insufficient or extensive. + ``0``) the default steps for increasing/decreasing the polling interval if + the set interval is deemed insufficient or excessive. ``defaultiothread`` This element represents the default event loop within hypervisor, where I/O requests from devices not assigned to a specific IOThread are processed. -- 2.54.0
On Wed, Jul 22, 2026 at 13:52:14 -0500, Jaehoon Kim wrote:
Fix three pre-existing typos in the iothread poll sub-element description: - Remove spurious 'with' in 'poll with can be used to' - Add missing closing ')' after the poll-grow/shrink '0' note - Fix typo 'extensive' -> 'excessive'
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/formatdomain.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index a861f9f177..3ba981422f 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -842,13 +842,13 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` ``thread_pool_max`` which allow setting lower and upper boundary for number of worker threads for given IOThread. While the former can be value of zero, the latter can't. :since:`Since 8.5.0` - :since:`Since 9.4.0` an optional sub-element ``poll`` with can be used to + :since:`Since 9.4.0` an optional sub-element ``poll`` can be used to override the hypervisor-default interval of polling for the iothread before it switches back to events. The optional attribute ``max`` sets the maximum time polling should be used in nanoseconds. Setting ``max`` to ``0`` disables polling. Attributes ``grow`` and ``shrink`` override (or disable when set to - ``0`` the default steps for increasing/decreasing the polling interval if - the set interval is deemed insufficient or extensive. + ``0``) the default steps for increasing/decreasing the polling interval if + the set interval is deemed insufficient or excessive. ``defaultiothread`` This element represents the default event loop within hypervisor, where I/O requests from devices not assigned to a specific IOThread are processed. -- 2.54.0
Reviewed-by: Peter Krempa <pkrempa@redhat.com> and I'll push this one right away.
On 7/29/2026 9:32 AM, Peter Krempa wrote:
On Wed, Jul 22, 2026 at 13:52:14 -0500, Jaehoon Kim wrote:
Fix three pre-existing typos in the iothread poll sub-element description: - Remove spurious 'with' in 'poll with can be used to' - Add missing closing ')' after the poll-grow/shrink '0' note - Fix typo 'extensive' -> 'excessive'
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/formatdomain.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index a861f9f177..3ba981422f 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -842,13 +842,13 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` ``thread_pool_max`` which allow setting lower and upper boundary for number of worker threads for given IOThread. While the former can be value of zero, the latter can't. :since:`Since 8.5.0` - :since:`Since 9.4.0` an optional sub-element ``poll`` with can be used to + :since:`Since 9.4.0` an optional sub-element ``poll`` can be used to override the hypervisor-default interval of polling for the iothread before it switches back to events. The optional attribute ``max`` sets the maximum time polling should be used in nanoseconds. Setting ``max`` to ``0`` disables polling. Attributes ``grow`` and ``shrink`` override (or disable when set to - ``0`` the default steps for increasing/decreasing the polling interval if - the set interval is deemed insufficient or extensive. + ``0``) the default steps for increasing/decreasing the polling interval if + the set interval is deemed insufficient or excessive. ``defaultiothread`` This element represents the default event loop within hypervisor, where I/O requests from devices not assigned to a specific IOThread are processed. -- 2.54.0
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
and I'll push this one right away.
Thanks for the review and for applying this. I'll drop this patch from the v2 series. Thanks, Jaehoon
Document the iothread poll-weight XML attribute in formatdomain. Extend the description of iothread polling controls to cover the new poll-weight attribute and how it relates to the existing poll-max, poll-grow, and poll-shrink settings. Also document the accepted range and the meaning of zero as a request for the hypervisor default. Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/formatdomain.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 3ba981422f..b173fc897b 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -815,7 +815,7 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` <iothread id="4"/> <iothread id="6"/> <iothread id="8" thread_pool_min="2" thread_pool_max="32"> - <poll max='123' grow='456' shrink='789'/> + <poll max='123' grow='456' shrink='789' weight='3'/> </iothread> </iothreadids> <defaultiothread thread_pool_min="8" thread_pool_max="16"/> @@ -849,6 +849,12 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` polling. Attributes ``grow`` and ``shrink`` override (or disable when set to ``0``) the default steps for increasing/decreasing the polling interval if the set interval is deemed insufficient or excessive. + :since:`Since 12.6.0` the optional attribute ``weight`` sets the shift value + for the adaptive polling algorithm, controlling how much the most recent + event interval affects the next polling duration calculation. Larger values + decrease the weight of recent events, producing more gradual adjustments. + Valid range is ``[0, 63]``. Setting ``weight`` to ``0`` lets the hypervisor + select its default value. ``defaultiothread`` This element represents the default event loop within hypervisor, where I/O requests from devices not assigned to a specific IOThread are processed. -- 2.54.0
On Wed, Jul 22, 2026 at 13:52:15 -0500, Jaehoon Kim wrote:
Document the iothread poll-weight XML attribute in formatdomain.
Extend the description of iothread polling controls to cover the new poll-weight attribute and how it relates to the existing poll-max, poll-grow, and poll-shrink settings. Also document the accepted range and the meaning of zero as a request for the hypervisor default.
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/formatdomain.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
Please move this patch right after the patch which adds the XML parser/formatter bits.
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 3ba981422f..b173fc897b 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -815,7 +815,7 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` <iothread id="4"/> <iothread id="6"/> <iothread id="8" thread_pool_min="2" thread_pool_max="32"> - <poll max='123' grow='456' shrink='789'/> + <poll max='123' grow='456' shrink='789' weight='3'/> </iothread> </iothreadids> <defaultiothread thread_pool_min="8" thread_pool_max="16"/> @@ -849,6 +849,12 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` polling. Attributes ``grow`` and ``shrink`` override (or disable when set to ``0``) the default steps for increasing/decreasing the polling interval if the set interval is deemed insufficient or excessive. + :since:`Since 12.6.0` the optional attribute ``weight`` sets the shift value
12.7.0
+ for the adaptive polling algorithm, controlling how much the most recent + event interval affects the next polling duration calculation. Larger values + decrease the weight of recent events, producing more gradual adjustments. + Valid range is ``[0, 63]``. Setting ``weight`` to ``0`` lets the hypervisor + select its default value.
Also omitting it is equivalent to 0. As said in the previous patch I'm not sure if we shouldn't drop the 'weight' parameter in the XML if it's parsed as 0 to have only 1 manifestation of the default.
``defaultiothread`` This element represents the default event loop within hypervisor, where I/O requests from devices not assigned to a specific IOThread are processed. -- 2.54.0
On 7/29/2026 9:34 AM, Peter Krempa wrote:
Document the iothread poll-weight XML attribute in formatdomain.
Extend the description of iothread polling controls to cover the new poll-weight attribute and how it relates to the existing poll-max, poll-grow, and poll-shrink settings. Also document the accepted range and the meaning of zero as a request for the hypervisor default.
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> --- docs/formatdomain.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) Please move this patch right after the patch which adds the XML
On Wed, Jul 22, 2026 at 13:52:15 -0500, Jaehoon Kim wrote: parser/formatter bits.
I'll move this patch right after the XML parser/formatter patch in v2.
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 3ba981422f..b173fc897b 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -815,7 +815,7 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` <iothread id="4"/> <iothread id="6"/> <iothread id="8" thread_pool_min="2" thread_pool_max="32"> - <poll max='123' grow='456' shrink='789'/> + <poll max='123' grow='456' shrink='789' weight='3'/> </iothread> </iothreadids> <defaultiothread thread_pool_min="8" thread_pool_max="16"/> @@ -849,6 +849,12 @@ host/guest with many LUNs. :since:`Since 1.2.8 (QEMU only)` polling. Attributes ``grow`` and ``shrink`` override (or disable when set to ``0``) the default steps for increasing/decreasing the polling interval if the set interval is deemed insufficient or excessive. + :since:`Since 12.6.0` the optional attribute ``weight`` sets the shift value 12.7.0
I'll fix it.
+ for the adaptive polling algorithm, controlling how much the most recent + event interval affects the next polling duration calculation. Larger values + decrease the weight of recent events, producing more gradual adjustments. + Valid range is ``[0, 63]``. Setting ``weight`` to ``0`` lets the hypervisor + select its default value. Also omitting it is equivalent to 0. As said in the previous patch I'm not sure if we shouldn't drop the 'weight' parameter in the XML if it's parsed as 0 to have only 1 manifestation of the default.
I agree that having two representations of the default value is not ideal. However, I would prefer to keep accepting an explicit weight='0' value for consistency with the existing grow and shrink attributes, which use the same convention. Also, keeping 0 as an explicit value leaves room for potential future use if 0 gains a different meaning,. Therefore, I would prefer to preserve a explicit weight='0' away at this point. Thanks, Jaehoon
``defaultiothread`` This element represents the default event loop within hypervisor, where I/O requests from devices not assigned to a specific IOThread are processed. -- 2.54.0
participants (3)
-
JAEHOON KIM -
Jaehoon Kim -
Peter Krempa