1
The following changes since commit 00928a421d47f49691cace1207481b7aad31b1f1:
1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
2
2
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180626' into staging (2018-06-26 18:23:49 +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
git://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 ed6e2161715c527330f936d44af4c547f25f687e:
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
10
10
11
linux-aio: properly bubble up errors from initialization (2018-06-27 13:06:34 +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
* Gracefully handle Linux AIO init failure
16
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
17
used for async I/O.
17
18
18
----------------------------------------------------------------
19
----------------------------------------------------------------
19
20
20
Nishanth Aravamudan (1):
21
Nicolas Saenz Julienne (3):
21
linux-aio: properly bubble up errors from initialization
22
Introduce event-loop-base abstract class
23
util/main-loop: Introduce the main loop into QOM
24
util/event-loop-base: Introduce options to set the thread pool size
22
25
23
Stefan Hajnoczi (1):
26
qapi/qom.json | 43 ++++++++--
24
compiler: add a sizeof_field() macro
27
meson.build | 26 +++---
25
28
include/block/aio.h | 10 +++
26
include/block/aio.h | 3 +++
29
include/block/thread-pool.h | 3 +
27
include/block/raw-aio.h | 2 +-
30
include/qemu/main-loop.h | 10 +++
28
include/hw/xen/io/ring.h | 2 +-
31
include/sysemu/event-loop-base.h | 41 +++++++++
29
include/qemu/compiler.h | 2 ++
32
include/sysemu/iothread.h | 6 +-
30
accel/tcg/translate-all.c | 2 +-
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
31
block/file-posix.c | 33 ++++++++++++++++++++++++++++-----
34
iothread.c | 68 +++++----------
32
block/linux-aio.c | 12 +++++++++---
35
util/aio-posix.c | 1 +
33
hw/display/xenfb.c | 4 ++--
36
util/async.c | 20 +++++
34
hw/net/rocker/rocker_of_dpa.c | 2 +-
37
util/main-loop.c | 65 ++++++++++++++
35
hw/net/virtio-net.c | 2 +-
38
util/thread-pool.c | 55 +++++++++++-
36
stubs/linux-aio.c | 2 +-
39
13 files changed, 419 insertions(+), 69 deletions(-)
37
target/i386/kvm.c | 2 +-
40
create mode 100644 include/sysemu/event-loop-base.h
38
target/ppc/arch_dump.c | 10 +++++-----
41
create mode 100644 event-loop-base.c
39
target/s390x/arch_dump.c | 20 ++++++++++----------
40
util/async.c | 14 +++++++++++---
41
15 files changed, 77 insertions(+), 35 deletions(-)
42
42
43
--
43
--
44
2.17.1
44
2.35.1
45
46
diff view generated by jsdifflib
1
Determining the size of a field is useful when you don't have a struct
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
variable handy. Open-coding this is ugly.
2
3
3
Introduce the 'event-loop-base' abstract class, it'll hold the
4
This patch adds the sizeof_field() macro, which is similar to
4
properties common to all event loops and provide the necessary hooks for
5
typeof_field(). Existing instances are updated to use the macro.
5
their creation and maintenance. Then have iothread inherit from it.
6
6
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
7
EventLoopBaseClass is defined as user creatable and provides a hook for
8
Reviewed-by: John Snow <jsnow@redhat.com>
8
its children to attach themselves to the user creatable class 'complete'
9
Message-id: 20180614164431.29305-1-stefanha@redhat.com
9
function. It also provides an update_params() callback to propagate
10
property changes onto its children.
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
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
39
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
40
---
12
include/hw/xen/io/ring.h | 2 +-
41
qapi/qom.json | 22 +++++--
13
include/qemu/compiler.h | 2 ++
42
meson.build | 23 ++++---
14
accel/tcg/translate-all.c | 2 +-
43
include/sysemu/event-loop-base.h | 36 +++++++++++
15
hw/display/xenfb.c | 4 ++--
44
include/sysemu/iothread.h | 6 +-
16
hw/net/rocker/rocker_of_dpa.c | 2 +-
45
event-loop-base.c | 104 +++++++++++++++++++++++++++++++
17
hw/net/virtio-net.c | 2 +-
46
iothread.c | 65 ++++++-------------
18
target/i386/kvm.c | 2 +-
47
6 files changed, 192 insertions(+), 64 deletions(-)
19
target/ppc/arch_dump.c | 10 +++++-----
48
create mode 100644 include/sysemu/event-loop-base.h
20
target/s390x/arch_dump.c | 20 ++++++++++----------
49
create mode 100644 event-loop-base.c
21
9 files changed, 24 insertions(+), 22 deletions(-)
50
22
51
diff --git a/qapi/qom.json b/qapi/qom.json
23
diff --git a/include/hw/xen/io/ring.h b/include/hw/xen/io/ring.h
24
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
25
--- a/include/hw/xen/io/ring.h
53
--- a/qapi/qom.json
26
+++ b/include/hw/xen/io/ring.h
54
+++ b/qapi/qom.json
27
@@ -XXX,XX +XXX,XX @@ typedef unsigned int RING_IDX;
55
@@ -XXX,XX +XXX,XX @@
28
*/
56
'*repeat': 'bool',
29
#define __CONST_RING_SIZE(_s, _sz) \
57
'*grab-toggle': 'GrabToggleKeys' } }
30
(__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \
58
31
-     sizeof(((struct _s##_sring *)0)->ring[0])))
59
+##
32
+ sizeof_field(struct _s##_sring, ring[0])))
60
+# @EventLoopBaseProperties:
33
/*
61
+#
34
* The same for passing in an actual pointer instead of a name tag.
62
+# Common properties for event loops
35
*/
63
+#
36
diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h
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
37
index XXXXXXX..XXXXXXX 100644
98
index XXXXXXX..XXXXXXX 100644
38
--- a/include/qemu/compiler.h
99
--- a/meson.build
39
+++ b/include/qemu/compiler.h
100
+++ b/meson.build
40
@@ -XXX,XX +XXX,XX @@
101
@@ -XXX,XX +XXX,XX @@ subdir('qom')
41
(type *) ((char *) __mptr - offsetof(type, member));})
102
subdir('authz')
42
#endif
103
subdir('crypto')
43
104
subdir('ui')
44
+#define sizeof_field(type, field) sizeof(((type *)0)->field)
105
+subdir('hw')
45
+
106
46
/* Convert from a base type to a parent type, with compile time checking. */
107
47
#ifdef __GNUC__
108
if enable_modules
48
#define DO_UPCAST(type, field, dev) ( __extension__ ( { \
109
@@ -XXX,XX +XXX,XX @@ if enable_modules
49
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
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
50
index XXXXXXX..XXXXXXX 100644
202
index XXXXXXX..XXXXXXX 100644
51
--- a/accel/tcg/translate-all.c
203
--- a/include/sysemu/iothread.h
52
+++ b/accel/tcg/translate-all.c
204
+++ b/include/sysemu/iothread.h
53
@@ -XXX,XX +XXX,XX @@ struct page_collection {
205
@@ -XXX,XX +XXX,XX @@
54
206
#include "block/aio.h"
55
/* Make sure all possible CPU event bits fit in tb->trace_vcpu_dstate */
207
#include "qemu/thread.h"
56
QEMU_BUILD_BUG_ON(CPU_TRACE_DSTATE_MAX_EVENTS >
208
#include "qom/object.h"
57
- sizeof(((TranslationBlock *)0)->trace_vcpu_dstate)
209
+#include "sysemu/event-loop-base.h"
58
+ sizeof_field(TranslationBlock, trace_vcpu_dstate)
210
59
* BITS_PER_BYTE);
211
#define TYPE_IOTHREAD "iothread"
60
212
61
/*
213
struct IOThread {
62
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
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
63
index XXXXXXX..XXXXXXX 100644
340
index XXXXXXX..XXXXXXX 100644
64
--- a/hw/display/xenfb.c
341
--- a/iothread.c
65
+++ b/hw/display/xenfb.c
342
+++ b/iothread.c
66
@@ -XXX,XX +XXX,XX @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim,
343
@@ -XXX,XX +XXX,XX @@
67
int width, int height, int depth,
344
#include "qemu/module.h"
68
size_t fb_len, int offset, int row_stride)
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)
69
{
357
{
70
- size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd);
358
+ IOThread *iothread = IOTHREAD(base);
71
- size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz;
359
ERRP_GUARD();
72
+ size_t mfn_sz = sizeof_field(struct xenfb_page, pd[0]);
360
73
+ size_t pd_len = sizeof_field(struct xenfb_page, pd) / mfn_sz;
361
+ if (!iothread->ctx) {
74
size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
362
+ return;
75
size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
363
+ }
76
int max_width, max_height;
364
+
77
diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c
365
aio_context_set_poll_params(iothread->ctx,
78
index XXXXXXX..XXXXXXX 100644
366
iothread->poll_max_ns,
79
--- a/hw/net/rocker/rocker_of_dpa.c
367
iothread->poll_grow,
80
+++ b/hw/net/rocker/rocker_of_dpa.c
368
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
81
@@ -XXX,XX +XXX,XX @@ typedef struct of_dpa_flow_key {
369
}
82
370
83
/* Width of key which includes field 'f' in u64s, rounded up */
371
aio_context_set_aio_params(iothread->ctx,
84
#define FLOW_KEY_WIDTH(f) \
372
- iothread->aio_max_batch,
85
- DIV_ROUND_UP(offsetof(OfDpaFlowKey, f) + sizeof(((OfDpaFlowKey *)0)->f), \
373
+ iothread->parent_obj.aio_max_batch,
86
+ DIV_ROUND_UP(offsetof(OfDpaFlowKey, f) + sizeof_field(OfDpaFlowKey, f), \
374
errp);
87
sizeof(uint64_t))
375
}
88
376
89
typedef struct of_dpa_flow_action {
377
-static void iothread_complete(UserCreatable *obj, Error **errp)
90
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
378
+
91
index XXXXXXX..XXXXXXX 100644
379
+static void iothread_init(EventLoopBase *base, Error **errp)
92
--- a/hw/net/virtio-net.c
380
{
93
+++ b/hw/net/virtio-net.c
381
Error *local_error = NULL;
94
@@ -XXX,XX +XXX,XX @@
382
- IOThread *iothread = IOTHREAD(obj);
95
* 'container'.
383
+ IOThread *iothread = IOTHREAD(base);
96
*/
384
char *thread_name;
97
#define endof(container, field) \
385
98
- (offsetof(container, field) + sizeof(((container *)0)->field))
386
iothread->stopping = false;
99
+ (offsetof(container, field) + sizeof_field(container, field))
387
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
100
388
*/
101
typedef struct VirtIOFeature {
389
iothread_init_gcontext(iothread);
102
uint64_t flags;
390
103
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
391
- iothread_set_aio_context_params(iothread, &local_error);
104
index XXXXXXX..XXXXXXX 100644
392
+ iothread_set_aio_context_params(base, &local_error);
105
--- a/target/i386/kvm.c
393
if (local_error) {
106
+++ b/target/i386/kvm.c
394
error_propagate(errp, local_error);
107
@@ -XXX,XX +XXX,XX @@ static int kvm_put_fpu(X86CPU *cpu)
395
aio_context_unref(iothread->ctx);
108
#define XSAVE_PKRU 672
396
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
109
397
* to inherit.
110
#define XSAVE_BYTE_OFFSET(word_offset) \
398
*/
111
- ((word_offset) * sizeof(((struct kvm_xsave *)0)->region[0]))
399
thread_name = g_strdup_printf("IO %s",
112
+ ((word_offset) * sizeof_field(struct kvm_xsave, region[0]))
400
- object_get_canonical_path_component(OBJECT(obj)));
113
401
+ object_get_canonical_path_component(OBJECT(base)));
114
#define ASSERT_OFFSET(word_offset, field) \
402
qemu_thread_create(&iothread->thread, thread_name, iothread_run,
115
QEMU_BUILD_BUG_ON(XSAVE_BYTE_OFFSET(word_offset) != \
403
iothread, QEMU_THREAD_JOINABLE);
116
diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c
404
g_free(thread_name);
117
index XXXXXXX..XXXXXXX 100644
405
@@ -XXX,XX +XXX,XX @@ static IOThreadParamInfo poll_grow_info = {
118
--- a/target/ppc/arch_dump.c
406
static IOThreadParamInfo poll_shrink_info = {
119
+++ b/target/ppc/arch_dump.c
407
"poll-shrink", offsetof(IOThread, poll_shrink),
120
@@ -XXX,XX +XXX,XX @@ static const struct NoteFuncDescStruct {
121
int contents_size;
122
void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu);
123
} note_func[] = {
124
- {sizeof(((Note *)0)->contents.prstatus), ppc_write_elf_prstatus},
125
- {sizeof(((Note *)0)->contents.fpregset), ppc_write_elf_fpregset},
126
- {sizeof(((Note *)0)->contents.vmxregset), ppc_write_elf_vmxregset},
127
- {sizeof(((Note *)0)->contents.vsxregset), ppc_write_elf_vsxregset},
128
- {sizeof(((Note *)0)->contents.speregset), ppc_write_elf_speregset},
129
+ {sizeof_field(Note, contents.prstatus), ppc_write_elf_prstatus},
130
+ {sizeof_field(Note, contents.fpregset), ppc_write_elf_fpregset},
131
+ {sizeof_field(Note, contents.vmxregset), ppc_write_elf_vmxregset},
132
+ {sizeof_field(Note, contents.vsxregset), ppc_write_elf_vsxregset},
133
+ {sizeof_field(Note, contents.speregset), ppc_write_elf_speregset},
134
{ 0, NULL}
135
};
408
};
136
409
-static IOThreadParamInfo aio_max_batch_info = {
137
diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c
410
- "aio-max-batch", offsetof(IOThread, aio_max_batch),
138
index XXXXXXX..XXXXXXX 100644
411
-};
139
--- a/target/s390x/arch_dump.c
412
140
+++ b/target/s390x/arch_dump.c
413
static void iothread_get_param(Object *obj, Visitor *v,
141
@@ -XXX,XX +XXX,XX @@ typedef struct NoteFuncDescStruct {
414
const char *name, IOThreadParamInfo *info, Error **errp)
142
} NoteFuncDesc;
415
@@ -XXX,XX +XXX,XX @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
143
416
}
144
static const NoteFuncDesc note_core[] = {
417
}
145
- {sizeof(((Note *)0)->contents.prstatus), s390x_write_elf64_prstatus},
418
146
- {sizeof(((Note *)0)->contents.fpregset), s390x_write_elf64_fpregset},
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
147
+ {sizeof_field(Note, contents.prstatus), s390x_write_elf64_prstatus},
420
- const char *name, void *opaque, Error **errp)
148
+ {sizeof_field(Note, contents.fpregset), s390x_write_elf64_fpregset},
421
-{
149
{ 0, NULL}
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)
445
{
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
447
- ucc->complete = iothread_complete;
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
449
+
450
+ bc->init = iothread_init;
451
+ bc->update_params = iothread_set_aio_context_params;
452
453
object_class_property_add(klass, "poll-max-ns", "int",
454
iothread_get_poll_param,
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);
463
}
464
465
static const TypeInfo iothread_info = {
466
.name = TYPE_IOTHREAD,
467
- .parent = TYPE_OBJECT,
468
+ .parent = TYPE_EVENT_LOOP_BASE,
469
.class_init = iothread_class_init,
470
.instance_size = sizeof(IOThread),
471
.instance_init = iothread_instance_init,
472
.instance_finalize = iothread_instance_finalize,
473
- .interfaces = (InterfaceInfo[]) {
474
- {TYPE_USER_CREATABLE},
475
- {}
476
- },
150
};
477
};
151
478
152
static const NoteFuncDesc note_linux[] = {
479
static void iothread_register_types(void)
153
- {sizeof(((Note *)0)->contents.prefix), s390x_write_elf64_prefix},
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
154
- {sizeof(((Note *)0)->contents.ctrs), s390x_write_elf64_ctrs},
481
info->poll_max_ns = iothread->poll_max_ns;
155
- {sizeof(((Note *)0)->contents.timer), s390x_write_elf64_timer},
482
info->poll_grow = iothread->poll_grow;
156
- {sizeof(((Note *)0)->contents.todcmp), s390x_write_elf64_todcmp},
483
info->poll_shrink = iothread->poll_shrink;
157
- {sizeof(((Note *)0)->contents.todpreg), s390x_write_elf64_todpreg},
484
- info->aio_max_batch = iothread->aio_max_batch;
158
- {sizeof(((Note *)0)->contents.vregslo), s390x_write_elf64_vregslo},
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
159
- {sizeof(((Note *)0)->contents.vregshi), s390x_write_elf64_vregshi},
486
160
- {sizeof(((Note *)0)->contents.gscb), s390x_write_elf64_gscb},
487
QAPI_LIST_APPEND(*tail, info);
161
+ {sizeof_field(Note, contents.prefix), s390x_write_elf64_prefix},
488
return 0;
162
+ {sizeof_field(Note, contents.ctrs), s390x_write_elf64_ctrs},
163
+ {sizeof_field(Note, contents.timer), s390x_write_elf64_timer},
164
+ {sizeof_field(Note, contents.todcmp), s390x_write_elf64_todcmp},
165
+ {sizeof_field(Note, contents.todpreg), s390x_write_elf64_todpreg},
166
+ {sizeof_field(Note, contents.vregslo), s390x_write_elf64_vregslo},
167
+ {sizeof_field(Note, contents.vregshi), s390x_write_elf64_vregshi},
168
+ {sizeof_field(Note, contents.gscb), s390x_write_elf64_gscb},
169
{ 0, NULL}
170
};
171
172
--
489
--
173
2.17.1
490
2.35.1
174
175
diff view generated by jsdifflib
New patch
1
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
3
'event-loop-base' provides basic property handling for all 'AioContext'
4
based event loops. So let's define a new 'MainLoopClass' that inherits
5
from it. This will permit tweaking the main loop's properties through
6
qapi as well as through the command line using the '-object' keyword[1].
7
Only one instance of 'MainLoopClass' might be created at any time.
8
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
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
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
20
---
21
qapi/qom.json | 13 ++++++++
22
meson.build | 3 +-
23
include/qemu/main-loop.h | 10 ++++++
24
include/sysemu/event-loop-base.h | 1 +
25
event-loop-base.c | 13 ++++++++
26
util/main-loop.c | 56 ++++++++++++++++++++++++++++++++
27
6 files changed, 95 insertions(+), 1 deletion(-)
28
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
}
123
}
124
125
+static bool event_loop_base_can_be_deleted(UserCreatable *uc)
126
+{
127
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
128
+ EventLoopBase *backend = EVENT_LOOP_BASE(uc);
129
+
130
+ if (bc->can_be_deleted) {
131
+ return bc->can_be_deleted(backend);
132
+ }
133
+
134
+ return true;
135
+}
136
+
137
static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
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;
159
}
160
161
+static void main_loop_update_params(EventLoopBase *base, Error **errp)
162
+{
163
+ if (!qemu_aio_context) {
164
+ error_setg(errp, "qemu aio context not ready");
165
+ return;
166
+ }
167
+
168
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
169
+}
170
+
171
+MainLoop *mloop;
172
+
173
+static void main_loop_init(EventLoopBase *base, Error **errp)
174
+{
175
+ MainLoop *m = MAIN_LOOP(base);
176
+
177
+ if (mloop) {
178
+ error_setg(errp, "only one main-loop instance allowed");
179
+ return;
180
+ }
181
+
182
+ main_loop_update_params(base, errp);
183
+
184
+ mloop = m;
185
+ return;
186
+}
187
+
188
+static bool main_loop_can_be_deleted(EventLoopBase *base)
189
+{
190
+ return false;
191
+}
192
+
193
+static void main_loop_class_init(ObjectClass *oc, void *class_data)
194
+{
195
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc);
196
+
197
+ bc->init = main_loop_init;
198
+ bc->update_params = main_loop_update_params;
199
+ bc->can_be_deleted = main_loop_can_be_deleted;
200
+}
201
+
202
+static const TypeInfo main_loop_info = {
203
+ .name = TYPE_MAIN_LOOP,
204
+ .parent = TYPE_EVENT_LOOP_BASE,
205
+ .class_init = main_loop_class_init,
206
+ .instance_size = sizeof(MainLoop),
207
+};
208
+
209
+static void main_loop_register_types(void)
210
+{
211
+ type_register_static(&main_loop_info);
212
+}
213
+
214
+type_init(main_loop_register_types)
215
+
216
static int max_priority;
217
218
#ifndef _WIN32
219
--
220
2.35.1
diff view generated by jsdifflib
1
From: Nishanth Aravamudan <naravamudan@digitalocean.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
laio_init() can fail for a couple of reasons, which will lead to a NULL
3
The thread pool regulates itself: when idle, it kills threads until
4
pointer dereference in laio_attach_aio_context().
4
empty, when in demand, it creates new threads until full. This behaviour
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.
5
9
6
To solve this, add a aio_setup_linux_aio() function which is called
10
In order to mitigate this let's introduce a new 'EventLoopBase'
7
early in raw_open_common. If this fails, propagate the error up. The
11
property to set the thread pool size. The threads will be created during
8
signature of aio_get_linux_aio() was not modified, because it seems
12
the pool's initialization or upon updating the property's value, remain
9
preferable to return the actual errno from the possible failing
13
available during its lifetime regardless of demand, and destroyed upon
10
initialization calls.
14
freeing it. A properly characterized workload will then be able to
15
configure the pool to avoid any latency spikes.
11
16
12
Additionally, when the AioContext changes, we need to associate a
17
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
13
LinuxAioState with the new AioContext. Use the bdrv_attach_aio_context
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
callback and call the new aio_setup_linux_aio(), which will allocate a
19
Acked-by: Markus Armbruster <armbru@redhat.com>
15
new AioContext if needed, and return errors on failures. If it fails for
20
Message-id: 20220425075723.20019-4-nsaenzju@redhat.com
16
any reason, fallback to threaded AIO with an error message, as the
17
device is already in-use by the guest.
18
19
Add an assert that aio_get_linux_aio() cannot return NULL.
20
21
Signed-off-by: Nishanth Aravamudan <naravamudan@digitalocean.com>
22
Message-id: 20180622193700.6523-1-naravamudan@digitalocean.com
23
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
24
---
22
---
25
include/block/aio.h | 3 +++
23
qapi/qom.json | 10 +++++-
26
include/block/raw-aio.h | 2 +-
24
include/block/aio.h | 10 ++++++
27
block/file-posix.c | 33 ++++++++++++++++++++++++++++-----
25
include/block/thread-pool.h | 3 ++
28
block/linux-aio.c | 12 +++++++++---
26
include/sysemu/event-loop-base.h | 4 +++
29
stubs/linux-aio.c | 2 +-
27
event-loop-base.c | 23 +++++++++++++
30
util/async.c | 14 +++++++++++---
28
iothread.c | 3 ++
31
6 files changed, 53 insertions(+), 13 deletions(-)
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(-)
32
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:
33
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
34
index XXXXXXX..XXXXXXX 100644
60
index XXXXXXX..XXXXXXX 100644
35
--- a/include/block/aio.h
61
--- a/include/block/aio.h
36
+++ b/include/block/aio.h
62
+++ b/include/block/aio.h
37
@@ -XXX,XX +XXX,XX @@ GSource *aio_get_g_source(AioContext *ctx);
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
38
/* Return the ThreadPool bound to this AioContext */
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
39
struct ThreadPool *aio_get_thread_pool(AioContext *ctx);
65
QEMUBH *co_schedule_bh;
40
66
41
+/* Setup the LinuxAioState bound to this AioContext */
67
+ int thread_pool_min;
42
+struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp);
68
+ int thread_pool_max;
43
+
69
/* Thread pool for performing work and receiving completion callbacks.
44
/* Return the LinuxAioState bound to this AioContext */
70
* Has its own locking.
45
struct LinuxAioState *aio_get_linux_aio(AioContext *ctx);
71
*/
46
72
@@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
47
diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h
73
void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
48
index XXXXXXX..XXXXXXX 100644
74
Error **errp);
49
--- a/include/block/raw-aio.h
75
50
+++ b/include/block/raw-aio.h
76
+/**
51
@@ -XXX,XX +XXX,XX @@
77
+ * aio_context_set_thread_pool_params:
52
/* linux-aio.c - Linux native implementation */
78
+ * @ctx: the aio context
53
#ifdef CONFIG_LINUX_AIO
79
+ * @min: min number of threads to have readily available in the thread pool
54
typedef struct LinuxAioState LinuxAioState;
80
+ * @min: max number of threads the thread pool can contain
55
-LinuxAioState *laio_init(void);
81
+ */
56
+LinuxAioState *laio_init(Error **errp);
82
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
57
void laio_cleanup(LinuxAioState *s);
83
+ int64_t max, Error **errp);
58
int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
84
#endif
59
uint64_t offset, QEMUIOVector *qiov, int type);
85
diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h
60
diff --git a/block/file-posix.c b/block/file-posix.c
86
index XXXXXXX..XXXXXXX 100644
61
index XXXXXXX..XXXXXXX 100644
87
--- a/include/block/thread-pool.h
62
--- a/block/file-posix.c
88
+++ b/include/block/thread-pool.h
63
+++ b/block/file-posix.c
89
@@ -XXX,XX +XXX,XX @@
64
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
90
65
91
#include "block/block.h"
66
#ifdef CONFIG_LINUX_AIO
92
67
/* Currently Linux does AIO only for files opened with O_DIRECT */
93
+#define THREAD_POOL_MAX_THREADS_DEFAULT 64
68
- if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) {
94
+
69
- error_setg(errp, "aio=native was specified, but it requires "
95
typedef int ThreadPoolFunc(void *opaque);
70
- "cache.direct=on, which was not specified.");
96
71
- ret = -EINVAL;
97
typedef struct ThreadPool ThreadPool;
72
- goto fail;
98
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
73
+ if (s->use_linux_aio) {
99
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
74
+ if (!(s->open_flags & O_DIRECT)) {
100
ThreadPoolFunc *func, void *arg);
75
+ error_setg(errp, "aio=native was specified, but it requires "
101
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
76
+ "cache.direct=on, which was not specified.");
102
+void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx);
77
+ ret = -EINVAL;
103
78
+ goto fail;
104
#endif
79
+ }
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
80
+ if (!aio_setup_linux_aio(bdrv_get_aio_context(bs), errp)) {
106
index XXXXXXX..XXXXXXX 100644
81
+ error_prepend(errp, "Unable to use native AIO: ");
107
--- a/include/sysemu/event-loop-base.h
82
+ goto fail;
108
+++ b/include/sysemu/event-loop-base.h
83
+ }
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBase {
84
}
110
85
#else
111
/* AioContext AIO engine parameters */
86
if (s->use_linux_aio) {
112
int64_t aio_max_batch;
87
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *raw_aio_flush(BlockDriverState *bs,
113
+
88
return paio_submit(bs, s->fd, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH);
114
+ /* AioContext thread pool parameters */
89
}
115
+ int64_t thread_pool_min;
90
116
+ int64_t thread_pool_max;
91
+static void raw_aio_attach_aio_context(BlockDriverState *bs,
117
};
92
+ AioContext *new_context)
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)
93
+{
136
+{
94
+#ifdef CONFIG_LINUX_AIO
137
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
95
+ BDRVRawState *s = bs->opaque;
138
+
96
+ if (s->use_linux_aio) {
139
+ base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
97
+ Error *local_err;
98
+ if (!aio_setup_linux_aio(new_context, &local_err)) {
99
+ error_reportf_err(local_err, "Unable to use native AIO, "
100
+ "falling back to thread pool: ");
101
+ s->use_linux_aio = false;
102
+ }
103
+ }
104
+#endif
105
+}
140
+}
106
+
141
+
107
static void raw_close(BlockDriverState *bs)
142
static EventLoopBaseParamInfo aio_max_batch_info = {
108
{
143
"aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
109
BDRVRawState *s = bs->opaque;
144
};
110
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
145
+static EventLoopBaseParamInfo thread_pool_min_info = {
111
.bdrv_refresh_limits = raw_refresh_limits,
146
+ "thread-pool-min", offsetof(EventLoopBase, thread_pool_min),
112
.bdrv_io_plug = raw_aio_plug,
147
+};
113
.bdrv_io_unplug = raw_aio_unplug,
148
+static EventLoopBaseParamInfo thread_pool_max_info = {
114
+ .bdrv_attach_aio_context = raw_aio_attach_aio_context,
149
+ "thread-pool-max", offsetof(EventLoopBase, thread_pool_max),
115
150
+};
116
.bdrv_truncate = raw_truncate,
151
117
.bdrv_getlength = raw_getlength,
152
static void event_loop_base_get_param(Object *obj, Visitor *v,
118
diff --git a/block/linux-aio.c b/block/linux-aio.c
153
const char *name, void *opaque, Error **errp)
119
index XXXXXXX..XXXXXXX 100644
154
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
120
--- a/block/linux-aio.c
155
event_loop_base_get_param,
121
+++ b/block/linux-aio.c
156
event_loop_base_set_param,
122
@@ -XXX,XX +XXX,XX @@
157
NULL, &aio_max_batch_info);
123
#include "block/raw-aio.h"
158
+ object_class_property_add(klass, "thread-pool-min", "int",
124
#include "qemu/event_notifier.h"
159
+ event_loop_base_get_param,
125
#include "qemu/coroutine.h"
160
+ event_loop_base_set_param,
126
+#include "qapi/error.h"
161
+ NULL, &thread_pool_min_info);
127
162
+ object_class_property_add(klass, "thread-pool-max", "int",
128
#include <libaio.h>
163
+ event_loop_base_get_param,
129
164
+ event_loop_base_set_param,
130
@@ -XXX,XX +XXX,XX @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
165
+ NULL, &thread_pool_max_info);
131
qemu_laio_poll_cb);
166
}
132
}
167
133
168
static const TypeInfo event_loop_base_info = {
134
-LinuxAioState *laio_init(void)
169
.name = TYPE_EVENT_LOOP_BASE,
135
+LinuxAioState *laio_init(Error **errp)
170
.parent = TYPE_OBJECT,
136
{
171
.instance_size = sizeof(EventLoopBase),
137
+ int rc;
172
+ .instance_init = event_loop_base_instance_init,
138
LinuxAioState *s;
173
.class_size = sizeof(EventLoopBaseClass),
139
174
.class_init = event_loop_base_class_init,
140
s = g_malloc0(sizeof(*s));
175
.abstract = true,
141
- if (event_notifier_init(&s->e, false) < 0) {
176
diff --git a/iothread.c b/iothread.c
142
+ rc = event_notifier_init(&s->e, false);
177
index XXXXXXX..XXXXXXX 100644
143
+ if (rc < 0) {
178
--- a/iothread.c
144
+ error_setg_errno(errp, -rc, "failed to to initialize event notifier");
179
+++ b/iothread.c
145
goto out_free_state;
180
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
146
}
181
aio_context_set_aio_params(iothread->ctx,
147
182
iothread->parent_obj.aio_max_batch,
148
- if (io_setup(MAX_EVENTS, &s->ctx) != 0) {
183
errp);
149
+ rc = io_setup(MAX_EVENTS, &s->ctx);
184
+
150
+ if (rc < 0) {
185
+ aio_context_set_thread_pool_params(iothread->ctx, base->thread_pool_min,
151
+ error_setg_errno(errp, -rc, "failed to create linux AIO context");
186
+ base->thread_pool_max, errp);
152
goto out_close_efd;
187
}
153
}
188
154
189
155
diff --git a/stubs/linux-aio.c b/stubs/linux-aio.c
190
diff --git a/util/aio-posix.c b/util/aio-posix.c
156
index XXXXXXX..XXXXXXX 100644
191
index XXXXXXX..XXXXXXX 100644
157
--- a/stubs/linux-aio.c
192
--- a/util/aio-posix.c
158
+++ b/stubs/linux-aio.c
193
+++ b/util/aio-posix.c
159
@@ -XXX,XX +XXX,XX @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
194
@@ -XXX,XX +XXX,XX @@
160
abort();
195
161
}
196
#include "qemu/osdep.h"
162
197
#include "block/block.h"
163
-LinuxAioState *laio_init(void)
198
+#include "block/thread-pool.h"
164
+LinuxAioState *laio_init(Error **errp)
199
#include "qemu/main-loop.h"
165
{
200
#include "qemu/rcu.h"
166
abort();
201
#include "qemu/rcu_queue.h"
167
}
168
diff --git a/util/async.c b/util/async.c
202
diff --git a/util/async.c b/util/async.c
169
index XXXXXXX..XXXXXXX 100644
203
index XXXXXXX..XXXXXXX 100644
170
--- a/util/async.c
204
--- a/util/async.c
171
+++ b/util/async.c
205
+++ b/util/async.c
172
@@ -XXX,XX +XXX,XX @@ ThreadPool *aio_get_thread_pool(AioContext *ctx)
206
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
173
}
207
174
208
ctx->aio_max_batch = 0;
175
#ifdef CONFIG_LINUX_AIO
209
176
-LinuxAioState *aio_get_linux_aio(AioContext *ctx)
210
+ ctx->thread_pool_min = 0;
177
+LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp)
211
+ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
212
+
213
return ctx;
214
fail:
215
g_source_destroy(&ctx->source);
216
@@ -XXX,XX +XXX,XX @@ void qemu_set_current_aio_context(AioContext *ctx)
217
assert(!get_my_aiocontext());
218
set_my_aiocontext(ctx);
219
}
220
+
221
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
222
+ int64_t max, Error **errp)
223
+{
224
+
225
+ if (min > max || !max || min > INT_MAX || max > INT_MAX) {
226
+ error_setg(errp, "bad thread-pool-min/thread-pool-max values");
227
+ return;
228
+ }
229
+
230
+ ctx->thread_pool_min = min;
231
+ ctx->thread_pool_max = max;
232
+
233
+ if (ctx->thread_pool) {
234
+ thread_pool_update_params(ctx->thread_pool, ctx);
235
+ }
236
+}
237
diff --git a/util/main-loop.c b/util/main-loop.c
238
index XXXXXXX..XXXXXXX 100644
239
--- a/util/main-loop.c
240
+++ b/util/main-loop.c
241
@@ -XXX,XX +XXX,XX @@
242
#include "sysemu/replay.h"
243
#include "qemu/main-loop.h"
244
#include "block/aio.h"
245
+#include "block/thread-pool.h"
246
#include "qemu/error-report.h"
247
#include "qemu/queue.h"
248
#include "qemu/compiler.h"
249
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
250
251
static void main_loop_update_params(EventLoopBase *base, Error **errp)
178
{
252
{
179
if (!ctx->linux_aio) {
253
+ ERRP_GUARD();
180
- ctx->linux_aio = laio_init();
254
+
181
- laio_attach_aio_context(ctx->linux_aio, ctx);
255
if (!qemu_aio_context) {
182
+ ctx->linux_aio = laio_init(errp);
256
error_setg(errp, "qemu aio context not ready");
183
+ if (ctx->linux_aio) {
257
return;
184
+ laio_attach_aio_context(ctx->linux_aio, ctx);
185
+ }
186
}
258
}
187
return ctx->linux_aio;
259
188
}
260
aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
189
+
261
+ if (*errp) {
190
+LinuxAioState *aio_get_linux_aio(AioContext *ctx)
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)
191
+{
291
+{
192
+ assert(ctx->linux_aio);
292
+ /*
193
+ return ctx->linux_aio;
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;
194
+}
305
+}
195
#endif
306
+
196
307
static void *worker_thread(void *opaque)
197
void aio_notify(AioContext *ctx)
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)
198
--
370
--
199
2.17.1
371
2.35.1
200
201
diff view generated by jsdifflib