From: Jaehoon Kim <jhkim@linux.ibm.com>
To: qemu-devel@nongnu.org, qemu-block@nongnu.org
Cc: mjrosato@linux.ibm.com, farman@linux.ibm.com,
pbonzini@redhat.com, stefanha@redhat.com, fam@euphon.net,
armbru@redhat.com, eblake@redhat.com, berrange@redhat.com,
eduardo@habkost.net, dave@treblig.org, sw@weilnetz.de,
Jaehoon Kim <jhkim@linux.ibm.com>
Subject: [PATCH RFC v2 3/3] qapi/iothread: introduce poll-weight parameter for aio-poll
Date: Mon, 23 Mar 2026 08:54:51 -0500 [thread overview]
Message-ID: <20260323135451.579655-4-jhkim@linux.ibm.com> (raw)
In-Reply-To: <20260323135451.579655-1-jhkim@linux.ibm.com>
Introduce a configurable poll-weight parameter for adaptive polling
in IOThread. This parameter replaces the hardcoded POLL_WEIGHT_SHIFT
constant, allowing runtime control over how much the most recent
event interval affects the next polling duration calculation.
The poll-weight parameter uses a shift value where larger values
decrease the weight of the current interval, enabling more gradual
adjustments. When set to 0, a default value of 3 is used (meaning
the current interval contributes approximately 1/8 to the weighted
average).
This patch also removes the hardcoded default values for poll-grow
and poll-shrink parameters from the grow_polling_time() and
shrink_polling_time() functions, as these defaults are now properly
initialized in iothread.c during IOThread creation.
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com>
---
include/qemu/aio.h | 4 +++-
include/system/iothread.h | 1 +
iothread.c | 34 ++++++++++++++++++++++++++++++-
monitor/hmp-cmds.c | 1 +
qapi/misc.json | 7 +++++++
qapi/qom.json | 8 +++++++-
qemu-options.hx | 7 ++++++-
tests/unit/test-nested-aio-poll.c | 2 +-
util/aio-posix.c | 17 +++++-----------
util/aio-win32.c | 3 ++-
util/async.c | 1 +
11 files changed, 67 insertions(+), 18 deletions(-)
diff --git a/include/qemu/aio.h b/include/qemu/aio.h
index 6c77a190e9..50b8db2712 100644
--- a/include/qemu/aio.h
+++ b/include/qemu/aio.h
@@ -311,6 +311,7 @@ struct AioContext {
int64_t poll_max_ns; /* maximum polling time in nanoseconds */
int64_t poll_grow; /* polling time growth factor */
int64_t poll_shrink; /* polling time shrink factor */
+ int64_t poll_weight; /* weight of current interval in calculation */
/* AIO engine parameters */
int64_t aio_max_batch; /* maximum number of requests in a batch */
@@ -792,12 +793,13 @@ void aio_context_destroy(AioContext *ctx);
* @max_ns: how long to busy poll for, in nanoseconds
* @grow: polling time growth factor
* @shrink: polling time shrink factor
+ * @weight: weight factor applied to the current polling interval
*
* Poll mode can be disabled by setting poll_max_ns to 0.
*/
void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
int64_t grow, int64_t shrink,
- Error **errp);
+ int64_t weight, Error **errp);
/**
* aio_context_set_aio_params:
diff --git a/include/system/iothread.h b/include/system/iothread.h
index e26d13c6c7..6ea57ed126 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -38,6 +38,7 @@ struct IOThread {
int64_t poll_max_ns;
int64_t poll_grow;
int64_t poll_shrink;
+ int64_t poll_weight;
};
typedef struct IOThread IOThread;
diff --git a/iothread.c b/iothread.c
index caf68e0764..0389b8f7a8 100644
--- a/iothread.c
+++ b/iothread.c
@@ -32,8 +32,14 @@
* workloads.
*/
#define IOTHREAD_POLL_MAX_NS_DEFAULT 32768ULL
+#define IOTHREAD_POLL_GROW_DEFAULT 2ULL
+#define IOTHREAD_POLL_SHRINK_DEFAULT 2ULL
+#define IOTHREAD_POLL_WEIGHT_DEFAULT 3ULL
#else
#define IOTHREAD_POLL_MAX_NS_DEFAULT 0ULL
+#define IOTHREAD_POLL_GROW_DEFAULT 0ULL
+#define IOTHREAD_POLL_SHRINK_DEFAULT 0ULL
+#define IOTHREAD_POLL_WEIGHT_DEFAULT 0ULL
#endif
static void *iothread_run(void *opaque)
@@ -103,6 +109,10 @@ static void iothread_instance_init(Object *obj)
IOThread *iothread = IOTHREAD(obj);
iothread->poll_max_ns = IOTHREAD_POLL_MAX_NS_DEFAULT;
+ iothread->poll_grow = IOTHREAD_POLL_GROW_DEFAULT;
+ iothread->poll_shrink = IOTHREAD_POLL_SHRINK_DEFAULT;
+ iothread->poll_weight = IOTHREAD_POLL_WEIGHT_DEFAULT;
+
iothread->thread_id = -1;
qemu_sem_init(&iothread->init_done_sem, 0);
/* By default, we don't run gcontext */
@@ -164,6 +174,7 @@ static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
iothread->poll_max_ns,
iothread->poll_grow,
iothread->poll_shrink,
+ iothread->poll_weight,
errp);
if (*errp) {
return;
@@ -233,6 +244,9 @@ static IOThreadParamInfo poll_grow_info = {
static IOThreadParamInfo poll_shrink_info = {
"poll-shrink", offsetof(IOThread, poll_shrink),
};
+static IOThreadParamInfo poll_weight_info = {
+ "poll-weight", offsetof(IOThread, poll_weight),
+};
static void iothread_get_param(Object *obj, Visitor *v,
const char *name, IOThreadParamInfo *info, Error **errp)
@@ -260,7 +274,19 @@ static bool iothread_set_param(Object *obj, Visitor *v,
return false;
}
- *field = value;
+ if (value == 0) {
+ if (info->offset == offsetof(IOThread, poll_grow)) {
+ *field = IOTHREAD_POLL_GROW_DEFAULT;
+ } else if (info->offset == offsetof(IOThread, poll_shrink)) {
+ *field = IOTHREAD_POLL_SHRINK_DEFAULT;
+ } else if (info->offset == offsetof(IOThread, poll_weight)) {
+ *field = IOTHREAD_POLL_WEIGHT_DEFAULT;
+ } else {
+ *field = value;
+ }
+ } else {
+ *field = value;
+ }
return true;
}
@@ -288,6 +314,7 @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
iothread->poll_max_ns,
iothread->poll_grow,
iothread->poll_shrink,
+ iothread->poll_weight,
errp);
}
}
@@ -311,6 +338,10 @@ static void iothread_class_init(ObjectClass *klass, const void *class_data)
iothread_get_poll_param,
iothread_set_poll_param,
NULL, &poll_shrink_info);
+ object_class_property_add(klass, "poll-weight", "int",
+ iothread_get_poll_param,
+ iothread_set_poll_param,
+ NULL, &poll_weight_info);
}
static const TypeInfo iothread_info = {
@@ -356,6 +387,7 @@ static int query_one_iothread(Object *object, void *opaque)
info->poll_max_ns = iothread->poll_max_ns;
info->poll_grow = iothread->poll_grow;
info->poll_shrink = iothread->poll_shrink;
+ info->poll_weight = iothread->poll_weight;
info->aio_max_batch = iothread->parent_obj.aio_max_batch;
QAPI_LIST_APPEND(*tail, info);
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index bad034937a..75b6e7fa65 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -206,6 +206,7 @@ void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
monitor_printf(mon, " poll-max-ns=%" PRId64 "\n", value->poll_max_ns);
monitor_printf(mon, " poll-grow=%" PRId64 "\n", value->poll_grow);
monitor_printf(mon, " poll-shrink=%" PRId64 "\n", value->poll_shrink);
+ monitor_printf(mon, " poll-weight=%" PRId64 "\n", value->poll_weight);
monitor_printf(mon, " aio-max-batch=%" PRId64 "\n",
value->aio_max_batch);
}
diff --git a/qapi/misc.json b/qapi/misc.json
index 28c641fe2f..39d17010bc 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -85,6 +85,12 @@
# @poll-shrink: how many ns will be removed from polling time, 0 means
# that it's not configured (since 2.9)
#
+# @poll-weight: the weight factor for adaptive polling.
+# Determines how much the current event interval contributes to
+# the next polling time calculation. Valid values are 1 or
+# greater. 0 selects the system default value which is current 3
+# (since 10.2)
+#
# @aio-max-batch: maximum number of requests in a batch for the AIO
# engine, 0 means that the engine will use its default (since 6.1)
#
@@ -96,6 +102,7 @@
'poll-max-ns': 'int',
'poll-grow': 'int',
'poll-shrink': 'int',
+ 'poll-weight': 'int',
'aio-max-batch': 'int' } }
##
diff --git a/qapi/qom.json b/qapi/qom.json
index c653248f85..feb80b6cfe 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -606,6 +606,11 @@
# algorithm detects it is spending too long polling without
# encountering events. 0 selects a default behaviour (default: 0)
#
+# @poll-weight: the weight factor for adaptive polling.
+# Determines how much the current event interval contributes to
+# the next polling time calculation. Valid values are 1 or
+# greater. If set to 0, the default value of 3 is used.
+#
# The @aio-max-batch option is available since 6.1.
#
# Since: 2.0
@@ -614,7 +619,8 @@
'base': 'EventLoopBaseProperties',
'data': { '*poll-max-ns': 'int',
'*poll-grow': 'int',
- '*poll-shrink': 'int' } }
+ '*poll-shrink': 'int',
+ '*poll-weight': 'int' } }
##
# @MainLoopProperties:
diff --git a/qemu-options.hx b/qemu-options.hx
index 69e5a874c1..8ddf6c8d36 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -6413,7 +6413,7 @@ SRST
CN=laptop.example.com,O=Example Home,L=London,ST=London,C=GB
- ``-object iothread,id=id,poll-max-ns=poll-max-ns,poll-grow=poll-grow,poll-shrink=poll-shrink,aio-max-batch=aio-max-batch``
+ ``-object iothread,id=id,poll-max-ns=poll-max-ns,poll-grow=poll-grow,poll-shrink=poll-shrink,poll-weight=poll-weight,aio-max-batch=aio-max-batch``
Creates a dedicated event loop thread that devices can be
assigned to. This is known as an IOThread. By default device
emulation happens in vCPU threads or the main event loop thread.
@@ -6449,6 +6449,11 @@ SRST
the polling time when the algorithm detects it is spending too
long polling without encountering events.
+ The ``poll-weight`` parameter is the weight factor used in the
+ adaptive polling algorithm. It determines how much the most
+ recent event interval affects the calculation of the next
+ polling duration.
+
The ``aio-max-batch`` parameter is the maximum number of requests
in a batch for the AIO engine, 0 means that the engine will use
its default.
diff --git a/tests/unit/test-nested-aio-poll.c b/tests/unit/test-nested-aio-poll.c
index 9ab1ad08a7..4c38f36fd4 100644
--- a/tests/unit/test-nested-aio-poll.c
+++ b/tests/unit/test-nested-aio-poll.c
@@ -81,7 +81,7 @@ static void test(void)
qemu_set_current_aio_context(td.ctx);
/* Enable polling */
- aio_context_set_poll_params(td.ctx, 1000000, 2, 2, &error_abort);
+ aio_context_set_poll_params(td.ctx, 1000000, 2, 2, 3, &error_abort);
/* Make the event notifier active (set) right away */
event_notifier_init(&td.poll_notifier, 1);
diff --git a/util/aio-posix.c b/util/aio-posix.c
index 2b3522f2f9..13b7f94911 100644
--- a/util/aio-posix.c
+++ b/util/aio-posix.c
@@ -29,7 +29,6 @@
/* Stop userspace polling on a handler if it isn't active for some time */
#define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND)
-#define POLL_WEIGHT_SHIFT (3)
static void adjust_block_ns(AioContext *ctx, int64_t block_ns);
static void grow_polling_time(AioContext *ctx, int64_t block_ns);
@@ -593,10 +592,6 @@ static void shrink_polling_time(AioContext *ctx, int64_t block_ns)
int64_t old = ctx->poll_ns;
int64_t shrink = ctx->poll_shrink;
- if (shrink == 0) {
- shrink = 2;
- }
-
if (block_ns < (ctx->poll_ns / shrink)) {
ctx->poll_ns /= shrink;
}
@@ -610,10 +605,6 @@ static void grow_polling_time(AioContext *ctx, int64_t block_ns)
int64_t old = ctx->poll_ns;
int64_t grow = ctx->poll_grow;
- if (grow == 0) {
- grow = 2;
- }
-
if (block_ns > ctx->poll_ns * grow) {
ctx->poll_ns = block_ns;
} else {
@@ -640,8 +631,8 @@ static void adjust_block_ns(AioContext *ctx, int64_t block_ns)
* poll.ns to smooth out polling time adjustments.
*/
node->poll.ns = node->poll.ns
- ? (node->poll.ns - (node->poll.ns >> POLL_WEIGHT_SHIFT))
- + (block_ns >> POLL_WEIGHT_SHIFT) : block_ns;
+ ? (node->poll.ns - (node->poll.ns >> ctx->poll_weight))
+ + (block_ns >> ctx->poll_weight) : block_ns;
if (node->poll.ns > ctx->poll_max_ns) {
node->poll.ns = 0;
@@ -831,7 +822,8 @@ void aio_context_destroy(AioContext *ctx)
}
void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
- int64_t grow, int64_t shrink, Error **errp)
+ int64_t grow, int64_t shrink,
+ int64_t weight, Error **errp)
{
AioHandler *node;
@@ -848,6 +840,7 @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
ctx->poll_max_ns = max_ns;
ctx->poll_grow = grow;
ctx->poll_shrink = shrink;
+ ctx->poll_weight = weight;
ctx->poll_ns = 0;
aio_notify(ctx);
diff --git a/util/aio-win32.c b/util/aio-win32.c
index 6e6f699e4b..1985843233 100644
--- a/util/aio-win32.c
+++ b/util/aio-win32.c
@@ -429,7 +429,8 @@ void aio_context_destroy(AioContext *ctx)
}
void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
- int64_t grow, int64_t shrink, Error **errp)
+ int64_t grow, int64_t shrink,
+ int64_t weight, Error **errp)
{
if (max_ns) {
error_setg(errp, "AioContext polling is not implemented on Windows");
diff --git a/util/async.c b/util/async.c
index 9d3627566f..741fcfd6a7 100644
--- a/util/async.c
+++ b/util/async.c
@@ -609,6 +609,7 @@ AioContext *aio_context_new(Error **errp)
ctx->poll_ns = 0;
ctx->poll_grow = 0;
ctx->poll_shrink = 0;
+ ctx->poll_weight = 0;
ctx->aio_max_batch = 0;
--
2.50.1
next prev parent reply other threads:[~2026-03-23 13:55 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-23 13:54 [PATCH RFC v2 0/3] improve aio-polling efficiency Jaehoon Kim
2026-03-23 13:54 ` [PATCH RFC v2 1/3] aio-poll: avoid unnecessary polling time computation Jaehoon Kim
2026-03-25 17:22 ` Stefan Hajnoczi
2026-03-26 18:17 ` JAEHOON KIM
2026-03-26 18:34 ` Stefan Hajnoczi
2026-03-23 13:54 ` [PATCH RFC v2 2/3] aio-poll: refine iothread polling using weighted handler intervals Jaehoon Kim
2026-03-25 20:37 ` Stefan Hajnoczi
2026-03-27 5:02 ` JAEHOON KIM
2026-03-30 19:17 ` Stefan Hajnoczi
2026-03-31 20:42 ` JAEHOON KIM
2026-03-23 13:54 ` Jaehoon Kim [this message]
2026-03-25 14:04 ` [PATCH RFC v2 3/3] qapi/iothread: introduce poll-weight parameter for aio-poll Markus Armbruster
2026-03-26 15:55 ` JAEHOON KIM
2026-03-27 5:49 ` Markus Armbruster
2026-03-27 14:23 ` JAEHOON KIM
2026-03-25 16:52 ` Stefan Hajnoczi
2026-03-25 16:56 ` Stefan Hajnoczi
2026-03-26 16:13 ` JAEHOON KIM
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260323135451.579655-4-jhkim@linux.ibm.com \
--to=jhkim@linux.ibm.com \
--cc=armbru@redhat.com \
--cc=berrange@redhat.com \
--cc=dave@treblig.org \
--cc=eblake@redhat.com \
--cc=eduardo@habkost.net \
--cc=fam@euphon.net \
--cc=farman@linux.ibm.com \
--cc=mjrosato@linux.ibm.com \
--cc=pbonzini@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@redhat.com \
--cc=sw@weilnetz.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.