1
The following changes since commit 801f3db7564dcce8a37a70833c0abe40ec19f8ce:
1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
2
2
3
Merge remote-tracking branch 'remotes/philmd/tags/kconfig-20210720' into staging (2021-07-20 19:30:28 +0100)
3
Merge tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu into staging (2022-05-04 03:42:49 -0700)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
8
8
9
for you to fetch changes up to d7ddd0a1618a75b31dc308bb37365ce1da972154:
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
10
10
11
linux-aio: limit the batch size using `aio-max-batch` parameter (2021-07-21 13:47:50 +0100)
11
util/event-loop-base: Introduce options to set the thread pool size (2022-05-04 17:02:19 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Pull request
14
Pull request
15
15
16
Stefano's performance regression fix for commit 2558cb8dd4 ("linux-aio:
16
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
17
increasing MAX_EVENTS to a larger hardcoded value").
17
used for async I/O.
18
18
19
----------------------------------------------------------------
19
----------------------------------------------------------------
20
20
21
Stefano Garzarella (3):
21
Nicolas Saenz Julienne (3):
22
iothread: generalize iothread_set_param/iothread_get_param
22
Introduce event-loop-base abstract class
23
iothread: add aio-max-batch parameter
23
util/main-loop: Introduce the main loop into QOM
24
linux-aio: limit the batch size using `aio-max-batch` parameter
24
util/event-loop-base: Introduce options to set the thread pool size
25
25
26
qapi/misc.json | 6 ++-
26
qapi/qom.json | 43 ++++++++--
27
qapi/qom.json | 7 +++-
27
meson.build | 26 +++---
28
include/block/aio.h | 12 ++++++
28
include/block/aio.h | 10 +++
29
include/sysemu/iothread.h | 3 ++
29
include/block/thread-pool.h | 3 +
30
block/linux-aio.c | 9 ++++-
30
include/qemu/main-loop.h | 10 +++
31
iothread.c | 82 ++++++++++++++++++++++++++++++++++-----
31
include/sysemu/event-loop-base.h | 41 +++++++++
32
monitor/hmp-cmds.c | 2 +
32
include/sysemu/iothread.h | 6 +-
33
util/aio-posix.c | 12 ++++++
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
34
util/aio-win32.c | 5 +++
34
iothread.c | 68 +++++----------
35
util/async.c | 2 +
35
util/aio-posix.c | 1 +
36
qemu-options.hx | 8 +++-
36
util/async.c | 20 +++++
37
11 files changed, 134 insertions(+), 14 deletions(-)
37
util/main-loop.c | 65 ++++++++++++++
38
util/thread-pool.c | 55 +++++++++++-
39
13 files changed, 419 insertions(+), 69 deletions(-)
40
create mode 100644 include/sysemu/event-loop-base.h
41
create mode 100644 event-loop-base.c
38
42
39
--
43
--
40
2.31.1
44
2.35.1
41
diff view generated by jsdifflib
1
From: Stefano Garzarella <sgarzare@redhat.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
The `aio-max-batch` parameter will be propagated to AIO engines
3
Introduce the 'event-loop-base' abstract class, it'll hold the
4
and it will be used to control the maximum number of queued requests.
4
properties common to all event loops and provide the necessary hooks for
5
5
their creation and maintenance. Then have iothread inherit from it.
6
When there are in queue a number of requests equal to `aio-max-batch`,
6
7
the engine invokes the system call to forward the requests to the kernel.
7
EventLoopBaseClass is defined as user creatable and provides a hook for
8
8
its children to attach themselves to the user creatable class 'complete'
9
This parameter allows us to control the maximum batch size to reduce
9
function. It also provides an update_params() callback to propagate
10
the latency that requests might accumulate while queued in the AIO
10
property changes onto its children.
11
engine queue.
11
12
12
The new 'event-loop-base' class will live in the root directory. It is
13
If `aio-max-batch` is equal to 0 (default value), the AIO engine will
13
built on its own using the 'link_whole' option (there are no direct
14
use its default maximum batch size value.
14
function dependencies between the class and its children, it all happens
15
15
trough 'constructor' magic). And also imposes new compilation
16
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
16
dependencies:
17
Message-id: 20210721094211.69853-3-sgarzare@redhat.com
17
18
qom <- event-loop-base <- blockdev (iothread.c)
19
20
And in subsequent patches:
21
22
qom <- event-loop-base <- qemuutil (util/main-loop.c)
23
24
All this forced some amount of reordering in meson.build:
25
26
- Moved qom build definition before qemuutil. Doing it the other way
27
around (i.e. moving qemuutil after qom) isn't possible as a lot of
28
core libraries that live in between the two depend on it.
29
30
- Process the 'hw' subdir earlier, as it introduces files into the
31
'qom' source set.
32
33
No functional changes intended.
34
35
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
36
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
37
Acked-by: Markus Armbruster <armbru@redhat.com>
38
Message-id: 20220425075723.20019-2-nsaenzju@redhat.com
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
39
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
---
40
---
20
qapi/misc.json | 6 ++++-
41
qapi/qom.json | 22 +++++--
21
qapi/qom.json | 7 ++++-
42
meson.build | 23 ++++---
22
include/block/aio.h | 12 +++++++++
43
include/sysemu/event-loop-base.h | 36 +++++++++++
23
include/sysemu/iothread.h | 3 +++
44
include/sysemu/iothread.h | 6 +-
24
iothread.c | 55 +++++++++++++++++++++++++++++++++++----
45
event-loop-base.c | 104 +++++++++++++++++++++++++++++++
25
monitor/hmp-cmds.c | 2 ++
46
iothread.c | 65 ++++++-------------
26
util/aio-posix.c | 12 +++++++++
47
6 files changed, 192 insertions(+), 64 deletions(-)
27
util/aio-win32.c | 5 ++++
48
create mode 100644 include/sysemu/event-loop-base.h
28
util/async.c | 2 ++
49
create mode 100644 event-loop-base.c
29
qemu-options.hx | 8 ++++--
50
30
10 files changed, 103 insertions(+), 9 deletions(-)
31
32
diff --git a/qapi/misc.json b/qapi/misc.json
33
index XXXXXXX..XXXXXXX 100644
34
--- a/qapi/misc.json
35
+++ b/qapi/misc.json
36
@@ -XXX,XX +XXX,XX @@
37
# @poll-shrink: how many ns will be removed from polling time, 0 means that
38
# it's not configured (since 2.9)
39
#
40
+# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
41
+# 0 means that the engine will use its default (since 6.1)
42
+#
43
# Since: 2.0
44
##
45
{ 'struct': 'IOThreadInfo',
46
@@ -XXX,XX +XXX,XX @@
47
'thread-id': 'int',
48
'poll-max-ns': 'int',
49
'poll-grow': 'int',
50
- 'poll-shrink': 'int' } }
51
+ 'poll-shrink': 'int',
52
+ 'aio-max-batch': 'int' } }
53
54
##
55
# @query-iothreads:
56
diff --git a/qapi/qom.json b/qapi/qom.json
51
diff --git a/qapi/qom.json b/qapi/qom.json
57
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
58
--- a/qapi/qom.json
53
--- a/qapi/qom.json
59
+++ b/qapi/qom.json
54
+++ b/qapi/qom.json
60
@@ -XXX,XX +XXX,XX @@
55
@@ -XXX,XX +XXX,XX @@
56
'*repeat': 'bool',
57
'*grab-toggle': 'GrabToggleKeys' } }
58
59
+##
60
+# @EventLoopBaseProperties:
61
+#
62
+# Common properties for event loops
63
+#
64
+# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
65
+# 0 means that the engine will use its default.
66
+# (default: 0)
67
+#
68
+# Since: 7.1
69
+##
70
+{ 'struct': 'EventLoopBaseProperties',
71
+ 'data': { '*aio-max-batch': 'int' } }
72
+
73
##
74
# @IothreadProperties:
75
#
76
@@ -XXX,XX +XXX,XX @@
61
# algorithm detects it is spending too long polling without
77
# algorithm detects it is spending too long polling without
62
# encountering events. 0 selects a default behaviour (default: 0)
78
# encountering events. 0 selects a default behaviour (default: 0)
63
#
79
#
64
+# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
80
-# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
65
+# 0 means that the engine will use its default
81
-# 0 means that the engine will use its default
66
+# (default:0, since 6.1)
82
-# (default:0, since 6.1)
67
+#
83
+# The @aio-max-batch option is available since 6.1.
84
#
68
# Since: 2.0
85
# Since: 2.0
69
##
86
##
70
{ 'struct': 'IothreadProperties',
87
{ 'struct': 'IothreadProperties',
88
+ 'base': 'EventLoopBaseProperties',
71
'data': { '*poll-max-ns': 'int',
89
'data': { '*poll-max-ns': 'int',
72
'*poll-grow': 'int',
90
'*poll-grow': 'int',
73
- '*poll-shrink': 'int' } }
91
- '*poll-shrink': 'int',
74
+ '*poll-shrink': 'int',
92
- '*aio-max-batch': 'int' } }
75
+ '*aio-max-batch': 'int' } }
93
+ '*poll-shrink': 'int' } }
76
94
77
##
95
##
78
# @MemoryBackendProperties:
96
# @MemoryBackendProperties:
79
diff --git a/include/block/aio.h b/include/block/aio.h
97
diff --git a/meson.build b/meson.build
80
index XXXXXXX..XXXXXXX 100644
98
index XXXXXXX..XXXXXXX 100644
81
--- a/include/block/aio.h
99
--- a/meson.build
82
+++ b/include/block/aio.h
100
+++ b/meson.build
83
@@ -XXX,XX +XXX,XX @@ struct AioContext {
101
@@ -XXX,XX +XXX,XX @@ subdir('qom')
84
int64_t poll_grow; /* polling time growth factor */
102
subdir('authz')
85
int64_t poll_shrink; /* polling time shrink factor */
103
subdir('crypto')
86
104
subdir('ui')
87
+ /* AIO engine parameters */
105
+subdir('hw')
88
+ int64_t aio_max_batch; /* maximum number of requests in a batch */
106
89
+
107
90
/*
108
if enable_modules
91
* List of handlers participating in userspace polling. Protected by
109
@@ -XXX,XX +XXX,XX @@ if enable_modules
92
* ctx->list_lock. Iterated and modified mostly by the event loop thread
110
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
93
@@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
111
endif
94
int64_t grow, int64_t shrink,
112
95
Error **errp);
113
+qom_ss = qom_ss.apply(config_host, strict: false)
96
114
+libqom = static_library('qom', qom_ss.sources() + genh,
97
+/**
115
+ dependencies: [qom_ss.dependencies()],
98
+ * aio_context_set_aio_params:
116
+ name_suffix: 'fa')
99
+ * @ctx: the aio context
117
+qom = declare_dependency(link_whole: libqom)
100
+ * @max_batch: maximum number of requests in a batch, 0 means that the
118
+
101
+ * engine will use its default
119
+event_loop_base = files('event-loop-base.c')
120
+event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh,
121
+ build_by_default: true)
122
+event_loop_base = declare_dependency(link_whole: event_loop_base,
123
+ dependencies: [qom])
124
+
125
stub_ss = stub_ss.apply(config_all, strict: false)
126
127
util_ss.add_all(trace_ss)
128
@@ -XXX,XX +XXX,XX @@ subdir('monitor')
129
subdir('net')
130
subdir('replay')
131
subdir('semihosting')
132
-subdir('hw')
133
subdir('tcg')
134
subdir('fpu')
135
subdir('accel')
136
@@ -XXX,XX +XXX,XX @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms',
137
capture: true,
138
command: [undefsym, nm, '@INPUT@'])
139
140
-qom_ss = qom_ss.apply(config_host, strict: false)
141
-libqom = static_library('qom', qom_ss.sources() + genh,
142
- dependencies: [qom_ss.dependencies()],
143
- name_suffix: 'fa')
144
-
145
-qom = declare_dependency(link_whole: libqom)
146
-
147
authz_ss = authz_ss.apply(config_host, strict: false)
148
libauthz = static_library('authz', authz_ss.sources() + genh,
149
dependencies: [authz_ss.dependencies()],
150
@@ -XXX,XX +XXX,XX @@ libblockdev = static_library('blockdev', blockdev_ss.sources() + genh,
151
build_by_default: false)
152
153
blockdev = declare_dependency(link_whole: [libblockdev],
154
- dependencies: [block])
155
+ dependencies: [block, event_loop_base])
156
157
qmp_ss = qmp_ss.apply(config_host, strict: false)
158
libqmp = static_library('qmp', qmp_ss.sources() + genh,
159
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
160
new file mode 100644
161
index XXXXXXX..XXXXXXX
162
--- /dev/null
163
+++ b/include/sysemu/event-loop-base.h
164
@@ -XXX,XX +XXX,XX @@
165
+/*
166
+ * QEMU event-loop backend
167
+ *
168
+ * Copyright (C) 2022 Red Hat Inc
169
+ *
170
+ * Authors:
171
+ * Nicolas Saenz Julienne <nsaenzju@redhat.com>
172
+ *
173
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
174
+ * See the COPYING file in the top-level directory.
102
+ */
175
+ */
103
+void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
176
+#ifndef QEMU_EVENT_LOOP_BASE_H
104
+ Error **errp);
177
+#define QEMU_EVENT_LOOP_BASE_H
105
+
178
+
106
#endif
179
+#include "qom/object.h"
180
+#include "block/aio.h"
181
+#include "qemu/typedefs.h"
182
+
183
+#define TYPE_EVENT_LOOP_BASE "event-loop-base"
184
+OBJECT_DECLARE_TYPE(EventLoopBase, EventLoopBaseClass,
185
+ EVENT_LOOP_BASE)
186
+
187
+struct EventLoopBaseClass {
188
+ ObjectClass parent_class;
189
+
190
+ void (*init)(EventLoopBase *base, Error **errp);
191
+ void (*update_params)(EventLoopBase *base, Error **errp);
192
+};
193
+
194
+struct EventLoopBase {
195
+ Object parent;
196
+
197
+ /* AioContext AIO engine parameters */
198
+ int64_t aio_max_batch;
199
+};
200
+#endif
107
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
201
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
108
index XXXXXXX..XXXXXXX 100644
202
index XXXXXXX..XXXXXXX 100644
109
--- a/include/sysemu/iothread.h
203
--- a/include/sysemu/iothread.h
110
+++ b/include/sysemu/iothread.h
204
+++ b/include/sysemu/iothread.h
205
@@ -XXX,XX +XXX,XX @@
206
#include "block/aio.h"
207
#include "qemu/thread.h"
208
#include "qom/object.h"
209
+#include "sysemu/event-loop-base.h"
210
211
#define TYPE_IOTHREAD "iothread"
212
213
struct IOThread {
214
- Object parent_obj;
215
+ EventLoopBase parent_obj;
216
217
QemuThread thread;
218
AioContext *ctx;
111
@@ -XXX,XX +XXX,XX @@ struct IOThread {
219
@@ -XXX,XX +XXX,XX @@ struct IOThread {
112
int64_t poll_max_ns;
220
int64_t poll_max_ns;
113
int64_t poll_grow;
221
int64_t poll_grow;
114
int64_t poll_shrink;
222
int64_t poll_shrink;
115
+
223
-
116
+ /* AioContext AIO engine parameters */
224
- /* AioContext AIO engine parameters */
117
+ int64_t aio_max_batch;
225
- int64_t aio_max_batch;
118
};
226
};
119
typedef struct IOThread IOThread;
227
typedef struct IOThread IOThread;
120
228
229
diff --git a/event-loop-base.c b/event-loop-base.c
230
new file mode 100644
231
index XXXXXXX..XXXXXXX
232
--- /dev/null
233
+++ b/event-loop-base.c
234
@@ -XXX,XX +XXX,XX @@
235
+/*
236
+ * QEMU event-loop base
237
+ *
238
+ * Copyright (C) 2022 Red Hat Inc
239
+ *
240
+ * Authors:
241
+ * Stefan Hajnoczi <stefanha@redhat.com>
242
+ * Nicolas Saenz Julienne <nsaenzju@redhat.com>
243
+ *
244
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
245
+ * See the COPYING file in the top-level directory.
246
+ */
247
+
248
+#include "qemu/osdep.h"
249
+#include "qom/object_interfaces.h"
250
+#include "qapi/error.h"
251
+#include "sysemu/event-loop-base.h"
252
+
253
+typedef struct {
254
+ const char *name;
255
+ ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
256
+} EventLoopBaseParamInfo;
257
+
258
+static EventLoopBaseParamInfo aio_max_batch_info = {
259
+ "aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
260
+};
261
+
262
+static void event_loop_base_get_param(Object *obj, Visitor *v,
263
+ const char *name, void *opaque, Error **errp)
264
+{
265
+ EventLoopBase *event_loop_base = EVENT_LOOP_BASE(obj);
266
+ EventLoopBaseParamInfo *info = opaque;
267
+ int64_t *field = (void *)event_loop_base + info->offset;
268
+
269
+ visit_type_int64(v, name, field, errp);
270
+}
271
+
272
+static void event_loop_base_set_param(Object *obj, Visitor *v,
273
+ const char *name, void *opaque, Error **errp)
274
+{
275
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(obj);
276
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
277
+ EventLoopBaseParamInfo *info = opaque;
278
+ int64_t *field = (void *)base + info->offset;
279
+ int64_t value;
280
+
281
+ if (!visit_type_int64(v, name, &value, errp)) {
282
+ return;
283
+ }
284
+
285
+ if (value < 0) {
286
+ error_setg(errp, "%s value must be in range [0, %" PRId64 "]",
287
+ info->name, INT64_MAX);
288
+ return;
289
+ }
290
+
291
+ *field = value;
292
+
293
+ if (bc->update_params) {
294
+ bc->update_params(base, errp);
295
+ }
296
+
297
+ return;
298
+}
299
+
300
+static void event_loop_base_complete(UserCreatable *uc, Error **errp)
301
+{
302
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
303
+ EventLoopBase *base = EVENT_LOOP_BASE(uc);
304
+
305
+ if (bc->init) {
306
+ bc->init(base, errp);
307
+ }
308
+}
309
+
310
+static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
311
+{
312
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
313
+ ucc->complete = event_loop_base_complete;
314
+
315
+ object_class_property_add(klass, "aio-max-batch", "int",
316
+ event_loop_base_get_param,
317
+ event_loop_base_set_param,
318
+ NULL, &aio_max_batch_info);
319
+}
320
+
321
+static const TypeInfo event_loop_base_info = {
322
+ .name = TYPE_EVENT_LOOP_BASE,
323
+ .parent = TYPE_OBJECT,
324
+ .instance_size = sizeof(EventLoopBase),
325
+ .class_size = sizeof(EventLoopBaseClass),
326
+ .class_init = event_loop_base_class_init,
327
+ .abstract = true,
328
+ .interfaces = (InterfaceInfo[]) {
329
+ { TYPE_USER_CREATABLE },
330
+ { }
331
+ }
332
+};
333
+
334
+static void register_types(void)
335
+{
336
+ type_register_static(&event_loop_base_info);
337
+}
338
+type_init(register_types);
121
diff --git a/iothread.c b/iothread.c
339
diff --git a/iothread.c b/iothread.c
122
index XXXXXXX..XXXXXXX 100644
340
index XXXXXXX..XXXXXXX 100644
123
--- a/iothread.c
341
--- a/iothread.c
124
+++ b/iothread.c
342
+++ b/iothread.c
343
@@ -XXX,XX +XXX,XX @@
344
#include "qemu/module.h"
345
#include "block/aio.h"
346
#include "block/block.h"
347
+#include "sysemu/event-loop-base.h"
348
#include "sysemu/iothread.h"
349
#include "qapi/error.h"
350
#include "qapi/qapi-commands-misc.h"
125
@@ -XXX,XX +XXX,XX @@ static void iothread_init_gcontext(IOThread *iothread)
351
@@ -XXX,XX +XXX,XX @@ static void iothread_init_gcontext(IOThread *iothread)
126
iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE);
352
iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE);
127
}
353
}
128
354
129
+static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
355
-static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
130
+{
356
+static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
131
+ ERRP_GUARD();
357
{
132
+
358
+ IOThread *iothread = IOTHREAD(base);
133
+ aio_context_set_poll_params(iothread->ctx,
359
ERRP_GUARD();
134
+ iothread->poll_max_ns,
360
135
+ iothread->poll_grow,
361
+ if (!iothread->ctx) {
136
+ iothread->poll_shrink,
137
+ errp);
138
+ if (*errp) {
139
+ return;
362
+ return;
140
+ }
363
+ }
141
+
364
+
142
+ aio_context_set_aio_params(iothread->ctx,
365
aio_context_set_poll_params(iothread->ctx,
143
+ iothread->aio_max_batch,
366
iothread->poll_max_ns,
144
+ errp);
367
iothread->poll_grow,
145
+}
368
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
146
+
369
}
147
static void iothread_complete(UserCreatable *obj, Error **errp)
370
371
aio_context_set_aio_params(iothread->ctx,
372
- iothread->aio_max_batch,
373
+ iothread->parent_obj.aio_max_batch,
374
errp);
375
}
376
377
-static void iothread_complete(UserCreatable *obj, Error **errp)
378
+
379
+static void iothread_init(EventLoopBase *base, Error **errp)
148
{
380
{
149
Error *local_error = NULL;
381
Error *local_error = NULL;
382
- IOThread *iothread = IOTHREAD(obj);
383
+ IOThread *iothread = IOTHREAD(base);
384
char *thread_name;
385
386
iothread->stopping = false;
150
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
387
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
151
*/
388
*/
152
iothread_init_gcontext(iothread);
389
iothread_init_gcontext(iothread);
153
390
154
- aio_context_set_poll_params(iothread->ctx,
391
- iothread_set_aio_context_params(iothread, &local_error);
155
- iothread->poll_max_ns,
392
+ iothread_set_aio_context_params(base, &local_error);
156
- iothread->poll_grow,
157
- iothread->poll_shrink,
158
- &local_error);
159
+ iothread_set_aio_context_params(iothread, &local_error);
160
if (local_error) {
393
if (local_error) {
161
error_propagate(errp, local_error);
394
error_propagate(errp, local_error);
162
aio_context_unref(iothread->ctx);
395
aio_context_unref(iothread->ctx);
163
@@ -XXX,XX +XXX,XX @@ static PollParamInfo poll_grow_info = {
396
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
164
static PollParamInfo poll_shrink_info = {
397
* to inherit.
398
*/
399
thread_name = g_strdup_printf("IO %s",
400
- object_get_canonical_path_component(OBJECT(obj)));
401
+ object_get_canonical_path_component(OBJECT(base)));
402
qemu_thread_create(&iothread->thread, thread_name, iothread_run,
403
iothread, QEMU_THREAD_JOINABLE);
404
g_free(thread_name);
405
@@ -XXX,XX +XXX,XX @@ static IOThreadParamInfo poll_grow_info = {
406
static IOThreadParamInfo poll_shrink_info = {
165
"poll-shrink", offsetof(IOThread, poll_shrink),
407
"poll-shrink", offsetof(IOThread, poll_shrink),
166
};
408
};
167
+static PollParamInfo aio_max_batch_info = {
409
-static IOThreadParamInfo aio_max_batch_info = {
168
+ "aio-max-batch", offsetof(IOThread, aio_max_batch),
410
- "aio-max-batch", offsetof(IOThread, aio_max_batch),
169
+};
411
-};
170
412
171
static void iothread_get_param(Object *obj, Visitor *v,
413
static void iothread_get_param(Object *obj, Visitor *v,
172
const char *name, void *opaque, Error **errp)
414
const char *name, IOThreadParamInfo *info, Error **errp)
173
@@ -XXX,XX +XXX,XX @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
415
@@ -XXX,XX +XXX,XX @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
174
}
416
}
175
}
417
}
176
418
177
+static void iothread_get_aio_param(Object *obj, Visitor *v,
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
178
+ const char *name, void *opaque, Error **errp)
420
- const char *name, void *opaque, Error **errp)
179
+{
421
-{
180
+
422
- IOThreadParamInfo *info = opaque;
181
+ iothread_get_param(obj, v, name, opaque, errp);
423
-
182
+}
424
- iothread_get_param(obj, v, name, info, errp);
183
+
425
-}
184
+static void iothread_set_aio_param(Object *obj, Visitor *v,
426
-
185
+ const char *name, void *opaque, Error **errp)
427
-static void iothread_set_aio_param(Object *obj, Visitor *v,
186
+{
428
- const char *name, void *opaque, Error **errp)
187
+ IOThread *iothread = IOTHREAD(obj);
429
-{
188
+
430
- IOThread *iothread = IOTHREAD(obj);
189
+ if (!iothread_set_param(obj, v, name, opaque, errp)) {
431
- IOThreadParamInfo *info = opaque;
190
+ return;
432
-
191
+ }
433
- if (!iothread_set_param(obj, v, name, info, errp)) {
192
+
434
- return;
193
+ if (iothread->ctx) {
435
- }
194
+ aio_context_set_aio_params(iothread->ctx,
436
-
195
+ iothread->aio_max_batch,
437
- if (iothread->ctx) {
196
+ errp);
438
- aio_context_set_aio_params(iothread->ctx,
197
+ }
439
- iothread->aio_max_batch,
198
+}
440
- errp);
199
+
441
- }
442
-}
443
-
200
static void iothread_class_init(ObjectClass *klass, void *class_data)
444
static void iothread_class_init(ObjectClass *klass, void *class_data)
201
{
445
{
202
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
447
- ucc->complete = iothread_complete;
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
449
+
450
+ bc->init = iothread_init;
451
+ bc->update_params = iothread_set_aio_context_params;
452
453
object_class_property_add(klass, "poll-max-ns", "int",
454
iothread_get_poll_param,
203
@@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
455
@@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
204
iothread_get_poll_param,
456
iothread_get_poll_param,
205
iothread_set_poll_param,
457
iothread_set_poll_param,
206
NULL, &poll_shrink_info);
458
NULL, &poll_shrink_info);
207
+ object_class_property_add(klass, "aio-max-batch", "int",
459
- object_class_property_add(klass, "aio-max-batch", "int",
208
+ iothread_get_aio_param,
460
- iothread_get_aio_param,
209
+ iothread_set_aio_param,
461
- iothread_set_aio_param,
210
+ NULL, &aio_max_batch_info);
462
- NULL, &aio_max_batch_info);
211
}
463
}
212
464
213
static const TypeInfo iothread_info = {
465
static const TypeInfo iothread_info = {
466
.name = TYPE_IOTHREAD,
467
- .parent = TYPE_OBJECT,
468
+ .parent = TYPE_EVENT_LOOP_BASE,
469
.class_init = iothread_class_init,
470
.instance_size = sizeof(IOThread),
471
.instance_init = iothread_instance_init,
472
.instance_finalize = iothread_instance_finalize,
473
- .interfaces = (InterfaceInfo[]) {
474
- {TYPE_USER_CREATABLE},
475
- {}
476
- },
477
};
478
479
static void iothread_register_types(void)
214
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
215
info->poll_max_ns = iothread->poll_max_ns;
481
info->poll_max_ns = iothread->poll_max_ns;
216
info->poll_grow = iothread->poll_grow;
482
info->poll_grow = iothread->poll_grow;
217
info->poll_shrink = iothread->poll_shrink;
483
info->poll_shrink = iothread->poll_shrink;
218
+ info->aio_max_batch = iothread->aio_max_batch;
484
- info->aio_max_batch = iothread->aio_max_batch;
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
219
486
220
QAPI_LIST_APPEND(*tail, info);
487
QAPI_LIST_APPEND(*tail, info);
221
return 0;
488
return 0;
222
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
223
index XXXXXXX..XXXXXXX 100644
224
--- a/monitor/hmp-cmds.c
225
+++ b/monitor/hmp-cmds.c
226
@@ -XXX,XX +XXX,XX @@ void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
227
monitor_printf(mon, " poll-max-ns=%" PRId64 "\n", value->poll_max_ns);
228
monitor_printf(mon, " poll-grow=%" PRId64 "\n", value->poll_grow);
229
monitor_printf(mon, " poll-shrink=%" PRId64 "\n", value->poll_shrink);
230
+ monitor_printf(mon, " aio-max-batch=%" PRId64 "\n",
231
+ value->aio_max_batch);
232
}
233
234
qapi_free_IOThreadInfoList(info_list);
235
diff --git a/util/aio-posix.c b/util/aio-posix.c
236
index XXXXXXX..XXXXXXX 100644
237
--- a/util/aio-posix.c
238
+++ b/util/aio-posix.c
239
@@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
240
241
aio_notify(ctx);
242
}
243
+
244
+void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
245
+ Error **errp)
246
+{
247
+ /*
248
+ * No thread synchronization here, it doesn't matter if an incorrect value
249
+ * is used once.
250
+ */
251
+ ctx->aio_max_batch = max_batch;
252
+
253
+ aio_notify(ctx);
254
+}
255
diff --git a/util/aio-win32.c b/util/aio-win32.c
256
index XXXXXXX..XXXXXXX 100644
257
--- a/util/aio-win32.c
258
+++ b/util/aio-win32.c
259
@@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
260
error_setg(errp, "AioContext polling is not implemented on Windows");
261
}
262
}
263
+
264
+void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
265
+ Error **errp)
266
+{
267
+}
268
diff --git a/util/async.c b/util/async.c
269
index XXXXXXX..XXXXXXX 100644
270
--- a/util/async.c
271
+++ b/util/async.c
272
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
273
ctx->poll_grow = 0;
274
ctx->poll_shrink = 0;
275
276
+ ctx->aio_max_batch = 0;
277
+
278
return ctx;
279
fail:
280
g_source_destroy(&ctx->source);
281
diff --git a/qemu-options.hx b/qemu-options.hx
282
index XXXXXXX..XXXXXXX 100644
283
--- a/qemu-options.hx
284
+++ b/qemu-options.hx
285
@@ -XXX,XX +XXX,XX @@ SRST
286
287
CN=laptop.example.com,O=Example Home,L=London,ST=London,C=GB
288
289
- ``-object iothread,id=id,poll-max-ns=poll-max-ns,poll-grow=poll-grow,poll-shrink=poll-shrink``
290
+ ``-object iothread,id=id,poll-max-ns=poll-max-ns,poll-grow=poll-grow,poll-shrink=poll-shrink,aio-max-batch=aio-max-batch``
291
Creates a dedicated event loop thread that devices can be
292
assigned to. This is known as an IOThread. By default device
293
emulation happens in vCPU threads or the main event loop thread.
294
@@ -XXX,XX +XXX,XX @@ SRST
295
the polling time when the algorithm detects it is spending too
296
long polling without encountering events.
297
298
- The polling parameters can be modified at run-time using the
299
+ The ``aio-max-batch`` parameter is the maximum number of requests
300
+ in a batch for the AIO engine, 0 means that the engine will use
301
+ its default.
302
+
303
+ The IOThread parameters can be modified at run-time using the
304
``qom-set`` command (where ``iothread1`` is the IOThread's
305
``id``):
306
307
--
489
--
308
2.31.1
490
2.35.1
309
diff view generated by jsdifflib
1
From: Stefano Garzarella <sgarzare@redhat.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
Changes in preparation for next patches where we add a new
3
'event-loop-base' provides basic property handling for all 'AioContext'
4
parameter not related to the poll mechanism.
4
based event loops. So let's define a new 'MainLoopClass' that inherits
5
5
from it. This will permit tweaking the main loop's properties through
6
Let's add two new generic functions (iothread_set_param and
6
qapi as well as through the command line using the '-object' keyword[1].
7
iothread_get_param) that we use to set and get IOThread
7
Only one instance of 'MainLoopClass' might be created at any time.
8
parameters.
8
9
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
10
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
10
mark 'MainLoop' as non-deletable.
11
Message-id: 20210721094211.69853-2-sgarzare@redhat.com
11
12
[1] For example:
13
-object main-loop,id=main-loop,aio-max-batch=<value>
14
15
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
16
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Acked-by: Markus Armbruster <armbru@redhat.com>
18
Message-id: 20220425075723.20019-3-nsaenzju@redhat.com
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
20
---
14
iothread.c | 27 +++++++++++++++++++++++----
21
qapi/qom.json | 13 ++++++++
15
1 file changed, 23 insertions(+), 4 deletions(-)
22
meson.build | 3 +-
16
23
include/qemu/main-loop.h | 10 ++++++
17
diff --git a/iothread.c b/iothread.c
24
include/sysemu/event-loop-base.h | 1 +
18
index XXXXXXX..XXXXXXX 100644
25
event-loop-base.c | 13 ++++++++
19
--- a/iothread.c
26
util/main-loop.c | 56 ++++++++++++++++++++++++++++++++
20
+++ b/iothread.c
27
6 files changed, 95 insertions(+), 1 deletion(-)
21
@@ -XXX,XX +XXX,XX @@ static PollParamInfo poll_shrink_info = {
28
22
"poll-shrink", offsetof(IOThread, poll_shrink),
29
diff --git a/qapi/qom.json b/qapi/qom.json
30
index XXXXXXX..XXXXXXX 100644
31
--- a/qapi/qom.json
32
+++ b/qapi/qom.json
33
@@ -XXX,XX +XXX,XX @@
34
'*poll-grow': 'int',
35
'*poll-shrink': 'int' } }
36
37
+##
38
+# @MainLoopProperties:
39
+#
40
+# Properties for the main-loop object.
41
+#
42
+# Since: 7.1
43
+##
44
+{ 'struct': 'MainLoopProperties',
45
+ 'base': 'EventLoopBaseProperties',
46
+ 'data': {} }
47
+
48
##
49
# @MemoryBackendProperties:
50
#
51
@@ -XXX,XX +XXX,XX @@
52
{ 'name': 'input-linux',
53
'if': 'CONFIG_LINUX' },
54
'iothread',
55
+ 'main-loop',
56
{ 'name': 'memory-backend-epc',
57
'if': 'CONFIG_LINUX' },
58
'memory-backend-file',
59
@@ -XXX,XX +XXX,XX @@
60
'input-linux': { 'type': 'InputLinuxProperties',
61
'if': 'CONFIG_LINUX' },
62
'iothread': 'IothreadProperties',
63
+ 'main-loop': 'MainLoopProperties',
64
'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties',
65
'if': 'CONFIG_LINUX' },
66
'memory-backend-file': 'MemoryBackendFileProperties',
67
diff --git a/meson.build b/meson.build
68
index XXXXXXX..XXXXXXX 100644
69
--- a/meson.build
70
+++ b/meson.build
71
@@ -XXX,XX +XXX,XX @@ libqemuutil = static_library('qemuutil',
72
sources: util_ss.sources() + stub_ss.sources() + genh,
73
dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman])
74
qemuutil = declare_dependency(link_with: libqemuutil,
75
- sources: genh + version_res)
76
+ sources: genh + version_res,
77
+ dependencies: [event_loop_base])
78
79
if have_system or have_user
80
decodetree = generator(find_program('scripts/decodetree.py'),
81
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/qemu/main-loop.h
84
+++ b/include/qemu/main-loop.h
85
@@ -XXX,XX +XXX,XX @@
86
#define QEMU_MAIN_LOOP_H
87
88
#include "block/aio.h"
89
+#include "qom/object.h"
90
+#include "sysemu/event-loop-base.h"
91
92
#define SIG_IPI SIGUSR1
93
94
+#define TYPE_MAIN_LOOP "main-loop"
95
+OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP)
96
+
97
+struct MainLoop {
98
+ EventLoopBase parent_obj;
99
+};
100
+typedef struct MainLoop MainLoop;
101
+
102
/**
103
* qemu_init_main_loop: Set up the process so that it can run the main loop.
104
*
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBaseClass {
110
111
void (*init)(EventLoopBase *base, Error **errp);
112
void (*update_params)(EventLoopBase *base, Error **errp);
113
+ bool (*can_be_deleted)(EventLoopBase *base);
23
};
114
};
24
115
25
-static void iothread_get_poll_param(Object *obj, Visitor *v,
116
struct EventLoopBase {
26
+static void iothread_get_param(Object *obj, Visitor *v,
117
diff --git a/event-loop-base.c b/event-loop-base.c
27
const char *name, void *opaque, Error **errp)
118
index XXXXXXX..XXXXXXX 100644
119
--- a/event-loop-base.c
120
+++ b/event-loop-base.c
121
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_complete(UserCreatable *uc, Error **errp)
122
}
123
}
124
125
+static bool event_loop_base_can_be_deleted(UserCreatable *uc)
126
+{
127
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
128
+ EventLoopBase *backend = EVENT_LOOP_BASE(uc);
129
+
130
+ if (bc->can_be_deleted) {
131
+ return bc->can_be_deleted(backend);
132
+ }
133
+
134
+ return true;
135
+}
136
+
137
static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
28
{
138
{
29
IOThread *iothread = IOTHREAD(obj);
139
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
30
@@ -XXX,XX +XXX,XX @@ static void iothread_get_poll_param(Object *obj, Visitor *v,
140
ucc->complete = event_loop_base_complete;
31
visit_type_int64(v, name, field, errp);
141
+ ucc->can_be_deleted = event_loop_base_can_be_deleted;
142
143
object_class_property_add(klass, "aio-max-batch", "int",
144
event_loop_base_get_param,
145
diff --git a/util/main-loop.c b/util/main-loop.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/util/main-loop.c
148
+++ b/util/main-loop.c
149
@@ -XXX,XX +XXX,XX @@
150
#include "qemu/error-report.h"
151
#include "qemu/queue.h"
152
#include "qemu/compiler.h"
153
+#include "qom/object.h"
154
155
#ifndef _WIN32
156
#include <sys/wait.h>
157
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
158
return 0;
32
}
159
}
33
160
34
-static void iothread_set_poll_param(Object *obj, Visitor *v,
161
+static void main_loop_update_params(EventLoopBase *base, Error **errp)
35
+static bool iothread_set_param(Object *obj, Visitor *v,
162
+{
36
const char *name, void *opaque, Error **errp)
163
+ if (!qemu_aio_context) {
37
{
164
+ error_setg(errp, "qemu aio context not ready");
38
IOThread *iothread = IOTHREAD(obj);
39
@@ -XXX,XX +XXX,XX @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
40
int64_t value;
41
42
if (!visit_type_int64(v, name, &value, errp)) {
43
- return;
44
+ return false;
45
}
46
47
if (value < 0) {
48
error_setg(errp, "%s value must be in range [0, %" PRId64 "]",
49
info->name, INT64_MAX);
50
- return;
51
+ return false;
52
}
53
54
*field = value;
55
56
+ return true;
57
+}
58
+
59
+static void iothread_get_poll_param(Object *obj, Visitor *v,
60
+ const char *name, void *opaque, Error **errp)
61
+{
62
+
63
+ iothread_get_param(obj, v, name, opaque, errp);
64
+}
65
+
66
+static void iothread_set_poll_param(Object *obj, Visitor *v,
67
+ const char *name, void *opaque, Error **errp)
68
+{
69
+ IOThread *iothread = IOTHREAD(obj);
70
+
71
+ if (!iothread_set_param(obj, v, name, opaque, errp)) {
72
+ return;
165
+ return;
73
+ }
166
+ }
74
+
167
+
75
if (iothread->ctx) {
168
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
76
aio_context_set_poll_params(iothread->ctx,
169
+}
77
iothread->poll_max_ns,
170
+
171
+MainLoop *mloop;
172
+
173
+static void main_loop_init(EventLoopBase *base, Error **errp)
174
+{
175
+ MainLoop *m = MAIN_LOOP(base);
176
+
177
+ if (mloop) {
178
+ error_setg(errp, "only one main-loop instance allowed");
179
+ return;
180
+ }
181
+
182
+ main_loop_update_params(base, errp);
183
+
184
+ mloop = m;
185
+ return;
186
+}
187
+
188
+static bool main_loop_can_be_deleted(EventLoopBase *base)
189
+{
190
+ return false;
191
+}
192
+
193
+static void main_loop_class_init(ObjectClass *oc, void *class_data)
194
+{
195
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc);
196
+
197
+ bc->init = main_loop_init;
198
+ bc->update_params = main_loop_update_params;
199
+ bc->can_be_deleted = main_loop_can_be_deleted;
200
+}
201
+
202
+static const TypeInfo main_loop_info = {
203
+ .name = TYPE_MAIN_LOOP,
204
+ .parent = TYPE_EVENT_LOOP_BASE,
205
+ .class_init = main_loop_class_init,
206
+ .instance_size = sizeof(MainLoop),
207
+};
208
+
209
+static void main_loop_register_types(void)
210
+{
211
+ type_register_static(&main_loop_info);
212
+}
213
+
214
+type_init(main_loop_register_types)
215
+
216
static int max_priority;
217
218
#ifndef _WIN32
78
--
219
--
79
2.31.1
220
2.35.1
80
diff view generated by jsdifflib
1
From: Stefano Garzarella <sgarzare@redhat.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
When there are multiple queues attached to the same AIO context,
3
The thread pool regulates itself: when idle, it kills threads until
4
some requests may experience high latency, since in the worst case
4
empty, when in demand, it creates new threads until full. This behaviour
5
the AIO engine queue is only flushed when it is full (MAX_EVENTS) or
5
doesn't play well with latency sensitive workloads where the price of
6
there are no more queues plugged.
6
creating a new thread is too high. For example, when paired with qemu's
7
'-mlock', or using safety features like SafeStack, creating a new thread
8
has been measured take multiple milliseconds.
7
9
8
Commit 2558cb8dd4 ("linux-aio: increasing MAX_EVENTS to a larger
10
In order to mitigate this let's introduce a new 'EventLoopBase'
9
hardcoded value") changed MAX_EVENTS from 128 to 1024, to increase
11
property to set the thread pool size. The threads will be created during
10
the number of in-flight requests. But this change also increased
12
the pool's initialization or upon updating the property's value, remain
11
the potential maximum batch to 1024 elements.
13
available during its lifetime regardless of demand, and destroyed upon
14
freeing it. A properly characterized workload will then be able to
15
configure the pool to avoid any latency spikes.
12
16
13
When there is a single queue attached to the AIO context, the issue
17
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
14
is mitigated from laio_io_unplug() that will flush the queue every
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
time is invoked since there can't be others queue plugged.
19
Acked-by: Markus Armbruster <armbru@redhat.com>
16
20
Message-id: 20220425075723.20019-4-nsaenzju@redhat.com
17
Let's use the new `aio-max-batch` IOThread parameter to mitigate
18
this issue, limiting the number of requests in a batch.
19
20
We also define a default value (32): this value is obtained running
21
some benchmarks and it represents a good tradeoff between the latency
22
increase while a request is queued and the cost of the io_submit(2)
23
system call.
24
25
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
26
Message-id: 20210721094211.69853-4-sgarzare@redhat.com
27
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
28
---
22
---
29
block/linux-aio.c | 9 ++++++++-
23
qapi/qom.json | 10 +++++-
30
1 file changed, 8 insertions(+), 1 deletion(-)
24
include/block/aio.h | 10 ++++++
25
include/block/thread-pool.h | 3 ++
26
include/sysemu/event-loop-base.h | 4 +++
27
event-loop-base.c | 23 +++++++++++++
28
iothread.c | 3 ++
29
util/aio-posix.c | 1 +
30
util/async.c | 20 ++++++++++++
31
util/main-loop.c | 9 ++++++
32
util/thread-pool.c | 55 +++++++++++++++++++++++++++++---
33
10 files changed, 133 insertions(+), 5 deletions(-)
31
34
32
diff --git a/block/linux-aio.c b/block/linux-aio.c
35
diff --git a/qapi/qom.json b/qapi/qom.json
33
index XXXXXXX..XXXXXXX 100644
36
index XXXXXXX..XXXXXXX 100644
34
--- a/block/linux-aio.c
37
--- a/qapi/qom.json
35
+++ b/block/linux-aio.c
38
+++ b/qapi/qom.json
36
@@ -XXX,XX +XXX,XX @@
39
@@ -XXX,XX +XXX,XX @@
37
*/
40
# 0 means that the engine will use its default.
38
#define MAX_EVENTS 1024
41
# (default: 0)
39
42
#
40
+/* Maximum number of requests in a batch. (default value) */
43
+# @thread-pool-min: minimum number of threads reserved in the thread pool
41
+#define DEFAULT_MAX_BATCH 32
44
+# (default:0)
42
+
45
+#
43
struct qemu_laiocb {
46
+# @thread-pool-max: maximum number of threads the thread pool can contain
44
Coroutine *co;
47
+# (default:64)
45
LinuxAioState *ctx;
48
+#
46
@@ -XXX,XX +XXX,XX @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
49
# Since: 7.1
47
LinuxAioState *s = laiocb->ctx;
50
##
48
struct iocb *iocbs = &laiocb->iocb;
51
{ 'struct': 'EventLoopBaseProperties',
49
QEMUIOVector *qiov = laiocb->qiov;
52
- 'data': { '*aio-max-batch': 'int' } }
50
+ int64_t max_batch = s->aio_context->aio_max_batch ?: DEFAULT_MAX_BATCH;
53
+ 'data': { '*aio-max-batch': 'int',
51
+
54
+ '*thread-pool-min': 'int',
52
+ /* limit the batch with the number of available events */
55
+ '*thread-pool-max': 'int' } }
53
+ max_batch = MIN_NON_ZERO(MAX_EVENTS - s->io_q.in_flight, max_batch);
56
54
57
##
55
switch (type) {
58
# @IothreadProperties:
56
case QEMU_AIO_WRITE:
59
diff --git a/include/block/aio.h b/include/block/aio.h
57
@@ -XXX,XX +XXX,XX @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
60
index XXXXXXX..XXXXXXX 100644
58
s->io_q.in_queue++;
61
--- a/include/block/aio.h
59
if (!s->io_q.blocked &&
62
+++ b/include/block/aio.h
60
(!s->io_q.plugged ||
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
61
- s->io_q.in_flight + s->io_q.in_queue >= MAX_EVENTS)) {
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
62
+ s->io_q.in_queue >= max_batch)) {
65
QEMUBH *co_schedule_bh;
63
ioq_submit(s);
66
67
+ int thread_pool_min;
68
+ int thread_pool_max;
69
/* Thread pool for performing work and receiving completion callbacks.
70
* Has its own locking.
71
*/
72
@@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
73
void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
74
Error **errp);
75
76
+/**
77
+ * aio_context_set_thread_pool_params:
78
+ * @ctx: the aio context
79
+ * @min: min number of threads to have readily available in the thread pool
80
+ * @min: max number of threads the thread pool can contain
81
+ */
82
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
83
+ int64_t max, Error **errp);
84
#endif
85
diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h
86
index XXXXXXX..XXXXXXX 100644
87
--- a/include/block/thread-pool.h
88
+++ b/include/block/thread-pool.h
89
@@ -XXX,XX +XXX,XX @@
90
91
#include "block/block.h"
92
93
+#define THREAD_POOL_MAX_THREADS_DEFAULT 64
94
+
95
typedef int ThreadPoolFunc(void *opaque);
96
97
typedef struct ThreadPool ThreadPool;
98
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
99
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
100
ThreadPoolFunc *func, void *arg);
101
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
102
+void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx);
103
104
#endif
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBase {
110
111
/* AioContext AIO engine parameters */
112
int64_t aio_max_batch;
113
+
114
+ /* AioContext thread pool parameters */
115
+ int64_t thread_pool_min;
116
+ int64_t thread_pool_max;
117
};
118
#endif
119
diff --git a/event-loop-base.c b/event-loop-base.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/event-loop-base.c
122
+++ b/event-loop-base.c
123
@@ -XXX,XX +XXX,XX @@
124
#include "qemu/osdep.h"
125
#include "qom/object_interfaces.h"
126
#include "qapi/error.h"
127
+#include "block/thread-pool.h"
128
#include "sysemu/event-loop-base.h"
129
130
typedef struct {
131
@@ -XXX,XX +XXX,XX @@ typedef struct {
132
ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
133
} EventLoopBaseParamInfo;
134
135
+static void event_loop_base_instance_init(Object *obj)
136
+{
137
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
138
+
139
+ base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
140
+}
141
+
142
static EventLoopBaseParamInfo aio_max_batch_info = {
143
"aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
144
};
145
+static EventLoopBaseParamInfo thread_pool_min_info = {
146
+ "thread-pool-min", offsetof(EventLoopBase, thread_pool_min),
147
+};
148
+static EventLoopBaseParamInfo thread_pool_max_info = {
149
+ "thread-pool-max", offsetof(EventLoopBase, thread_pool_max),
150
+};
151
152
static void event_loop_base_get_param(Object *obj, Visitor *v,
153
const char *name, void *opaque, Error **errp)
154
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
155
event_loop_base_get_param,
156
event_loop_base_set_param,
157
NULL, &aio_max_batch_info);
158
+ object_class_property_add(klass, "thread-pool-min", "int",
159
+ event_loop_base_get_param,
160
+ event_loop_base_set_param,
161
+ NULL, &thread_pool_min_info);
162
+ object_class_property_add(klass, "thread-pool-max", "int",
163
+ event_loop_base_get_param,
164
+ event_loop_base_set_param,
165
+ NULL, &thread_pool_max_info);
166
}
167
168
static const TypeInfo event_loop_base_info = {
169
.name = TYPE_EVENT_LOOP_BASE,
170
.parent = TYPE_OBJECT,
171
.instance_size = sizeof(EventLoopBase),
172
+ .instance_init = event_loop_base_instance_init,
173
.class_size = sizeof(EventLoopBaseClass),
174
.class_init = event_loop_base_class_init,
175
.abstract = true,
176
diff --git a/iothread.c b/iothread.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/iothread.c
179
+++ b/iothread.c
180
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
181
aio_context_set_aio_params(iothread->ctx,
182
iothread->parent_obj.aio_max_batch,
183
errp);
184
+
185
+ aio_context_set_thread_pool_params(iothread->ctx, base->thread_pool_min,
186
+ base->thread_pool_max, errp);
187
}
188
189
190
diff --git a/util/aio-posix.c b/util/aio-posix.c
191
index XXXXXXX..XXXXXXX 100644
192
--- a/util/aio-posix.c
193
+++ b/util/aio-posix.c
194
@@ -XXX,XX +XXX,XX @@
195
196
#include "qemu/osdep.h"
197
#include "block/block.h"
198
+#include "block/thread-pool.h"
199
#include "qemu/main-loop.h"
200
#include "qemu/rcu.h"
201
#include "qemu/rcu_queue.h"
202
diff --git a/util/async.c b/util/async.c
203
index XXXXXXX..XXXXXXX 100644
204
--- a/util/async.c
205
+++ b/util/async.c
206
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
207
208
ctx->aio_max_batch = 0;
209
210
+ ctx->thread_pool_min = 0;
211
+ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
212
+
213
return ctx;
214
fail:
215
g_source_destroy(&ctx->source);
216
@@ -XXX,XX +XXX,XX @@ void qemu_set_current_aio_context(AioContext *ctx)
217
assert(!get_my_aiocontext());
218
set_my_aiocontext(ctx);
219
}
220
+
221
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
222
+ int64_t max, Error **errp)
223
+{
224
+
225
+ if (min > max || !max || min > INT_MAX || max > INT_MAX) {
226
+ error_setg(errp, "bad thread-pool-min/thread-pool-max values");
227
+ return;
228
+ }
229
+
230
+ ctx->thread_pool_min = min;
231
+ ctx->thread_pool_max = max;
232
+
233
+ if (ctx->thread_pool) {
234
+ thread_pool_update_params(ctx->thread_pool, ctx);
235
+ }
236
+}
237
diff --git a/util/main-loop.c b/util/main-loop.c
238
index XXXXXXX..XXXXXXX 100644
239
--- a/util/main-loop.c
240
+++ b/util/main-loop.c
241
@@ -XXX,XX +XXX,XX @@
242
#include "sysemu/replay.h"
243
#include "qemu/main-loop.h"
244
#include "block/aio.h"
245
+#include "block/thread-pool.h"
246
#include "qemu/error-report.h"
247
#include "qemu/queue.h"
248
#include "qemu/compiler.h"
249
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
250
251
static void main_loop_update_params(EventLoopBase *base, Error **errp)
252
{
253
+ ERRP_GUARD();
254
+
255
if (!qemu_aio_context) {
256
error_setg(errp, "qemu aio context not ready");
257
return;
64
}
258
}
65
259
260
aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
261
+ if (*errp) {
262
+ return;
263
+ }
264
+
265
+ aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min,
266
+ base->thread_pool_max, errp);
267
}
268
269
MainLoop *mloop;
270
diff --git a/util/thread-pool.c b/util/thread-pool.c
271
index XXXXXXX..XXXXXXX 100644
272
--- a/util/thread-pool.c
273
+++ b/util/thread-pool.c
274
@@ -XXX,XX +XXX,XX @@ struct ThreadPool {
275
QemuMutex lock;
276
QemuCond worker_stopped;
277
QemuSemaphore sem;
278
- int max_threads;
279
QEMUBH *new_thread_bh;
280
281
/* The following variables are only accessed from one AioContext. */
282
@@ -XXX,XX +XXX,XX @@ struct ThreadPool {
283
int new_threads; /* backlog of threads we need to create */
284
int pending_threads; /* threads created but not running yet */
285
bool stopping;
286
+ int min_threads;
287
+ int max_threads;
288
};
289
290
+static inline bool back_to_sleep(ThreadPool *pool, int ret)
291
+{
292
+ /*
293
+ * The semaphore timed out, we should exit the loop except when:
294
+ * - There is work to do, we raced with the signal.
295
+ * - The max threads threshold just changed, we raced with the signal.
296
+ * - The thread pool forces a minimum number of readily available threads.
297
+ */
298
+ if (ret == -1 && (!QTAILQ_EMPTY(&pool->request_list) ||
299
+ pool->cur_threads > pool->max_threads ||
300
+ pool->cur_threads <= pool->min_threads)) {
301
+ return true;
302
+ }
303
+
304
+ return false;
305
+}
306
+
307
static void *worker_thread(void *opaque)
308
{
309
ThreadPool *pool = opaque;
310
@@ -XXX,XX +XXX,XX @@ static void *worker_thread(void *opaque)
311
ret = qemu_sem_timedwait(&pool->sem, 10000);
312
qemu_mutex_lock(&pool->lock);
313
pool->idle_threads--;
314
- } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list));
315
- if (ret == -1 || pool->stopping) {
316
+ } while (back_to_sleep(pool, ret));
317
+ if (ret == -1 || pool->stopping ||
318
+ pool->cur_threads > pool->max_threads) {
319
break;
320
}
321
322
@@ -XXX,XX +XXX,XX @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg)
323
thread_pool_submit_aio(pool, func, arg, NULL, NULL);
324
}
325
326
+void thread_pool_update_params(ThreadPool *pool, AioContext *ctx)
327
+{
328
+ qemu_mutex_lock(&pool->lock);
329
+
330
+ pool->min_threads = ctx->thread_pool_min;
331
+ pool->max_threads = ctx->thread_pool_max;
332
+
333
+ /*
334
+ * We either have to:
335
+ * - Increase the number available of threads until over the min_threads
336
+ * threshold.
337
+ * - Decrease the number of available threads until under the max_threads
338
+ * threshold.
339
+ * - Do nothing. The current number of threads fall in between the min and
340
+ * max thresholds. We'll let the pool manage itself.
341
+ */
342
+ for (int i = pool->cur_threads; i < pool->min_threads; i++) {
343
+ spawn_thread(pool);
344
+ }
345
+
346
+ for (int i = pool->cur_threads; i > pool->max_threads; i--) {
347
+ qemu_sem_post(&pool->sem);
348
+ }
349
+
350
+ qemu_mutex_unlock(&pool->lock);
351
+}
352
+
353
static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
354
{
355
if (!ctx) {
356
@@ -XXX,XX +XXX,XX @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
357
qemu_mutex_init(&pool->lock);
358
qemu_cond_init(&pool->worker_stopped);
359
qemu_sem_init(&pool->sem, 0);
360
- pool->max_threads = 64;
361
pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool);
362
363
QLIST_INIT(&pool->head);
364
QTAILQ_INIT(&pool->request_list);
365
+
366
+ thread_pool_update_params(pool, ctx);
367
}
368
369
ThreadPool *thread_pool_new(AioContext *ctx)
66
--
370
--
67
2.31.1
371
2.35.1
68
diff view generated by jsdifflib