1
The following changes since commit 711c0418c8c1ce3a24346f058b001c4c5a2f0f81:
1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
2
2
3
Merge remote-tracking branch 'remotes/philmd/tags/mips-20210702' into staging (2021-07-04 14:04:12 +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 9f460c64e13897117f35ffb61f6f5e0102cabc70:
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
10
10
11
block/io: Merge discard request alignments (2021-07-06 14:28:55 +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
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
17
used for async I/O.
18
16
----------------------------------------------------------------
19
----------------------------------------------------------------
17
20
18
Akihiko Odaki (3):
21
Nicolas Saenz Julienne (3):
19
block/file-posix: Optimize for macOS
22
Introduce event-loop-base abstract class
20
block: Add backend_defaults property
23
util/main-loop: Introduce the main loop into QOM
21
block/io: Merge discard request alignments
24
util/event-loop-base: Introduce options to set the thread pool size
22
25
23
Stefan Hajnoczi (2):
26
qapi/qom.json | 43 ++++++++--
24
util/async: add a human-readable name to BHs for debugging
27
meson.build | 26 +++---
25
util/async: print leaked BH name when AioContext finalizes
28
include/block/aio.h | 10 +++
26
29
include/block/thread-pool.h | 3 +
27
include/block/aio.h | 31 ++++++++++++++++++++++---
30
include/qemu/main-loop.h | 10 +++
28
include/hw/block/block.h | 3 +++
31
include/sysemu/event-loop-base.h | 41 +++++++++
29
include/qemu/main-loop.h | 4 +++-
32
include/sysemu/iothread.h | 6 +-
30
block/file-posix.c | 27 ++++++++++++++++++++--
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
31
block/io.c | 2 ++
34
iothread.c | 68 +++++----------
32
hw/block/block.c | 42 ++++++++++++++++++++++++++++++----
35
util/aio-posix.c | 1 +
33
tests/unit/ptimer-test-stubs.c | 2 +-
36
util/async.c | 20 +++++
34
util/async.c | 25 ++++++++++++++++----
37
util/main-loop.c | 65 ++++++++++++++
35
util/main-loop.c | 4 ++--
38
util/thread-pool.c | 55 +++++++++++-
36
tests/qemu-iotests/172.out | 38 ++++++++++++++++++++++++++++++
39
13 files changed, 419 insertions(+), 69 deletions(-)
37
10 files changed, 161 insertions(+), 17 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: Akihiko Odaki <akihiko.odaki@gmail.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
This commit introduces "punch hole" operation and optimizes transfer
3
Introduce the 'event-loop-base' abstract class, it'll hold the
4
block size for macOS.
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
Thanks to Konstantin Nazarov for detailed analysis of a flaw in an
6
7
old version of this change:
7
EventLoopBaseClass is defined as user creatable and provides a hook for
8
https://gist.github.com/akihikodaki/87df4149e7ca87f18dc56807ec5a1bc5#gistcomment-3654667
8
its children to attach themselves to the user creatable class 'complete'
9
9
function. It also provides an update_params() callback to propagate
10
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
10
property changes onto its children.
11
Message-id: 20210705130458.97642-1-akihiko.odaki@gmail.com
11
12
The new 'event-loop-base' class will live in the root directory. It is
13
built on its own using the 'link_whole' option (there are no direct
14
function dependencies between the class and its children, it all happens
15
trough 'constructor' magic). And also imposes new compilation
16
dependencies:
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
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
39
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
40
---
14
block/file-posix.c | 27 +++++++++++++++++++++++++--
41
qapi/qom.json | 22 +++++--
15
1 file changed, 25 insertions(+), 2 deletions(-)
42
meson.build | 23 ++++---
16
43
include/sysemu/event-loop-base.h | 36 +++++++++++
17
diff --git a/block/file-posix.c b/block/file-posix.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
18
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
19
--- a/block/file-posix.c
53
--- a/qapi/qom.json
20
+++ b/block/file-posix.c
54
+++ b/qapi/qom.json
21
@@ -XXX,XX +XXX,XX @@
55
@@ -XXX,XX +XXX,XX @@
22
#if defined(HAVE_HOST_BLOCK_DEVICE)
56
'*repeat': 'bool',
23
#include <paths.h>
57
'*grab-toggle': 'GrabToggleKeys' } }
24
#include <sys/param.h>
58
25
+#include <sys/mount.h>
59
+##
26
#include <IOKit/IOKitLib.h>
60
+# @EventLoopBaseProperties:
27
#include <IOKit/IOBSD.h>
61
+#
28
#include <IOKit/storage/IOMediaBSDClient.h>
62
+# Common properties for event loops
29
@@ -XXX,XX +XXX,XX @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
63
+#
30
return;
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)
357
{
358
+ IOThread *iothread = IOTHREAD(base);
359
ERRP_GUARD();
360
361
+ if (!iothread->ctx) {
362
+ return;
363
+ }
364
+
365
aio_context_set_poll_params(iothread->ctx,
366
iothread->poll_max_ns,
367
iothread->poll_grow,
368
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
31
}
369
}
32
370
33
+#if defined(__APPLE__) && (__MACH__)
371
aio_context_set_aio_params(iothread->ctx,
34
+ struct statfs buf;
372
- iothread->aio_max_batch,
35
+
373
+ iothread->parent_obj.aio_max_batch,
36
+ if (!fstatfs(s->fd, &buf)) {
374
errp);
37
+ bs->bl.opt_transfer = buf.f_iosize;
375
}
38
+ bs->bl.pdiscard_alignment = buf.f_bsize;
376
39
+ }
377
-static void iothread_complete(UserCreatable *obj, Error **errp)
40
+#endif
378
+
41
+
379
+static void iothread_init(EventLoopBase *base, Error **errp)
42
if (bs->sg || S_ISBLK(st.st_mode)) {
380
{
43
int ret = hdev_get_max_hw_transfer(s->fd, &st);
381
Error *local_error = NULL;
44
382
- IOThread *iothread = IOTHREAD(obj);
45
@@ -XXX,XX +XXX,XX @@ out:
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)
388
*/
389
iothread_init_gcontext(iothread);
390
391
- iothread_set_aio_context_params(iothread, &local_error);
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,
46
}
416
}
47
}
417
}
48
418
49
+#if defined(CONFIG_FALLOCATE) || defined(BLKZEROOUT) || defined(BLKDISCARD)
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
50
static int translate_err(int err)
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)
51
{
445
{
52
if (err == -ENODEV || err == -ENOSYS || err == -EOPNOTSUPP ||
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
53
@@ -XXX,XX +XXX,XX @@ static int translate_err(int err)
447
- ucc->complete = iothread_complete;
54
}
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
55
return err;
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,
455
@@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
456
iothread_get_poll_param,
457
iothread_set_poll_param,
458
NULL, &poll_shrink_info);
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);
56
}
463
}
57
+#endif
464
58
465
static const TypeInfo iothread_info = {
59
#ifdef CONFIG_FALLOCATE
466
.name = TYPE_IOTHREAD,
60
static int do_fallocate(int fd, int mode, off_t offset, off_t len)
467
- .parent = TYPE_OBJECT,
61
@@ -XXX,XX +XXX,XX @@ static int handle_aiocb_discard(void *opaque)
468
+ .parent = TYPE_EVENT_LOOP_BASE,
62
}
469
.class_init = iothread_class_init,
63
} while (errno == EINTR);
470
.instance_size = sizeof(IOThread),
64
471
.instance_init = iothread_instance_init,
65
- ret = -errno;
472
.instance_finalize = iothread_instance_finalize,
66
+ ret = translate_err(-errno);
473
- .interfaces = (InterfaceInfo[]) {
67
#endif
474
- {TYPE_USER_CREATABLE},
68
} else {
475
- {}
69
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
476
- },
70
ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
477
};
71
aiocb->aio_offset, aiocb->aio_nbytes);
478
72
+ ret = translate_err(-errno);
479
static void iothread_register_types(void)
73
+#elif defined(__APPLE__) && (__MACH__)
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
74
+ fpunchhole_t fpunchhole;
481
info->poll_max_ns = iothread->poll_max_ns;
75
+ fpunchhole.fp_flags = 0;
482
info->poll_grow = iothread->poll_grow;
76
+ fpunchhole.reserved = 0;
483
info->poll_shrink = iothread->poll_shrink;
77
+ fpunchhole.fp_offset = aiocb->aio_offset;
484
- info->aio_max_batch = iothread->aio_max_batch;
78
+ fpunchhole.fp_length = aiocb->aio_nbytes;
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
79
+ if (fcntl(s->fd, F_PUNCHHOLE, &fpunchhole) == -1) {
486
80
+ ret = errno == ENODEV ? -ENOTSUP : -errno;
487
QAPI_LIST_APPEND(*tail, info);
81
+ } else {
488
return 0;
82
+ ret = 0;
83
+ }
84
#endif
85
}
86
87
- ret = translate_err(ret);
88
if (ret == -ENOTSUP) {
89
s->has_discard = false;
90
}
91
--
489
--
92
2.31.1
490
2.35.1
93
diff view generated by jsdifflib
1
From: Akihiko Odaki <akihiko.odaki@gmail.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
backend_defaults property allow users to control if default block
3
'event-loop-base' provides basic property handling for all 'AioContext'
4
properties should be decided with backend information.
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
If it is off, any backend information will be discarded, which is
6
qapi as well as through the command line using the '-object' keyword[1].
7
suitable if you plan to perform live migration to a different disk backend.
7
Only one instance of 'MainLoopClass' might be created at any time.
8
8
9
If it is on, a block device may utilize backend information more
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
10
aggressively.
10
mark 'MainLoop' as non-deletable.
11
11
12
By default, it is auto, which uses backend information for block
12
[1] For example:
13
sizes and ignores the others, which is consistent with the older
13
-object main-loop,id=main-loop,aio-max-batch=<value>
14
versions.
14
15
15
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
16
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
16
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Message-id: 20210705130458.97642-2-akihiko.odaki@gmail.com
17
Acked-by: Markus Armbruster <armbru@redhat.com>
18
Message-id: 20220425075723.20019-3-nsaenzju@redhat.com
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
---
20
---
20
include/hw/block/block.h | 3 +++
21
qapi/qom.json | 13 ++++++++
21
hw/block/block.c | 42 ++++++++++++++++++++++++++++++++++----
22
meson.build | 3 +-
22
tests/qemu-iotests/172.out | 38 ++++++++++++++++++++++++++++++++++
23
include/qemu/main-loop.h | 10 ++++++
23
3 files changed, 79 insertions(+), 4 deletions(-)
24
include/sysemu/event-loop-base.h | 1 +
24
25
event-loop-base.c | 13 ++++++++
25
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
26
util/main-loop.c | 56 ++++++++++++++++++++++++++++++++
26
index XXXXXXX..XXXXXXX 100644
27
6 files changed, 95 insertions(+), 1 deletion(-)
27
--- a/include/hw/block/block.h
28
28
+++ b/include/hw/block/block.h
29
diff --git a/qapi/qom.json b/qapi/qom.json
29
@@ -XXX,XX +XXX,XX @@
30
index XXXXXXX..XXXXXXX 100644
30
31
--- a/qapi/qom.json
31
typedef struct BlockConf {
32
+++ b/qapi/qom.json
32
BlockBackend *blk;
33
@@ -XXX,XX +XXX,XX @@
33
+ OnOffAuto backend_defaults;
34
'*poll-grow': 'int',
34
uint32_t physical_block_size;
35
'*poll-shrink': 'int' } }
35
uint32_t logical_block_size;
36
36
uint32_t min_io_size;
37
+##
37
@@ -XXX,XX +XXX,XX @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
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
}
38
}
123
}
39
124
40
#define DEFINE_BLOCK_PROPERTIES_BASE(_state, _conf) \
125
+static bool event_loop_base_can_be_deleted(UserCreatable *uc)
41
+ DEFINE_PROP_ON_OFF_AUTO("backend_defaults", _state, \
126
+{
42
+ _conf.backend_defaults, ON_OFF_AUTO_AUTO), \
127
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
43
DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \
128
+ EventLoopBase *backend = EVENT_LOOP_BASE(uc);
44
_conf.logical_block_size), \
129
+
45
DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \
130
+ if (bc->can_be_deleted) {
46
diff --git a/hw/block/block.c b/hw/block/block.c
131
+ return bc->can_be_deleted(backend);
47
index XXXXXXX..XXXXXXX 100644
132
+ }
48
--- a/hw/block/block.c
133
+
49
+++ b/hw/block/block.c
134
+ return true;
50
@@ -XXX,XX +XXX,XX @@ bool blkconf_blocksizes(BlockConf *conf, Error **errp)
135
+}
136
+
137
static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
51
{
138
{
52
BlockBackend *blk = conf->blk;
139
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
53
BlockSizes blocksizes;
140
ucc->complete = event_loop_base_complete;
54
- int backend_ret;
141
+ ucc->can_be_deleted = event_loop_base_can_be_deleted;
55
+ BlockDriverState *bs;
142
56
+ bool use_blocksizes;
143
object_class_property_add(klass, "aio-max-batch", "int",
57
+ bool use_bs;
144
event_loop_base_get_param,
58
+
145
diff --git a/util/main-loop.c b/util/main-loop.c
59
+ switch (conf->backend_defaults) {
146
index XXXXXXX..XXXXXXX 100644
60
+ case ON_OFF_AUTO_AUTO:
147
--- a/util/main-loop.c
61
+ use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes);
148
+++ b/util/main-loop.c
62
+ use_bs = false;
149
@@ -XXX,XX +XXX,XX @@
63
+ break;
150
#include "qemu/error-report.h"
64
+
151
#include "qemu/queue.h"
65
+ case ON_OFF_AUTO_ON:
152
#include "qemu/compiler.h"
66
+ use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes);
153
+#include "qom/object.h"
67
+ bs = blk_bs(blk);
154
68
+ use_bs = bs;
155
#ifndef _WIN32
69
+ break;
156
#include <sys/wait.h>
70
+
157
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
71
+ case ON_OFF_AUTO_OFF:
158
return 0;
72
+ use_blocksizes = false;
159
}
73
+ use_bs = false;
160
74
+ break;
161
+static void main_loop_update_params(EventLoopBase *base, Error **errp)
75
+
162
+{
76
+ default:
163
+ if (!qemu_aio_context) {
77
+ abort();
164
+ error_setg(errp, "qemu aio context not ready");
165
+ return;
78
+ }
166
+ }
79
167
+
80
- backend_ret = blk_probe_blocksizes(blk, &blocksizes);
168
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
81
/* fill in detected values if they are not defined via qemu command line */
169
+}
82
if (!conf->physical_block_size) {
170
+
83
- if (!backend_ret) {
171
+MainLoop *mloop;
84
+ if (use_blocksizes) {
172
+
85
conf->physical_block_size = blocksizes.phys;
173
+static void main_loop_init(EventLoopBase *base, Error **errp)
86
} else {
174
+{
87
conf->physical_block_size = BDRV_SECTOR_SIZE;
175
+ MainLoop *m = MAIN_LOOP(base);
88
}
176
+
89
}
177
+ if (mloop) {
90
if (!conf->logical_block_size) {
178
+ error_setg(errp, "only one main-loop instance allowed");
91
- if (!backend_ret) {
179
+ return;
92
+ if (use_blocksizes) {
93
conf->logical_block_size = blocksizes.log;
94
} else {
95
conf->logical_block_size = BDRV_SECTOR_SIZE;
96
}
97
}
98
+ if (use_bs) {
99
+ if (!conf->opt_io_size) {
100
+ conf->opt_io_size = bs->bl.opt_transfer;
101
+ }
102
+ if (conf->discard_granularity == -1) {
103
+ if (bs->bl.pdiscard_alignment) {
104
+ conf->discard_granularity = bs->bl.pdiscard_alignment;
105
+ } else if (bs->bl.request_alignment != 1) {
106
+ conf->discard_granularity = bs->bl.request_alignment;
107
+ }
108
+ }
109
+ }
180
+ }
110
181
+
111
if (conf->logical_block_size > conf->physical_block_size) {
182
+ main_loop_update_params(base, errp);
112
error_setg(errp,
183
+
113
diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out
184
+ mloop = m;
114
index XXXXXXX..XXXXXXX 100644
185
+ return;
115
--- a/tests/qemu-iotests/172.out
186
+}
116
+++ b/tests/qemu-iotests/172.out
187
+
117
@@ -XXX,XX +XXX,XX @@ Testing:
188
+static bool main_loop_can_be_deleted(EventLoopBase *base)
118
dev: floppy, id ""
189
+{
119
unit = 0 (0x0)
190
+ return false;
120
drive = "floppy0"
191
+}
121
+ backend_defaults = "auto"
192
+
122
logical_block_size = 512 (512 B)
193
+static void main_loop_class_init(ObjectClass *oc, void *class_data)
123
physical_block_size = 512 (512 B)
194
+{
124
min_io_size = 0 (0 B)
195
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc);
125
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2
196
+
126
dev: floppy, id ""
197
+ bc->init = main_loop_init;
127
unit = 0 (0x0)
198
+ bc->update_params = main_loop_update_params;
128
drive = "floppy0"
199
+ bc->can_be_deleted = main_loop_can_be_deleted;
129
+ backend_defaults = "auto"
200
+}
130
logical_block_size = 512 (512 B)
201
+
131
physical_block_size = 512 (512 B)
202
+static const TypeInfo main_loop_info = {
132
min_io_size = 0 (0 B)
203
+ .name = TYPE_MAIN_LOOP,
133
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2
204
+ .parent = TYPE_EVENT_LOOP_BASE,
134
dev: floppy, id ""
205
+ .class_init = main_loop_class_init,
135
unit = 1 (0x1)
206
+ .instance_size = sizeof(MainLoop),
136
drive = "floppy1"
207
+};
137
+ backend_defaults = "auto"
208
+
138
logical_block_size = 512 (512 B)
209
+static void main_loop_register_types(void)
139
physical_block_size = 512 (512 B)
210
+{
140
min_io_size = 0 (0 B)
211
+ type_register_static(&main_loop_info);
141
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2
212
+}
142
dev: floppy, id ""
213
+
143
unit = 0 (0x0)
214
+type_init(main_loop_register_types)
144
drive = "floppy0"
215
+
145
+ backend_defaults = "auto"
216
static int max_priority;
146
logical_block_size = 512 (512 B)
217
147
physical_block_size = 512 (512 B)
218
#ifndef _WIN32
148
min_io_size = 0 (0 B)
149
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2
150
dev: floppy, id ""
151
unit = 1 (0x1)
152
drive = "floppy1"
153
+ backend_defaults = "auto"
154
logical_block_size = 512 (512 B)
155
physical_block_size = 512 (512 B)
156
min_io_size = 0 (0 B)
157
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2
158
dev: floppy, id ""
159
unit = 0 (0x0)
160
drive = "floppy0"
161
+ backend_defaults = "auto"
162
logical_block_size = 512 (512 B)
163
physical_block_size = 512 (512 B)
164
min_io_size = 0 (0 B)
165
@@ -XXX,XX +XXX,XX @@ Testing: -fdb
166
dev: floppy, id ""
167
unit = 1 (0x1)
168
drive = "floppy1"
169
+ backend_defaults = "auto"
170
logical_block_size = 512 (512 B)
171
physical_block_size = 512 (512 B)
172
min_io_size = 0 (0 B)
173
@@ -XXX,XX +XXX,XX @@ Testing: -fdb
174
dev: floppy, id ""
175
unit = 0 (0x0)
176
drive = "floppy0"
177
+ backend_defaults = "auto"
178
logical_block_size = 512 (512 B)
179
physical_block_size = 512 (512 B)
180
min_io_size = 0 (0 B)
181
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2
182
dev: floppy, id ""
183
unit = 0 (0x0)
184
drive = "floppy0"
185
+ backend_defaults = "auto"
186
logical_block_size = 512 (512 B)
187
physical_block_size = 512 (512 B)
188
min_io_size = 0 (0 B)
189
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
190
dev: floppy, id ""
191
unit = 1 (0x1)
192
drive = "floppy1"
193
+ backend_defaults = "auto"
194
logical_block_size = 512 (512 B)
195
physical_block_size = 512 (512 B)
196
min_io_size = 0 (0 B)
197
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
198
dev: floppy, id ""
199
unit = 0 (0x0)
200
drive = "floppy0"
201
+ backend_defaults = "auto"
202
logical_block_size = 512 (512 B)
203
physical_block_size = 512 (512 B)
204
min_io_size = 0 (0 B)
205
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t
206
dev: floppy, id ""
207
unit = 1 (0x1)
208
drive = "floppy1"
209
+ backend_defaults = "auto"
210
logical_block_size = 512 (512 B)
211
physical_block_size = 512 (512 B)
212
min_io_size = 0 (0 B)
213
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t
214
dev: floppy, id ""
215
unit = 0 (0x0)
216
drive = "floppy0"
217
+ backend_defaults = "auto"
218
logical_block_size = 512 (512 B)
219
physical_block_size = 512 (512 B)
220
min_io_size = 0 (0 B)
221
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
222
dev: floppy, id ""
223
unit = 0 (0x0)
224
drive = "none0"
225
+ backend_defaults = "auto"
226
logical_block_size = 512 (512 B)
227
physical_block_size = 512 (512 B)
228
min_io_size = 0 (0 B)
229
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
230
dev: floppy, id ""
231
unit = 1 (0x1)
232
drive = "none0"
233
+ backend_defaults = "auto"
234
logical_block_size = 512 (512 B)
235
physical_block_size = 512 (512 B)
236
min_io_size = 0 (0 B)
237
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
238
dev: floppy, id ""
239
unit = 1 (0x1)
240
drive = "none1"
241
+ backend_defaults = "auto"
242
logical_block_size = 512 (512 B)
243
physical_block_size = 512 (512 B)
244
min_io_size = 0 (0 B)
245
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
246
dev: floppy, id ""
247
unit = 0 (0x0)
248
drive = "none0"
249
+ backend_defaults = "auto"
250
logical_block_size = 512 (512 B)
251
physical_block_size = 512 (512 B)
252
min_io_size = 0 (0 B)
253
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
254
dev: floppy, id ""
255
unit = 1 (0x1)
256
drive = "none0"
257
+ backend_defaults = "auto"
258
logical_block_size = 512 (512 B)
259
physical_block_size = 512 (512 B)
260
min_io_size = 0 (0 B)
261
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
262
dev: floppy, id ""
263
unit = 0 (0x0)
264
drive = "floppy0"
265
+ backend_defaults = "auto"
266
logical_block_size = 512 (512 B)
267
physical_block_size = 512 (512 B)
268
min_io_size = 0 (0 B)
269
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
270
dev: floppy, id ""
271
unit = 1 (0x1)
272
drive = "none0"
273
+ backend_defaults = "auto"
274
logical_block_size = 512 (512 B)
275
physical_block_size = 512 (512 B)
276
min_io_size = 0 (0 B)
277
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
278
dev: floppy, id ""
279
unit = 0 (0x0)
280
drive = "floppy0"
281
+ backend_defaults = "auto"
282
logical_block_size = 512 (512 B)
283
physical_block_size = 512 (512 B)
284
min_io_size = 0 (0 B)
285
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
286
dev: floppy, id ""
287
unit = 0 (0x0)
288
drive = "none0"
289
+ backend_defaults = "auto"
290
logical_block_size = 512 (512 B)
291
physical_block_size = 512 (512 B)
292
min_io_size = 0 (0 B)
293
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
294
dev: floppy, id ""
295
unit = 1 (0x1)
296
drive = "floppy1"
297
+ backend_defaults = "auto"
298
logical_block_size = 512 (512 B)
299
physical_block_size = 512 (512 B)
300
min_io_size = 0 (0 B)
301
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
302
dev: floppy, id ""
303
unit = 0 (0x0)
304
drive = "none0"
305
+ backend_defaults = "auto"
306
logical_block_size = 512 (512 B)
307
physical_block_size = 512 (512 B)
308
min_io_size = 0 (0 B)
309
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
310
dev: floppy, id ""
311
unit = 1 (0x1)
312
drive = "floppy1"
313
+ backend_defaults = "auto"
314
logical_block_size = 512 (512 B)
315
physical_block_size = 512 (512 B)
316
min_io_size = 0 (0 B)
317
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
318
dev: floppy, id ""
319
unit = 1 (0x1)
320
drive = "none0"
321
+ backend_defaults = "auto"
322
logical_block_size = 512 (512 B)
323
physical_block_size = 512 (512 B)
324
min_io_size = 0 (0 B)
325
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
326
dev: floppy, id ""
327
unit = 0 (0x0)
328
drive = "floppy0"
329
+ backend_defaults = "auto"
330
logical_block_size = 512 (512 B)
331
physical_block_size = 512 (512 B)
332
min_io_size = 0 (0 B)
333
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
334
dev: floppy, id ""
335
unit = 1 (0x1)
336
drive = "none0"
337
+ backend_defaults = "auto"
338
logical_block_size = 512 (512 B)
339
physical_block_size = 512 (512 B)
340
min_io_size = 0 (0 B)
341
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
342
dev: floppy, id ""
343
unit = 0 (0x0)
344
drive = "floppy0"
345
+ backend_defaults = "auto"
346
logical_block_size = 512 (512 B)
347
physical_block_size = 512 (512 B)
348
min_io_size = 0 (0 B)
349
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global floppy.drive=none0 -device
350
dev: floppy, id ""
351
unit = 0 (0x0)
352
drive = "none0"
353
+ backend_defaults = "auto"
354
logical_block_size = 512 (512 B)
355
physical_block_size = 512 (512 B)
356
min_io_size = 0 (0 B)
357
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy
358
dev: floppy, id ""
359
unit = 0 (0x0)
360
drive = ""
361
+ backend_defaults = "auto"
362
logical_block_size = 512 (512 B)
363
physical_block_size = 512 (512 B)
364
min_io_size = 0 (0 B)
365
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=120
366
dev: floppy, id ""
367
unit = 0 (0x0)
368
drive = ""
369
+ backend_defaults = "auto"
370
logical_block_size = 512 (512 B)
371
physical_block_size = 512 (512 B)
372
min_io_size = 0 (0 B)
373
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=144
374
dev: floppy, id ""
375
unit = 0 (0x0)
376
drive = ""
377
+ backend_defaults = "auto"
378
logical_block_size = 512 (512 B)
379
physical_block_size = 512 (512 B)
380
min_io_size = 0 (0 B)
381
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=288
382
dev: floppy, id ""
383
unit = 0 (0x0)
384
drive = ""
385
+ backend_defaults = "auto"
386
logical_block_size = 512 (512 B)
387
physical_block_size = 512 (512 B)
388
min_io_size = 0 (0 B)
389
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t
390
dev: floppy, id ""
391
unit = 0 (0x0)
392
drive = "none0"
393
+ backend_defaults = "auto"
394
logical_block_size = 512 (512 B)
395
physical_block_size = 512 (512 B)
396
min_io_size = 0 (0 B)
397
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t
398
dev: floppy, id ""
399
unit = 0 (0x0)
400
drive = "none0"
401
+ backend_defaults = "auto"
402
logical_block_size = 512 (512 B)
403
physical_block_size = 512 (512 B)
404
min_io_size = 0 (0 B)
405
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical
406
dev: floppy, id ""
407
unit = 0 (0x0)
408
drive = "none0"
409
+ backend_defaults = "auto"
410
logical_block_size = 512 (512 B)
411
physical_block_size = 512 (512 B)
412
min_io_size = 0 (0 B)
413
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physica
414
dev: floppy, id ""
415
unit = 0 (0x0)
416
drive = "none0"
417
+ backend_defaults = "auto"
418
logical_block_size = 512 (512 B)
419
physical_block_size = 512 (512 B)
420
min_io_size = 0 (0 B)
421
--
219
--
422
2.31.1
220
2.35.1
423
diff view generated by jsdifflib
1
It can be difficult to debug issues with BHs in production environments.
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
Although BHs can usually be identified by looking up their ->cb()
3
function pointer, this requires debug information for the program. It is
4
also not possible to print human-readable diagnostics about BHs because
5
they have no identifier.
6
2
7
This patch adds a name to each BH. The name is not unique per instance
3
The thread pool regulates itself: when idle, it kills threads until
8
but differentiates between cb() functions, which is usually enough. It's
4
empty, when in demand, it creates new threads until full. This behaviour
9
done by changing aio_bh_new() and friends to macros that stringify cb.
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.
10
9
11
The next patch will use the name field when reporting leaked BHs.
10
In order to mitigate this let's introduce a new 'EventLoopBase'
11
property to set the thread pool size. The threads will be created during
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
17
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Acked-by: Markus Armbruster <armbru@redhat.com>
20
Message-id: 20220425075723.20019-4-nsaenzju@redhat.com
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
15
Message-Id: <20210414200247.917496-2-stefanha@redhat.com>
16
---
22
---
17
include/block/aio.h | 31 ++++++++++++++++++++++++++++---
23
qapi/qom.json | 10 +++++-
18
include/qemu/main-loop.h | 4 +++-
24
include/block/aio.h | 10 ++++++
19
tests/unit/ptimer-test-stubs.c | 2 +-
25
include/block/thread-pool.h | 3 ++
20
util/async.c | 9 +++++++--
26
include/sysemu/event-loop-base.h | 4 +++
21
util/main-loop.c | 4 ++--
27
event-loop-base.c | 23 +++++++++++++
22
5 files changed, 41 insertions(+), 9 deletions(-)
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(-)
23
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:
24
diff --git a/include/block/aio.h b/include/block/aio.h
59
diff --git a/include/block/aio.h b/include/block/aio.h
25
index XXXXXXX..XXXXXXX 100644
60
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/aio.h
61
--- a/include/block/aio.h
27
+++ b/include/block/aio.h
62
+++ b/include/block/aio.h
28
@@ -XXX,XX +XXX,XX @@ void aio_context_acquire(AioContext *ctx);
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
29
/* Relinquish ownership of the AioContext. */
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
30
void aio_context_release(AioContext *ctx);
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);
31
75
32
+/**
76
+/**
33
+ * aio_bh_schedule_oneshot_full: Allocate a new bottom half structure that will
77
+ * aio_context_set_thread_pool_params:
34
+ * run only once and as soon as possible.
78
+ * @ctx: the aio context
35
+ *
79
+ * @min: min number of threads to have readily available in the thread pool
36
+ * @name: A human-readable identifier for debugging purposes.
80
+ * @min: max number of threads the thread pool can contain
37
+ */
81
+ */
38
+void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque,
82
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
39
+ const char *name);
83
+ int64_t max, Error **errp);
40
+
84
#endif
41
/**
85
diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h
42
* aio_bh_schedule_oneshot: Allocate a new bottom half structure that will run
86
index XXXXXXX..XXXXXXX 100644
43
* only once and as soon as possible.
87
--- a/include/block/thread-pool.h
44
+ *
88
+++ b/include/block/thread-pool.h
45
+ * A convenience wrapper for aio_bh_schedule_oneshot_full() that uses cb as the
89
@@ -XXX,XX +XXX,XX @@
46
+ * name string.
90
47
*/
91
#include "block/block.h"
48
-void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque);
92
49
+#define aio_bh_schedule_oneshot(ctx, cb, opaque) \
93
+#define THREAD_POOL_MAX_THREADS_DEFAULT 64
50
+ aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb)))
94
+
51
95
typedef int ThreadPoolFunc(void *opaque);
52
/**
96
53
- * aio_bh_new: Allocate a new bottom half structure.
97
typedef struct ThreadPool ThreadPool;
54
+ * aio_bh_new_full: Allocate a new bottom half structure.
98
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
55
*
99
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
56
* Bottom halves are lightweight callbacks whose invocation is guaranteed
100
ThreadPoolFunc *func, void *arg);
57
* to be wait-free, thread-safe and signal-safe. The #QEMUBH structure
101
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
58
* is opaque and must be allocated prior to its use.
102
+void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx);
59
+ *
103
60
+ * @name: A human-readable identifier for debugging purposes.
104
#endif
61
*/
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
62
-QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque);
106
index XXXXXXX..XXXXXXX 100644
63
+QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque,
107
--- a/include/sysemu/event-loop-base.h
64
+ const char *name);
108
+++ b/include/sysemu/event-loop-base.h
65
+
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBase {
66
+/**
110
67
+ * aio_bh_new: Allocate a new bottom half structure
111
/* AioContext AIO engine parameters */
68
+ *
112
int64_t aio_max_batch;
69
+ * A convenience wrapper for aio_bh_new_full() that uses the cb as the name
113
+
70
+ * string.
114
+ /* AioContext thread pool parameters */
71
+ */
115
+ int64_t thread_pool_min;
72
+#define aio_bh_new(ctx, cb, opaque) \
116
+ int64_t thread_pool_max;
73
+ aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb)))
117
};
74
118
#endif
75
/**
119
diff --git a/event-loop-base.c b/event-loop-base.c
76
* aio_notify: Force processing of pending events.
120
index XXXXXXX..XXXXXXX 100644
77
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
121
--- a/event-loop-base.c
78
index XXXXXXX..XXXXXXX 100644
122
+++ b/event-loop-base.c
79
--- a/include/qemu/main-loop.h
123
@@ -XXX,XX +XXX,XX @@
80
+++ b/include/qemu/main-loop.h
124
#include "qemu/osdep.h"
81
@@ -XXX,XX +XXX,XX @@ void qemu_cond_timedwait_iothread(QemuCond *cond, int ms);
125
#include "qom/object_interfaces.h"
82
126
#include "qapi/error.h"
83
void qemu_fd_register(int fd);
127
+#include "block/thread-pool.h"
84
128
#include "sysemu/event-loop-base.h"
85
-QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque);
129
86
+#define qemu_bh_new(cb, opaque) \
130
typedef struct {
87
+ qemu_bh_new_full((cb), (opaque), (stringify(cb)))
131
@@ -XXX,XX +XXX,XX @@ typedef struct {
88
+QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name);
132
ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
89
void qemu_bh_schedule_idle(QEMUBH *bh);
133
} EventLoopBaseParamInfo;
90
134
91
enum {
135
+static void event_loop_base_instance_init(Object *obj)
92
diff --git a/tests/unit/ptimer-test-stubs.c b/tests/unit/ptimer-test-stubs.c
136
+{
93
index XXXXXXX..XXXXXXX 100644
137
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
94
--- a/tests/unit/ptimer-test-stubs.c
138
+
95
+++ b/tests/unit/ptimer-test-stubs.c
139
+ base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
96
@@ -XXX,XX +XXX,XX @@ int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask)
140
+}
97
return deadline;
141
+
98
}
142
static EventLoopBaseParamInfo aio_max_batch_info = {
99
143
"aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
100
-QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
144
};
101
+QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name)
145
+static EventLoopBaseParamInfo thread_pool_min_info = {
102
{
146
+ "thread-pool-min", offsetof(EventLoopBase, thread_pool_min),
103
QEMUBH *bh = g_new(QEMUBH, 1);
147
+};
104
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"
105
diff --git a/util/async.c b/util/async.c
202
diff --git a/util/async.c b/util/async.c
106
index XXXXXXX..XXXXXXX 100644
203
index XXXXXXX..XXXXXXX 100644
107
--- a/util/async.c
204
--- a/util/async.c
108
+++ b/util/async.c
205
+++ b/util/async.c
109
@@ -XXX,XX +XXX,XX @@ enum {
206
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
110
207
111
struct QEMUBH {
208
ctx->aio_max_batch = 0;
112
AioContext *ctx;
209
113
+ const char *name;
210
+ ctx->thread_pool_min = 0;
114
QEMUBHFunc *cb;
211
+ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
115
void *opaque;
212
+
116
QSLIST_ENTRY(QEMUBH) next;
213
return ctx;
117
@@ -XXX,XX +XXX,XX @@ static QEMUBH *aio_bh_dequeue(BHList *head, unsigned *flags)
214
fail:
118
return bh;
215
g_source_destroy(&ctx->source);
119
}
216
@@ -XXX,XX +XXX,XX @@ void qemu_set_current_aio_context(AioContext *ctx)
120
217
assert(!get_my_aiocontext());
121
-void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
218
set_my_aiocontext(ctx);
122
+void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb,
219
}
123
+ void *opaque, const char *name)
220
+
124
{
221
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
125
QEMUBH *bh;
222
+ int64_t max, Error **errp)
126
bh = g_new(QEMUBH, 1);
223
+{
127
@@ -XXX,XX +XXX,XX @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
224
+
128
.ctx = ctx,
225
+ if (min > max || !max || min > INT_MAX || max > INT_MAX) {
129
.cb = cb,
226
+ error_setg(errp, "bad thread-pool-min/thread-pool-max values");
130
.opaque = opaque,
227
+ return;
131
+ .name = name,
228
+ }
132
};
229
+
133
aio_bh_enqueue(bh, BH_SCHEDULED | BH_ONESHOT);
230
+ ctx->thread_pool_min = min;
134
}
231
+ ctx->thread_pool_max = max;
135
232
+
136
-QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
233
+ if (ctx->thread_pool) {
137
+QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque,
234
+ thread_pool_update_params(ctx->thread_pool, ctx);
138
+ const char *name)
235
+ }
139
{
236
+}
140
QEMUBH *bh;
141
bh = g_new(QEMUBH, 1);
142
@@ -XXX,XX +XXX,XX @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
143
.ctx = ctx,
144
.cb = cb,
145
.opaque = opaque,
146
+ .name = name,
147
};
148
return bh;
149
}
150
diff --git a/util/main-loop.c b/util/main-loop.c
237
diff --git a/util/main-loop.c b/util/main-loop.c
151
index XXXXXXX..XXXXXXX 100644
238
index XXXXXXX..XXXXXXX 100644
152
--- a/util/main-loop.c
239
--- a/util/main-loop.c
153
+++ b/util/main-loop.c
240
+++ b/util/main-loop.c
154
@@ -XXX,XX +XXX,XX @@ void main_loop_wait(int nonblocking)
241
@@ -XXX,XX +XXX,XX @@
155
242
#include "sysemu/replay.h"
156
/* Functions to operate on the main QEMU AioContext. */
243
#include "qemu/main-loop.h"
157
244
#include "block/aio.h"
158
-QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
245
+#include "block/thread-pool.h"
159
+QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name)
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)
160
{
252
{
161
- return aio_bh_new(qemu_aio_context, cb, opaque);
253
+ ERRP_GUARD();
162
+ return aio_bh_new_full(qemu_aio_context, cb, opaque, name);
254
+
163
}
255
if (!qemu_aio_context) {
164
256
error_setg(errp, "qemu aio context not ready");
165
/*
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
+{
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)
166
--
370
--
167
2.31.1
371
2.35.1
168
diff view generated by jsdifflib
Deleted patch
1
BHs must be deleted before the AioContext is finalized. If not, it's a
2
bug and probably indicates that some part of the program still expects
3
the BH to run in the future. That can lead to memory leaks, inconsistent
4
state, or just hangs.
5
1
6
Unfortunately the assert(flags & BH_DELETED) call in aio_ctx_finalize()
7
is difficult to debug because the assertion failure contains no
8
information about the BH!
9
10
Use the QEMUBH name field added in the previous patch to show a useful
11
error when a leaked BH is detected.
12
13
Suggested-by: Eric Ernst <eric.g.ernst@gmail.com>
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Message-Id: <20210414200247.917496-3-stefanha@redhat.com>
16
---
17
util/async.c | 16 ++++++++++++++--
18
1 file changed, 14 insertions(+), 2 deletions(-)
19
20
diff --git a/util/async.c b/util/async.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/util/async.c
23
+++ b/util/async.c
24
@@ -XXX,XX +XXX,XX @@ aio_ctx_finalize(GSource *source)
25
assert(QSIMPLEQ_EMPTY(&ctx->bh_slice_list));
26
27
while ((bh = aio_bh_dequeue(&ctx->bh_list, &flags))) {
28
- /* qemu_bh_delete() must have been called on BHs in this AioContext */
29
- assert(flags & BH_DELETED);
30
+ /*
31
+ * qemu_bh_delete() must have been called on BHs in this AioContext. In
32
+ * many cases memory leaks, hangs, or inconsistent state occur when a
33
+ * BH is leaked because something still expects it to run.
34
+ *
35
+ * If you hit this, fix the lifecycle of the BH so that
36
+ * qemu_bh_delete() and any associated cleanup is called before the
37
+ * AioContext is finalized.
38
+ */
39
+ if (unlikely(!(flags & BH_DELETED))) {
40
+ fprintf(stderr, "%s: BH '%s' leaked, aborting...\n",
41
+ __func__, bh->name);
42
+ abort();
43
+ }
44
45
g_free(bh);
46
}
47
--
48
2.31.1
49
diff view generated by jsdifflib
Deleted patch
1
From: Akihiko Odaki <akihiko.odaki@gmail.com>
2
1
3
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
4
Message-id: 20210705130458.97642-3-akihiko.odaki@gmail.com
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
6
---
7
block/io.c | 2 ++
8
1 file changed, 2 insertions(+)
9
10
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/block/io.c
13
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
15
16
static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
17
{
18
+ dst->pdiscard_alignment = MAX(dst->pdiscard_alignment,
19
+ src->pdiscard_alignment);
20
dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer);
21
dst->max_transfer = MIN_NON_ZERO(dst->max_transfer, src->max_transfer);
22
dst->max_hw_transfer = MIN_NON_ZERO(dst->max_hw_transfer,
23
--
24
2.31.1
25
diff view generated by jsdifflib