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
+ * 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

Reply via email to