1
The following changes since commit d0ed6a69d399ae193959225cdeaa9382746c91cc:
1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
2
2
3
Update version for v5.1.0 release (2020-08-11 17:07:03 +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://github.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 44277bf914471962c9e88e09c859aae65ae109c4:
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
10
10
11
aio-posix: keep aio_notify_me disabled during polling (2020-08-13 13:34:14 =
11
util/event-loop-base: Introduce options to set the thread pool size (2022-05-04 17:02:19 +0100)
12
+0100)
13
12
14
----------------------------------------------------------------
13
----------------------------------------------------------------
15
Pull request
14
Pull request
16
15
16
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
17
used for async I/O.
18
17
----------------------------------------------------------------
19
----------------------------------------------------------------
18
20
19
Stefan Hajnoczi (3):
21
Nicolas Saenz Julienne (3):
20
async: rename event_notifier_dummy_cb/poll()
22
Introduce event-loop-base abstract class
21
async: always set ctx->notified in aio_notify()
23
util/main-loop: Introduce the main loop into QOM
22
aio-posix: keep aio_notify_me disabled during polling
24
util/event-loop-base: Introduce options to set the thread pool size
23
25
24
util/aio-posix.c | 47 +++++++++++++++++++++++++----------------------
26
qapi/qom.json | 43 ++++++++--
25
util/async.c | 36 +++++++++++++++++++++++-------------
27
meson.build | 26 +++---
26
2 files changed, 48 insertions(+), 35 deletions(-)
28
include/block/aio.h | 10 +++
29
include/block/thread-pool.h | 3 +
30
include/qemu/main-loop.h | 10 +++
31
include/sysemu/event-loop-base.h | 41 +++++++++
32
include/sysemu/iothread.h | 6 +-
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
34
iothread.c | 68 +++++----------
35
util/aio-posix.c | 1 +
36
util/async.c | 20 +++++
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
27
42
28
--=20
43
--
29
2.26.2
44
2.35.1
30
diff view generated by jsdifflib
1
aio_notify() does not set ctx->notified when called with
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
ctx->aio_notify_me disabled. Therefore aio_notify_me needs to be enabled
2
3
during polling.
3
Introduce the 'event-loop-base' abstract class, it'll hold the
4
4
properties common to all event loops and provide the necessary hooks for
5
This is suboptimal since expensive event_notifier_set(&ctx->notifier)
5
their creation and maintenance. Then have iothread inherit from it.
6
and event_notifier_test_and_clear(&ctx->notifier) calls are required
6
7
when ctx->aio_notify_me is enabled.
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
Change aio_notify() so that aio->notified is always set, regardless of
9
function. It also provides an update_params() callback to propagate
10
ctx->aio_notify_me. This will make polling cheaper since
10
property changes onto its children.
11
ctx->aio_notify_me can remain disabled. Move the
11
12
event_notifier_test_and_clear() to the fd handler function (which is now
12
The new 'event-loop-base' class will live in the root directory. It is
13
no longer an empty function so "dummy" has been dropped from its name).
13
built on its own using the 'link_whole' option (there are no direct
14
14
function dependencies between the class and its children, it all happens
15
The next patch takes advantage of this by optimizing polling in
15
trough 'constructor' magic). And also imposes new compilation
16
util/aio-posix.c.
16
dependencies:
17
17
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
18
qom <- event-loop-base <- blockdev (iothread.c)
19
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
19
20
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
20
And in subsequent patches:
21
Message-id: 20200806131802.569478-3-stefanha@redhat.com
21
22
22
qom <- event-loop-base <- qemuutil (util/main-loop.c)
23
[Paolo Bonzini pointed out that the smp_wmb() in aio_notify_accept()
23
24
should be smp_wb() but the comment should be smp_wmb() instead of
24
All this forced some amount of reordering in meson.build:
25
smp_wb(). Fixed.
25
26
--Stefan]
26
- Moved qom build definition before qemuutil. Doing it the other way
27
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
28
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
39
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
29
---
40
---
30
util/async.c | 32 +++++++++++++++++++++-----------
41
qapi/qom.json | 22 +++++--
31
1 file changed, 21 insertions(+), 11 deletions(-)
42
meson.build | 23 ++++---
32
43
include/sysemu/event-loop-base.h | 36 +++++++++++
33
diff --git a/util/async.c b/util/async.c
44
include/sysemu/iothread.h | 6 +-
45
event-loop-base.c | 104 +++++++++++++++++++++++++++++++
46
iothread.c | 65 ++++++-------------
47
6 files changed, 192 insertions(+), 64 deletions(-)
48
create mode 100644 include/sysemu/event-loop-base.h
49
create mode 100644 event-loop-base.c
50
51
diff --git a/qapi/qom.json b/qapi/qom.json
34
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
35
--- a/util/async.c
53
--- a/qapi/qom.json
36
+++ b/util/async.c
54
+++ b/qapi/qom.json
37
@@ -XXX,XX +XXX,XX @@ LuringState *aio_get_linux_io_uring(AioContext *ctx)
55
@@ -XXX,XX +XXX,XX @@
38
56
'*repeat': 'bool',
39
void aio_notify(AioContext *ctx)
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 @@
77
# algorithm detects it is spending too long polling without
78
# encountering events. 0 selects a default behaviour (default: 0)
79
#
80
-# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
81
-# 0 means that the engine will use its default
82
-# (default:0, since 6.1)
83
+# The @aio-max-batch option is available since 6.1.
84
#
85
# Since: 2.0
86
##
87
{ 'struct': 'IothreadProperties',
88
+ 'base': 'EventLoopBaseProperties',
89
'data': { '*poll-max-ns': 'int',
90
'*poll-grow': 'int',
91
- '*poll-shrink': 'int',
92
- '*aio-max-batch': 'int' } }
93
+ '*poll-shrink': 'int' } }
94
95
##
96
# @MemoryBackendProperties:
97
diff --git a/meson.build b/meson.build
98
index XXXXXXX..XXXXXXX 100644
99
--- a/meson.build
100
+++ b/meson.build
101
@@ -XXX,XX +XXX,XX @@ subdir('qom')
102
subdir('authz')
103
subdir('crypto')
104
subdir('ui')
105
+subdir('hw')
106
107
108
if enable_modules
109
@@ -XXX,XX +XXX,XX @@ if enable_modules
110
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
111
endif
112
113
+qom_ss = qom_ss.apply(config_host, strict: false)
114
+libqom = static_library('qom', qom_ss.sources() + genh,
115
+ dependencies: [qom_ss.dependencies()],
116
+ name_suffix: 'fa')
117
+qom = declare_dependency(link_whole: libqom)
118
+
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.
175
+ */
176
+#ifndef QEMU_EVENT_LOOP_BASE_H
177
+#define QEMU_EVENT_LOOP_BASE_H
178
+
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
201
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
202
index XXXXXXX..XXXXXXX 100644
203
--- a/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;
219
@@ -XXX,XX +XXX,XX @@ struct IOThread {
220
int64_t poll_max_ns;
221
int64_t poll_grow;
222
int64_t poll_shrink;
223
-
224
- /* AioContext AIO engine parameters */
225
- int64_t aio_max_batch;
226
};
227
typedef struct IOThread IOThread;
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);
339
diff --git a/iothread.c b/iothread.c
340
index XXXXXXX..XXXXXXX 100644
341
--- a/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"
351
@@ -XXX,XX +XXX,XX @@ static void iothread_init_gcontext(IOThread *iothread)
352
iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE);
353
}
354
355
-static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
356
+static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
40
{
357
{
41
- /* Write e.g. bh->scheduled before reading ctx->notify_me. Pairs
358
+ IOThread *iothread = IOTHREAD(base);
42
+ /*
359
ERRP_GUARD();
43
+ * Write e.g. bh->flags before writing ctx->notified. Pairs with smp_mb in
360
44
+ * aio_notify_accept.
361
+ if (!iothread->ctx) {
45
+ */
362
+ return;
46
+ smp_wmb();
363
+ }
47
+ atomic_set(&ctx->notified, true);
364
+
48
+
365
aio_context_set_poll_params(iothread->ctx,
49
+ /*
366
iothread->poll_max_ns,
50
+ * Write ctx->notified before reading ctx->notify_me. Pairs
367
iothread->poll_grow,
51
* with smp_mb in aio_ctx_prepare or aio_poll.
368
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
369
}
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)
380
{
381
Error *local_error = NULL;
382
- IOThread *iothread = IOTHREAD(obj);
383
+ IOThread *iothread = IOTHREAD(base);
384
char *thread_name;
385
386
iothread->stopping = false;
387
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
52
*/
388
*/
53
smp_mb();
389
iothread_init_gcontext(iothread);
54
if (atomic_read(&ctx->notify_me)) {
390
55
event_notifier_set(&ctx->notifier);
391
- iothread_set_aio_context_params(iothread, &local_error);
56
- atomic_mb_set(&ctx->notified, true);
392
+ iothread_set_aio_context_params(base, &local_error);
393
if (local_error) {
394
error_propagate(errp, local_error);
395
aio_context_unref(iothread->ctx);
396
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
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 = {
407
"poll-shrink", offsetof(IOThread, poll_shrink),
408
};
409
-static IOThreadParamInfo aio_max_batch_info = {
410
- "aio-max-batch", offsetof(IOThread, aio_max_batch),
411
-};
412
413
static void iothread_get_param(Object *obj, Visitor *v,
414
const char *name, IOThreadParamInfo *info, Error **errp)
415
@@ -XXX,XX +XXX,XX @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
57
}
416
}
58
}
417
}
59
418
60
void aio_notify_accept(AioContext *ctx)
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
420
- const char *name, void *opaque, Error **errp)
421
-{
422
- IOThreadParamInfo *info = opaque;
423
-
424
- iothread_get_param(obj, v, name, info, errp);
425
-}
426
-
427
-static void iothread_set_aio_param(Object *obj, Visitor *v,
428
- const char *name, void *opaque, Error **errp)
429
-{
430
- IOThread *iothread = IOTHREAD(obj);
431
- IOThreadParamInfo *info = opaque;
432
-
433
- if (!iothread_set_param(obj, v, name, info, errp)) {
434
- return;
435
- }
436
-
437
- if (iothread->ctx) {
438
- aio_context_set_aio_params(iothread->ctx,
439
- iothread->aio_max_batch,
440
- errp);
441
- }
442
-}
443
-
444
static void iothread_class_init(ObjectClass *klass, void *class_data)
61
{
445
{
62
- if (atomic_xchg(&ctx->notified, false)
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
63
-#ifdef WIN32
447
- ucc->complete = iothread_complete;
64
- || true
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
65
-#endif
449
+
66
- ) {
450
+ bc->init = iothread_init;
67
- event_notifier_test_and_clear(&ctx->notifier);
451
+ bc->update_params = iothread_set_aio_context_params;
68
- }
452
69
+ atomic_set(&ctx->notified, false);
453
object_class_property_add(klass, "poll-max-ns", "int",
70
+
454
iothread_get_poll_param,
71
+ /*
455
@@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
72
+ * Write ctx->notified before reading e.g. bh->flags. Pairs with smp_wmb
456
iothread_get_poll_param,
73
+ * in aio_notify.
457
iothread_set_poll_param,
74
+ */
458
NULL, &poll_shrink_info);
75
+ smp_mb();
459
- object_class_property_add(klass, "aio-max-batch", "int",
460
- iothread_get_aio_param,
461
- iothread_set_aio_param,
462
- NULL, &aio_max_batch_info);
76
}
463
}
77
464
78
static void aio_timerlist_notify(void *opaque, QEMUClockType type)
465
static const TypeInfo iothread_info = {
79
@@ -XXX,XX +XXX,XX @@ static void aio_timerlist_notify(void *opaque, QEMUClockType type)
466
.name = TYPE_IOTHREAD,
80
aio_notify(opaque);
467
- .parent = TYPE_OBJECT,
81
}
468
+ .parent = TYPE_EVENT_LOOP_BASE,
82
469
.class_init = iothread_class_init,
83
-static void aio_context_notifier_dummy_cb(EventNotifier *e)
470
.instance_size = sizeof(IOThread),
84
+static void aio_context_notifier_cb(EventNotifier *e)
471
.instance_init = iothread_instance_init,
85
{
472
.instance_finalize = iothread_instance_finalize,
86
+ AioContext *ctx = container_of(e, AioContext, notifier);
473
- .interfaces = (InterfaceInfo[]) {
87
+
474
- {TYPE_USER_CREATABLE},
88
+ event_notifier_test_and_clear(&ctx->notifier);
475
- {}
89
}
476
- },
90
477
};
91
/* Returns true if aio_notify() was called (e.g. a BH was scheduled) */
478
92
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
479
static void iothread_register_types(void)
93
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
94
aio_set_event_notifier(ctx, &ctx->notifier,
481
info->poll_max_ns = iothread->poll_max_ns;
95
false,
482
info->poll_grow = iothread->poll_grow;
96
- aio_context_notifier_dummy_cb,
483
info->poll_shrink = iothread->poll_shrink;
97
+ aio_context_notifier_cb,
484
- info->aio_max_batch = iothread->aio_max_batch;
98
aio_context_notifier_poll);
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
99
#ifdef CONFIG_LINUX_AIO
486
100
ctx->linux_aio = NULL;
487
QAPI_LIST_APPEND(*tail, info);
488
return 0;
101
--
489
--
102
2.26.2
490
2.35.1
103
diff view generated by jsdifflib
1
The event_notifier_*() prefix can be confused with the EventNotifier
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
APIs that are also called event_notifier_*().
2
3
3
'event-loop-base' provides basic property handling for all 'AioContext'
4
Rename the functions to aio_context_notifier_*() to make it clear that
4
based event loops. So let's define a new 'MainLoopClass' that inherits
5
they relate to the AioContext::notifier field.
5
from it. This will permit tweaking the main loop's properties through
6
6
qapi as well as through the command line using the '-object' keyword[1].
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Only one instance of 'MainLoopClass' might be created at any time.
8
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
8
9
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
10
Message-id: 20200806131802.569478-2-stefanha@redhat.com
10
mark 'MainLoop' as non-deletable.
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
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
20
---
13
util/async.c | 8 ++++----
21
qapi/qom.json | 13 ++++++++
14
1 file changed, 4 insertions(+), 4 deletions(-)
22
meson.build | 3 +-
15
23
include/qemu/main-loop.h | 10 ++++++
16
diff --git a/util/async.c b/util/async.c
24
include/sysemu/event-loop-base.h | 1 +
17
index XXXXXXX..XXXXXXX 100644
25
event-loop-base.c | 13 ++++++++
18
--- a/util/async.c
26
util/main-loop.c | 56 ++++++++++++++++++++++++++++++++
19
+++ b/util/async.c
27
6 files changed, 95 insertions(+), 1 deletion(-)
20
@@ -XXX,XX +XXX,XX @@ static void aio_timerlist_notify(void *opaque, QEMUClockType type)
28
21
aio_notify(opaque);
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);
114
};
115
116
struct EventLoopBase {
117
diff --git a/event-loop-base.c b/event-loop-base.c
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
}
22
}
123
}
23
124
24
-static void event_notifier_dummy_cb(EventNotifier *e)
125
+static bool event_loop_base_can_be_deleted(UserCreatable *uc)
25
+static void aio_context_notifier_dummy_cb(EventNotifier *e)
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)
26
{
138
{
139
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
140
ucc->complete = event_loop_base_complete;
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;
27
}
159
}
28
160
29
/* Returns true if aio_notify() was called (e.g. a BH was scheduled) */
161
+static void main_loop_update_params(EventLoopBase *base, Error **errp)
30
-static bool event_notifier_poll(void *opaque)
162
+{
31
+static bool aio_context_notifier_poll(void *opaque)
163
+ if (!qemu_aio_context) {
32
{
164
+ error_setg(errp, "qemu aio context not ready");
33
EventNotifier *e = opaque;
165
+ return;
34
AioContext *ctx = container_of(e, AioContext, notifier);
166
+ }
35
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
167
+
36
168
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
37
aio_set_event_notifier(ctx, &ctx->notifier,
169
+}
38
false,
170
+
39
- event_notifier_dummy_cb,
171
+MainLoop *mloop;
40
- event_notifier_poll);
172
+
41
+ aio_context_notifier_dummy_cb,
173
+static void main_loop_init(EventLoopBase *base, Error **errp)
42
+ aio_context_notifier_poll);
174
+{
43
#ifdef CONFIG_LINUX_AIO
175
+ MainLoop *m = MAIN_LOOP(base);
44
ctx->linux_aio = NULL;
176
+
45
#endif
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
46
--
219
--
47
2.26.2
220
2.35.1
48
diff view generated by jsdifflib
1
Polling only monitors the ctx->notified field and does not need the
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
ctx->notifier EventNotifier to be signalled. Keep ctx->aio_notify_me
3
disabled while polling to avoid unnecessary EventNotifier syscalls.
4
2
5
This optimization improves virtio-blk 4KB random read performance by
3
The thread pool regulates itself: when idle, it kills threads until
6
18%. The following results are with an IOThread and the null-co block
4
empty, when in demand, it creates new threads until full. This behaviour
7
driver:
5
doesn't play well with latency sensitive workloads where the price of
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.
8
9
9
Test IOPS Error
10
In order to mitigate this let's introduce a new 'EventLoopBase'
10
Before 244518.62 ± 1.20%
11
property to set the thread pool size. The threads will be created during
11
After 290706.11 ± 0.44%
12
the pool's initialization or upon updating the property's value, remain
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
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
14
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
19
Acked-by: Markus Armbruster <armbru@redhat.com>
16
Message-id: 20200806131802.569478-4-stefanha@redhat.com
20
Message-id: 20220425075723.20019-4-nsaenzju@redhat.com
17
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
18
---
22
---
19
util/aio-posix.c | 47 +++++++++++++++++++++++++----------------------
23
qapi/qom.json | 10 +++++-
20
1 file changed, 25 insertions(+), 22 deletions(-)
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(-)
21
34
35
diff --git a/qapi/qom.json b/qapi/qom.json
36
index XXXXXXX..XXXXXXX 100644
37
--- a/qapi/qom.json
38
+++ b/qapi/qom.json
39
@@ -XXX,XX +XXX,XX @@
40
# 0 means that the engine will use its default.
41
# (default: 0)
42
#
43
+# @thread-pool-min: minimum number of threads reserved in the thread pool
44
+# (default:0)
45
+#
46
+# @thread-pool-max: maximum number of threads the thread pool can contain
47
+# (default:64)
48
+#
49
# Since: 7.1
50
##
51
{ 'struct': 'EventLoopBaseProperties',
52
- 'data': { '*aio-max-batch': 'int' } }
53
+ 'data': { '*aio-max-batch': 'int',
54
+ '*thread-pool-min': 'int',
55
+ '*thread-pool-max': 'int' } }
56
57
##
58
# @IothreadProperties:
59
diff --git a/include/block/aio.h b/include/block/aio.h
60
index XXXXXXX..XXXXXXX 100644
61
--- a/include/block/aio.h
62
+++ b/include/block/aio.h
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
65
QEMUBH *co_schedule_bh;
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
22
diff --git a/util/aio-posix.c b/util/aio-posix.c
190
diff --git a/util/aio-posix.c b/util/aio-posix.c
23
index XXXXXXX..XXXXXXX 100644
191
index XXXXXXX..XXXXXXX 100644
24
--- a/util/aio-posix.c
192
--- a/util/aio-posix.c
25
+++ b/util/aio-posix.c
193
+++ b/util/aio-posix.c
26
@@ -XXX,XX +XXX,XX @@ static bool remove_idle_poll_handlers(AioContext *ctx, int64_t now)
194
@@ -XXX,XX +XXX,XX @@
27
*
195
28
* Polls for a given time.
196
#include "qemu/osdep.h"
29
*
197
#include "block/block.h"
30
- * Note that ctx->notify_me must be non-zero so this function can detect
198
+#include "block/thread-pool.h"
31
- * aio_notify().
199
#include "qemu/main-loop.h"
32
- *
200
#include "qemu/rcu.h"
33
* Note that the caller must have incremented ctx->list_lock.
201
#include "qemu/rcu_queue.h"
34
*
202
diff --git a/util/async.c b/util/async.c
35
* Returns: true if progress was made, false otherwise
203
index XXXXXXX..XXXXXXX 100644
36
@@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout)
204
--- a/util/async.c
37
bool progress;
205
+++ b/util/async.c
38
int64_t start_time, elapsed_time;
206
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
39
207
40
- assert(ctx->notify_me);
208
ctx->aio_max_batch = 0;
41
assert(qemu_lockcnt_count(&ctx->list_lock) > 0);
209
42
210
+ ctx->thread_pool_min = 0;
43
trace_run_poll_handlers_begin(ctx, max_ns, *timeout);
211
+ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
44
@@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout)
212
+
45
* @timeout: timeout for blocking wait, computed by the caller and updated if
213
return ctx;
46
* polling succeeds.
214
fail:
47
*
215
g_source_destroy(&ctx->source);
48
- * ctx->notify_me must be non-zero so this function can detect aio_notify().
216
@@ -XXX,XX +XXX,XX @@ void qemu_set_current_aio_context(AioContext *ctx)
49
- *
217
assert(!get_my_aiocontext());
50
* Note that the caller must have incremented ctx->list_lock.
218
set_my_aiocontext(ctx);
51
*
219
}
52
* Returns: true if progress was made, false otherwise
220
+
53
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
221
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
54
AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list);
222
+ int64_t max, Error **errp)
55
int ret = 0;
223
+{
56
bool progress;
224
+
57
+ bool use_notify_me;
225
+ if (min > max || !max || min > INT_MAX || max > INT_MAX) {
58
int64_t timeout;
226
+ error_setg(errp, "bad thread-pool-min/thread-pool-max values");
59
int64_t start = 0;
227
+ return;
60
228
+ }
61
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
229
+
62
*/
230
+ ctx->thread_pool_min = min;
63
assert(in_aio_context_home_thread(ctx));
231
+ ctx->thread_pool_max = max;
64
232
+
65
- /* aio_notify can avoid the expensive event_notifier_set if
233
+ if (ctx->thread_pool) {
66
+ qemu_lockcnt_inc(&ctx->list_lock);
234
+ thread_pool_update_params(ctx->thread_pool, ctx);
67
+
235
+ }
68
+ if (ctx->poll_max_ns) {
236
+}
69
+ start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
237
diff --git a/util/main-loop.c b/util/main-loop.c
70
+ }
238
index XXXXXXX..XXXXXXX 100644
71
+
239
--- a/util/main-loop.c
72
+ timeout = blocking ? aio_compute_timeout(ctx) : 0;
240
+++ b/util/main-loop.c
73
+ progress = try_poll_mode(ctx, &timeout);
241
@@ -XXX,XX +XXX,XX @@
74
+ assert(!(timeout && progress));
242
#include "sysemu/replay.h"
75
+
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;
258
}
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
+{
76
+ /*
292
+ /*
77
+ * aio_notify can avoid the expensive event_notifier_set if
293
+ * The semaphore timed out, we should exit the loop except when:
78
* everything (file descriptors, bottom halves, timers) will
294
+ * - There is work to do, we raced with the signal.
79
* be re-evaluated before the next blocking poll(). This is
295
+ * - The max threads threshold just changed, we raced with the signal.
80
* already true when aio_poll is called with blocking == false;
296
+ * - The thread pool forces a minimum number of readily available threads.
81
* if blocking == true, it is only true after poll() returns,
297
+ */
82
* so disable the optimization now.
298
+ if (ret == -1 && (!QTAILQ_EMPTY(&pool->request_list) ||
83
*/
299
+ pool->cur_threads > pool->max_threads ||
84
- if (blocking) {
300
+ pool->cur_threads <= pool->min_threads)) {
85
+ use_notify_me = timeout != 0;
301
+ return true;
86
+ if (use_notify_me) {
302
+ }
87
atomic_set(&ctx->notify_me, atomic_read(&ctx->notify_me) + 2);
303
+
88
/*
304
+ return false;
89
- * Write ctx->notify_me before computing the timeout
305
+}
90
- * (reading bottom half flags, etc.). Pairs with
306
+
91
+ * Write ctx->notify_me before reading ctx->notified. Pairs with
307
static void *worker_thread(void *opaque)
92
* smp_mb in aio_notify().
308
{
93
*/
309
ThreadPool *pool = opaque;
94
smp_mb();
310
@@ -XXX,XX +XXX,XX @@ static void *worker_thread(void *opaque)
95
- }
311
ret = qemu_sem_timedwait(&pool->sem, 10000);
96
-
312
qemu_mutex_lock(&pool->lock);
97
- qemu_lockcnt_inc(&ctx->list_lock);
313
pool->idle_threads--;
98
314
- } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list));
99
- if (ctx->poll_max_ns) {
315
- if (ret == -1 || pool->stopping) {
100
- start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
316
+ } while (back_to_sleep(pool, ret));
101
+ /* Don't block if aio_notify() was called */
317
+ if (ret == -1 || pool->stopping ||
102
+ if (atomic_read(&ctx->notified)) {
318
+ pool->cur_threads > pool->max_threads) {
103
+ timeout = 0;
319
break;
104
+ }
320
}
105
}
321
106
322
@@ -XXX,XX +XXX,XX @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg)
107
- timeout = blocking ? aio_compute_timeout(ctx) : 0;
323
thread_pool_submit_aio(pool, func, arg, NULL, NULL);
108
- progress = try_poll_mode(ctx, &timeout);
324
}
109
- assert(!(timeout && progress));
325
110
-
326
+void thread_pool_update_params(ThreadPool *pool, AioContext *ctx)
111
/* If polling is allowed, non-blocking aio_poll does not need the
327
+{
112
* system call---a single round of run_poll_handlers_once suffices.
328
+ qemu_mutex_lock(&pool->lock);
113
*/
329
+
114
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
330
+ pool->min_threads = ctx->thread_pool_min;
115
ret = ctx->fdmon_ops->wait(ctx, &ready_list, timeout);
331
+ pool->max_threads = ctx->thread_pool_max;
116
}
332
+
117
333
+ /*
118
- if (blocking) {
334
+ * We either have to:
119
+ if (use_notify_me) {
335
+ * - Increase the number available of threads until over the min_threads
120
/* Finish the poll before clearing the flag. */
336
+ * threshold.
121
- atomic_store_release(&ctx->notify_me, atomic_read(&ctx->notify_me) - 2);
337
+ * - Decrease the number of available threads until under the max_threads
122
- aio_notify_accept(ctx);
338
+ * threshold.
123
+ atomic_store_release(&ctx->notify_me,
339
+ * - Do nothing. The current number of threads fall in between the min and
124
+ atomic_read(&ctx->notify_me) - 2);
340
+ * max thresholds. We'll let the pool manage itself.
125
}
341
+ */
126
342
+ for (int i = pool->cur_threads; i < pool->min_threads; i++) {
127
+ aio_notify_accept(ctx);
343
+ spawn_thread(pool);
128
+
344
+ }
129
/* Adjust polling time */
345
+
130
if (ctx->poll_max_ns) {
346
+ for (int i = pool->cur_threads; i > pool->max_threads; i--) {
131
int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start;
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)
132
--
370
--
133
2.26.2
371
2.35.1
134
diff view generated by jsdifflib