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 <[email protected]>
> ---
>  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.

Reply via email to