Wire the poll-weight setting into the QEMU command line, monitor, and
driver-side parameter handling.

Add poll-weight to the iothread object properties when building the
QEMU command line. Add the live setter in the monitor JSON layer using
a new VIR_IOTHREAD_SET_PROP_UINT macro consistent with the existing UL
variant. Wire up typed parameter parsing with an explicit range check
for [0, 63] and propagate the value into the persistent domain
definition on hotplug. Guard the live path with a capability check.

Add a CAPS check in the domain validator and remove the now-unnecessary
break from the thread pool validation loop since the loop must continue
to check poll-weight on every iothread.

Also add VIR_DOMAIN_IOTHREAD_POLL_WEIGHT typed parameter constant and
a qemuxmlconf test case for the new functionality.

Signed-off-by: Jaehoon Kim <[email protected]>
---
 include/libvirt/libvirt-domain.h              | 15 +++++
 src/qemu/qemu_command.c                       |  6 ++
 src/qemu/qemu_driver.c                        | 33 +++++++++++
 src/qemu/qemu_monitor_json.c                  | 13 +++++
 src/qemu/qemu_validate.c                      | 10 +++-
 ...s-poll-weight-outofrange.x86_64-latest.err |  1 +
 .../iothreads-ids-poll-weight-outofrange.xml  | 58 +++++++++++++++++++
 ...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, 292 insertions(+), 2 deletions(-)
 create mode 100644 
tests/qemuxmlconfdata/iothreads-ids-poll-weight-outofrange.x86_64-latest.err
 create mode 100644 
tests/qemuxmlconfdata/iothreads-ids-poll-weight-outofrange.xml
 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 bb524f41ee..33dc700755 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -2748,6 +2748,21 @@ 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.
+ *
+ * Accepted type is VIR_TYPED_PARAM_UINT.
+ *
+ * Since: 12.7.0
+ */
+# define VIR_DOMAIN_IOTHREAD_POLL_WEIGHT "poll_weight"
+
 /**
  * VIR_DOMAIN_IOTHREAD_THREAD_POOL_MIN:
  *
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index a76a034158..19c57eb09a 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -7687,6 +7687,12 @@ qemuBuildIOThreadCommandLine(virCommand *cmd,
                                   NULL) < 0)
             return -1;
 
+        if (iothread->set_poll_weight &&
+            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 842b56a2c2..e973c91efd 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5292,6 +5292,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;
 
@@ -5384,6 +5389,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
@@ -5419,6 +5429,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)
@@ -5617,6 +5641,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;
 
@@ -5747,6 +5778,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,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index b02c0722d9..7e3249cd47 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -7248,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 ae1edf435a..40be2e957e 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-outofrange.x86_64-latest.err 
b/tests/qemuxmlconfdata/iothreads-ids-poll-weight-outofrange.x86_64-latest.err
new file mode 100644
index 0000000000..d3dbab3e50
--- /dev/null
+++ 
b/tests/qemuxmlconfdata/iothreads-ids-poll-weight-outofrange.x86_64-latest.err
@@ -0,0 +1 @@
+unsupported configuration: poll weight 64 is out of range [0, 63]
diff --git a/tests/qemuxmlconfdata/iothreads-ids-poll-weight-outofrange.xml 
b/tests/qemuxmlconfdata/iothreads-ids-poll-weight-outofrange.xml
new file mode 100644
index 0000000000..26bbd8ee45
--- /dev/null
+++ b/tests/qemuxmlconfdata/iothreads-ids-poll-weight-outofrange.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='64'/>
+    </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.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 f9e426f4eb..298dded032 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_LATEST_PARSE_ERROR("iothreads-ids-poll-weight-outofrange");
     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