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 */
* @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:
#define TYPE_IOTHREAD "iothread"
+#ifdef CONFIG_POSIX
+/*
+ * Benchmark results from 2016 on NVMe SSD drives show max polling times around
+ * 16-32 microseconds yield IOPS improvements for both iodepth=1 and iodepth=32
+ * 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
+
struct IOThread {
EventLoopBase parent_obj;
int64_t poll_max_ns;
int64_t poll_grow;
int64_t poll_shrink;
+ int64_t poll_weight;
};
typedef struct IOThread IOThread;
#include "qemu/rcu.h"
#include "qemu/main-loop.h"
-
-#ifdef CONFIG_POSIX
-/* Benchmark results from 2016 on NVMe SSD drives show max polling times around
- * 16-32 microseconds yield IOPS improvements for both iodepth=1 and iodepth=32
- * workloads.
- */
-#define IOTHREAD_POLL_MAX_NS_DEFAULT 32768ULL
-#else
-#define IOTHREAD_POLL_MAX_NS_DEFAULT 0ULL
-#endif
-
static void *iothread_run(void *opaque)
{
IOThread *iothread = opaque;
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 */
iothread->poll_max_ns,
iothread->poll_grow,
iothread->poll_shrink,
+ iothread->poll_weight,
errp);
if (*errp) {
return;
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)
return false;
}
- if (value < 0) {
+ if (info->offset == offsetof(IOThread, poll_weight)) {
+ if (value < 0 || value > 63) {
+ error_setg(errp, "%s value must be in range [0, 63]",
+ info->name);
+ return false;
+ }
+ } else if (value < 0) {
error_setg(errp, "%s value must be in range [0, %" PRId64 "]",
info->name, INT64_MAX);
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;
}
iothread->poll_max_ns,
iothread->poll_grow,
iothread->poll_shrink,
+ iothread->poll_weight,
errp);
}
}
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 = {
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);
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);
}
# @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 (since 11.1)
+#
# @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)
#
'poll-max-ns': 'int',
'poll-grow': 'int',
'poll-shrink': 'int',
+ 'poll-weight': 'int',
'aio-max-batch': 'int' } }
##
# 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 most recent event interval affects the next
+# polling duration calculation. If set to 0, the system default
+# value of 3 is used. Typical values: 1 (high weight on recent
+# interval), 2-4 (moderate weight on recent interval).
+# (default: 0) (since 11.1)
+#
# The @aio-max-batch option is available since 6.1.
#
# Since: 2.0
'base': 'EventLoopBaseProperties',
'data': { '*poll-max-ns': 'int',
'*poll-grow': 'int',
- '*poll-shrink': 'int' } }
+ '*poll-shrink': 'int',
+ '*poll-weight': 'int' } }
##
# @MainLoopProperties:
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.
the polling time when the algorithm detects it is spending too
long polling without encountering events.
+ The ``poll-weight`` parameter is the weight factor for adaptive
+ polling. It determines how much the most recent event interval
+ affects the next polling duration calculation. If set to 0, the
+ system default value of 3 is used. Typical values: 1 (high weight
+ on recent interval), 2-4 (moderate weight on recent interval).
+
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.
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);
/* 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 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)
{
- 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, ctx->poll_ns);
- } else if (block_ns > ctx->poll_ns) {
+ if (block_ns > ctx->poll_ns) {
/* There is room to grow, poll longer */
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 {
}
trace_poll_grow(ctx, old, ctx->poll_ns);
+ } else if (block_ns < (ctx->poll_ns / ctx->poll_shrink)) {
+ int64_t old = ctx->poll_ns;
+ ctx->poll_ns /= ctx->poll_shrink;
+
+ trace_poll_shrink(ctx, old, ctx->poll_ns);
}
}
* 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;
+ ? (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;
}
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;
* is used once.
*/
ctx->poll_max_ns = max_ns;
- ctx->poll_grow = grow;
- ctx->poll_shrink = shrink;
+ ctx->poll_grow = (grow ? grow : IOTHREAD_POLL_GROW_DEFAULT);
+ ctx->poll_shrink = (shrink ? shrink : IOTHREAD_POLL_SHRINK_DEFAULT);
+ ctx->poll_weight = (weight ? weight : IOTHREAD_POLL_WEIGHT_DEFAULT);
ctx->poll_ns = 0;
aio_notify(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");
ctx->poll_ns = 0;
ctx->poll_grow = 0;
ctx->poll_shrink = 0;
+ ctx->poll_weight = 0;
ctx->aio_max_batch = 0;