/* 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_polling_time(AioContext *ctx, AioPolledEvent *poll,
- int64_t block_ns);
+static void update_handler_poll_times(AioContext *ctx, int64_t block_ns,
+ int64_t dispatch_time);
+static void adjust_polling_time(AioContext *ctx, int64_t block_ns);
bool aio_poll_disabled(AioContext *ctx)
{
static bool aio_dispatch_ready_handlers(AioContext *ctx,
AioHandlerList *ready_list,
- int64_t block_ns)
+ int64_t dispatch_time)
{
bool progress = false;
AioHandler *node;
progress = aio_dispatch_handler(ctx, node) || progress;
/*
- * Adjust polling time only after aio_dispatch_handler(), which can
- * add the handler to ctx->poll_aio_handlers.
+ * Update last_dispatch_timestamp to mark this as an active
+ * handler for polling time adjustment and prevent idle removal.
*/
if (ctx->poll_max_ns && QLIST_IS_INSERTED(node, node_poll)) {
- adjust_polling_time(ctx, &node->poll, block_ns);
+ node->last_dispatch_timestamp = dispatch_time;
}
}
ctx->fdmon_ops->dispatch(ctx);
}
- /* block_ns is 0 because polling is disabled in the glib event loop */
+ /* Set now to 0 as polling is disabled in the glib event loop */
aio_dispatch_ready_handlers(ctx, &ready_list, 0);
aio_free_deleted_handlers(ctx);
QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) {
if (node->io_poll(node->opaque)) {
aio_add_poll_ready_handler(ready_list, node);
-
- node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS;
-
/*
* Polling was successful, exit try_poll_mode immediately
* to adjust the next polling time.
}
QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) {
- if (node->poll_idle_timeout == 0LL) {
- node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS;
- } else if (now >= node->poll_idle_timeout) {
+ if (node->poll_ready == false &&
+ now >= node->last_dispatch_timestamp + POLL_IDLE_INTERVAL_NS) {
trace_poll_remove(ctx, node, node->pfd.fd);
- node->poll_idle_timeout = 0LL;
+ node->last_dispatch_timestamp = 0LL;
QLIST_SAFE_REMOVE(node, node_poll);
if (ctx->poll_started && node->io_poll_end) {
node->io_poll_end(node->opaque);
static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list,
int64_t *timeout)
{
- AioHandler *node;
int64_t max_ns;
if (QLIST_EMPTY_RCU(&ctx->poll_aio_handlers)) {
return false;
}
- max_ns = 0;
- QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) {
- max_ns = MAX(max_ns, node->poll.ns);
- }
- max_ns = qemu_soonest_timeout(*timeout, max_ns);
+ max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns);
if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) {
/*
return false;
}
-static void adjust_polling_time(AioContext *ctx, AioPolledEvent *poll,
- int64_t block_ns)
+static void adjust_polling_time(AioContext *ctx, int64_t block_ns)
{
- if (block_ns <= poll->ns) {
- /* This is the sweet spot, no adjustment needed */
- } else if (block_ns > ctx->poll_max_ns) {
- /* We'd have to poll for too long, poll less */
- int64_t old = poll->ns;
-
- if (ctx->poll_shrink) {
- poll->ns /= ctx->poll_shrink;
- } else {
- poll->ns = 0;
+ if (block_ns < ctx->poll_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;
}
- trace_poll_shrink(ctx, old, poll->ns);
- } else if (poll->ns < ctx->poll_max_ns &&
- block_ns < ctx->poll_max_ns) {
+ trace_poll_shrink(ctx, old, ctx->poll_ns);
+ } else if (block_ns > ctx->poll_ns) {
/* There is room to grow, poll longer */
- int64_t old = poll->ns;
+ int64_t old = ctx->poll_ns;
int64_t grow = ctx->poll_grow;
if (grow == 0) {
grow = 2;
}
- if (poll->ns) {
- poll->ns *= grow;
+ if (block_ns > ctx->poll_ns * grow) {
+ ctx->poll_ns = block_ns;
} else {
- poll->ns = 4000; /* start polling at 4 microseconds */
+ ctx->poll_ns *= grow;
}
- if (poll->ns > ctx->poll_max_ns) {
- poll->ns = ctx->poll_max_ns;
+ if (ctx->poll_ns > ctx->poll_max_ns) {
+ ctx->poll_ns = ctx->poll_max_ns;
}
- trace_poll_grow(ctx, old, poll->ns);
+ trace_poll_grow(ctx, old, ctx->poll_ns);
+ }
+}
+
+static void update_handler_poll_times(AioContext *ctx, int64_t block_ns,
+ int64_t dispatch_time)
+{
+ AioHandler *node;
+ int64_t max_poll_ns = -1;
+
+ QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) {
+ if (node->last_dispatch_timestamp == dispatch_time) {
+ /*
+ * Active handler: had an event in this aio_poll() call.
+ * Update poll.ns using a weighted average of the current
+ * block_ns and previous poll.ns to smooth adjustments.
+ */
+ node->poll.ns = node->poll.ns
+ ? (node->poll.ns - (node->poll.ns >> POLL_WEIGHT_SHIFT))
+ + (block_ns >> POLL_WEIGHT_SHIFT) : block_ns;
+
+ if (node->poll.ns > ctx->poll_max_ns) {
+ node->poll.ns = 0;
+ }
+ /*
+ * Track the maximum poll.ns among active handlers to
+ * calculate the next polling time.
+ */
+ max_poll_ns = MAX(max_poll_ns, node->poll.ns);
+ } else {
+ /*
+ * Inactive handler: no event in this aio_poll() call but
+ * was active before. Increase poll.ns by block_ns. If it
+ * exceeds poll_max_ns, reset to 0 until next event.
+ */
+ if (node->poll.ns != 0) {
+ node->poll.ns += block_ns;
+ if (node->poll.ns > ctx->poll_max_ns) {
+ node->poll.ns = 0;
+ }
+ }
+ }
+ }
+ if (max_poll_ns >= 0) {
+ adjust_polling_time(ctx, max_poll_ns);
}
}
int64_t timeout;
int64_t start = 0;
int64_t block_ns = 0;
+ int64_t dispatch_ns = 0;
/*
* There cannot be two concurrent aio_poll calls for the same AioContext (or
/* Calculate blocked time for adaptive polling */
if (ctx->poll_max_ns) {
- block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start;
+ dispatch_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ block_ns = dispatch_ns - start;
}
if (ctx->fdmon_ops->dispatch) {
}
progress |= aio_bh_poll(ctx);
- progress |= aio_dispatch_ready_handlers(ctx, &ready_list, block_ns);
+ progress |= aio_dispatch_ready_handlers(ctx, &ready_list, dispatch_ns);
aio_free_deleted_handlers(ctx);
+ if (ctx->poll_max_ns) {
+ update_handler_poll_times(ctx, block_ns, dispatch_ns);
+ }
+
qemu_lockcnt_dec(&ctx->list_lock);
progress |= timerlistgroup_run_timers(&ctx->tlg);
ctx->poll_max_ns = max_ns;
ctx->poll_grow = grow;
ctx->poll_shrink = shrink;
+ ctx->poll_ns = 0;
aio_notify(ctx);
}