1
The following changes since commit ecc1f5adeec4e3324d1b695a7c54e3967c526949:
1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
2
2
3
maintainers: Add myself as linux-user reviewer (2017-05-11 13:31:11 -0400)
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 321d1dba8bef9676a77e9399484e3cd8bf2cf16a:
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
10
10
11
aio: add missing aio_notify() to aio_enable_external() (2017-05-12 10:36:46 -0400)
11
util/event-loop-base: Introduce options to set the thread pool size (2022-05-04 17:02:19 +0100)
12
13
----------------------------------------------------------------
14
Pull request
15
16
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
17
used for async I/O.
12
18
13
----------------------------------------------------------------
19
----------------------------------------------------------------
14
20
15
----------------------------------------------------------------
21
Nicolas Saenz Julienne (3):
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
16
25
17
Daniel P. Berrange (1):
26
qapi/qom.json | 43 ++++++++--
18
coroutine: remove GThread implementation
27
meson.build | 26 +++---
19
28
include/block/aio.h | 10 +++
20
Eric Blake (1):
29
include/block/thread-pool.h | 3 +
21
block: Simplify BDRV_BLOCK_RAW recursion
30
include/qemu/main-loop.h | 10 +++
22
31
include/sysemu/event-loop-base.h | 41 +++++++++
23
Stefan Hajnoczi (1):
32
include/sysemu/iothread.h | 6 +-
24
aio: add missing aio_notify() to aio_enable_external()
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
25
34
iothread.c | 68 +++++----------
26
configure | 19 ++---
35
util/aio-posix.c | 1 +
27
include/block/aio.h | 10 ++-
36
util/async.c | 20 +++++
28
block/io.c | 4 +-
37
util/main-loop.c | 65 ++++++++++++++
29
util/coroutine-gthread.c | 198 -----------------------------------------------
38
util/thread-pool.c | 55 +++++++++++-
30
.travis.yml | 5 +-
39
13 files changed, 419 insertions(+), 69 deletions(-)
31
5 files changed, 16 insertions(+), 220 deletions(-)
40
create mode 100644 include/sysemu/event-loop-base.h
32
delete mode 100644 util/coroutine-gthread.c
41
create mode 100644 event-loop-base.c
33
42
34
--
43
--
35
2.9.3
44
2.35.1
36
37
diff view generated by jsdifflib
1
From: "Daniel P. Berrange" <berrange@redhat.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
The GThread implementation is not functional enough to actually
3
Introduce the 'event-loop-base' abstract class, it'll hold the
4
run QEMU reliably. While it was potentially useful for debugging,
4
properties common to all event loops and provide the necessary hooks for
5
we have a scripts/qemugdb/coroutine.py to enable tracing of
5
their creation and maintenance. Then have iothread inherit from it.
6
ucontext coroutines in GDB, so that removes the only reason for
6
7
GThread to exist.
7
EventLoopBaseClass is defined as user creatable and provides a hook for
8
8
its children to attach themselves to the user creatable class 'complete'
9
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
9
function. It also provides an update_params() callback to propagate
10
Acked-by: Alex Bennée <alex.bennee@linaro.org>
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
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
39
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
40
---
13
configure | 19 ++---
41
qapi/qom.json | 22 +++++--
14
util/coroutine-gthread.c | 198 -----------------------------------------------
42
meson.build | 23 ++++---
15
.travis.yml | 5 +-
43
include/sysemu/event-loop-base.h | 36 +++++++++++
16
3 files changed, 6 insertions(+), 216 deletions(-)
44
include/sysemu/iothread.h | 6 +-
17
delete mode 100644 util/coroutine-gthread.c
45
event-loop-base.c | 104 +++++++++++++++++++++++++++++++
18
46
iothread.c | 65 ++++++-------------
19
diff --git a/configure b/configure
47
6 files changed, 192 insertions(+), 64 deletions(-)
20
index XXXXXXX..XXXXXXX 100755
48
create mode 100644 include/sysemu/event-loop-base.h
21
--- a/configure
49
create mode 100644 event-loop-base.c
22
+++ b/configure
50
23
@@ -XXX,XX +XXX,XX @@ Advanced options (experts only):
51
diff --git a/qapi/qom.json b/qapi/qom.json
24
--oss-lib path to OSS library
52
index XXXXXXX..XXXXXXX 100644
25
--cpu=CPU Build for host CPU [$cpu]
53
--- a/qapi/qom.json
26
--with-coroutine=BACKEND coroutine backend. Supported options:
54
+++ b/qapi/qom.json
27
- gthread, ucontext, sigaltstack, windows
55
@@ -XXX,XX +XXX,XX @@
28
+ ucontext, sigaltstack, windows
56
'*repeat': 'bool',
29
--enable-gcov enable test coverage analysis with gcov
57
'*grab-toggle': 'GrabToggleKeys' } }
30
--gcov=GCOV use specified gcov [$gcov_tool]
58
31
--disable-blobs disable installing provided firmware blobs
59
+##
32
@@ -XXX,XX +XXX,XX @@ fi
60
+# @EventLoopBaseProperties:
33
# check and set a backend for coroutine
61
+#
34
62
+# Common properties for event loops
35
# We prefer ucontext, but it's not always possible. The fallback
63
+#
36
-# is sigcontext. gthread is not selectable except explicitly, because
64
+# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
37
-# it is not functional enough to run QEMU proper. (It is occasionally
65
+# 0 means that the engine will use its default.
38
-# useful for debugging purposes.) On Windows the only valid backend
66
+# (default: 0)
39
-# is the Windows-specific one.
67
+#
40
+# is sigcontext. On Windows the only valid backend is the Windows
68
+# Since: 7.1
41
+# specific one.
69
+##
42
70
+{ 'struct': 'EventLoopBaseProperties',
43
ucontext_works=no
71
+ 'data': { '*aio-max-batch': 'int' } }
44
if test "$darwin" != "yes"; then
72
+
45
@@ -XXX,XX +XXX,XX @@ else
73
##
46
feature_not_found "ucontext"
74
# @IothreadProperties:
47
fi
75
#
48
;;
76
@@ -XXX,XX +XXX,XX @@
49
- gthread|sigaltstack)
77
# algorithm detects it is spending too long polling without
50
+ sigaltstack)
78
# encountering events. 0 selects a default behaviour (default: 0)
51
if test "$mingw32" = "yes"; then
79
#
52
error_exit "only the 'windows' coroutine backend is valid for Windows"
80
-# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
53
fi
81
-# 0 means that the engine will use its default
54
@@ -XXX,XX +XXX,XX @@ else
82
-# (default:0, since 6.1)
55
fi
83
+# The @aio-max-batch option is available since 6.1.
56
84
#
57
if test "$coroutine_pool" = ""; then
85
# Since: 2.0
58
- if test "$coroutine" = "gthread"; then
86
##
59
- coroutine_pool=no
87
{ 'struct': 'IothreadProperties',
60
- else
88
+ 'base': 'EventLoopBaseProperties',
61
- coroutine_pool=yes
89
'data': { '*poll-max-ns': 'int',
62
- fi
90
'*poll-grow': 'int',
63
-fi
91
- '*poll-shrink': 'int',
64
-if test "$coroutine" = "gthread" -a "$coroutine_pool" = "yes"; then
92
- '*aio-max-batch': 'int' } }
65
- error_exit "'gthread' coroutine backend does not support pool (use --disable-coroutine-pool)"
93
+ '*poll-shrink': 'int' } }
66
+ coroutine_pool=yes
94
67
fi
95
##
68
96
# @MemoryBackendProperties:
69
if test "$debug_stack_usage" = "yes"; then
97
diff --git a/meson.build b/meson.build
70
diff --git a/util/coroutine-gthread.c b/util/coroutine-gthread.c
98
index XXXXXXX..XXXXXXX 100644
71
deleted file mode 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
72
index XXXXXXX..XXXXXXX
161
index XXXXXXX..XXXXXXX
73
--- a/util/coroutine-gthread.c
162
--- /dev/null
74
+++ /dev/null
163
+++ b/include/sysemu/event-loop-base.h
75
@@ -XXX,XX +XXX,XX @@
164
@@ -XXX,XX +XXX,XX @@
76
-/*
165
+/*
77
- * GThread coroutine initialization code
166
+ * QEMU event-loop backend
78
- *
167
+ *
79
- * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
168
+ * Copyright (C) 2022 Red Hat Inc
80
- * Copyright (C) 2011 Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
169
+ *
81
- *
170
+ * Authors:
82
- * This library is free software; you can redistribute it and/or
171
+ * Nicolas Saenz Julienne <nsaenzju@redhat.com>
83
- * modify it under the terms of the GNU Lesser General Public
172
+ *
84
- * License as published by the Free Software Foundation; either
173
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
85
- * version 2.0 of the License, or (at your option) any later version.
174
+ * See the COPYING file in the top-level directory.
86
- *
175
+ */
87
- * This library is distributed in the hope that it will be useful,
176
+#ifndef QEMU_EVENT_LOOP_BASE_H
88
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
177
+#define QEMU_EVENT_LOOP_BASE_H
89
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
178
+
90
- * Lesser General Public License for more details.
179
+#include "qom/object.h"
91
- *
180
+#include "block/aio.h"
92
- * You should have received a copy of the GNU Lesser General Public
181
+#include "qemu/typedefs.h"
93
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
182
+
94
- */
183
+#define TYPE_EVENT_LOOP_BASE "event-loop-base"
95
-
184
+OBJECT_DECLARE_TYPE(EventLoopBase, EventLoopBaseClass,
96
-#include "qemu/osdep.h"
185
+ EVENT_LOOP_BASE)
97
-#include "qemu-common.h"
186
+
98
-#include "qemu/coroutine_int.h"
187
+struct EventLoopBaseClass {
99
-
188
+ ObjectClass parent_class;
100
-typedef struct {
189
+
101
- Coroutine base;
190
+ void (*init)(EventLoopBase *base, Error **errp);
102
- GThread *thread;
191
+ void (*update_params)(EventLoopBase *base, Error **errp);
103
- bool runnable;
192
+};
104
- bool free_on_thread_exit;
193
+
105
- CoroutineAction action;
194
+struct EventLoopBase {
106
-} CoroutineGThread;
195
+ Object parent;
107
-
196
+
108
-static CompatGMutex coroutine_lock;
197
+ /* AioContext AIO engine parameters */
109
-static CompatGCond coroutine_cond;
198
+ int64_t aio_max_batch;
110
-
199
+};
111
-/* GLib 2.31 and beyond deprecated various parts of the thread API,
200
+#endif
112
- * but the new interfaces are not available in older GLib versions
201
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
113
- * so we have to cope with both.
202
index XXXXXXX..XXXXXXX 100644
114
- */
203
--- a/include/sysemu/iothread.h
115
-#if GLIB_CHECK_VERSION(2, 31, 0)
204
+++ b/include/sysemu/iothread.h
116
-/* Awkwardly, the GPrivate API doesn't provide a way to update the
205
@@ -XXX,XX +XXX,XX @@
117
- * GDestroyNotify handler for the coroutine key dynamically. So instead
206
#include "block/aio.h"
118
- * we track whether or not the CoroutineGThread should be freed on
207
#include "qemu/thread.h"
119
- * thread exit / coroutine key update using the free_on_thread_exit
208
#include "qom/object.h"
120
- * field.
209
+#include "sysemu/event-loop-base.h"
121
- */
210
122
-static void coroutine_destroy_notify(gpointer data)
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)
369
}
370
371
aio_context_set_aio_params(iothread->ctx,
372
- iothread->aio_max_batch,
373
+ iothread->parent_obj.aio_max_batch,
374
errp);
375
}
376
377
-static void iothread_complete(UserCreatable *obj, Error **errp)
378
+
379
+static void iothread_init(EventLoopBase *base, Error **errp)
380
{
381
Error *local_error = NULL;
382
- IOThread *iothread = IOTHREAD(obj);
383
+ IOThread *iothread = IOTHREAD(base);
384
char *thread_name;
385
386
iothread->stopping = false;
387
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
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,
416
}
417
}
418
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
420
- const char *name, void *opaque, Error **errp)
123
-{
421
-{
124
- CoroutineGThread *co = data;
422
- IOThreadParamInfo *info = opaque;
125
- if (co && co->free_on_thread_exit) {
423
-
126
- g_free(co);
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);
127
- }
441
- }
128
-}
442
-}
129
-
443
-
130
-static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify);
444
static void iothread_class_init(ObjectClass *klass, void *class_data)
131
-
445
{
132
-static inline CoroutineGThread *get_coroutine_key(void)
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
133
-{
447
- ucc->complete = iothread_complete;
134
- return g_private_get(&coroutine_key);
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
135
-}
449
+
136
-
450
+ bc->init = iothread_init;
137
-static inline void set_coroutine_key(CoroutineGThread *co,
451
+ bc->update_params = iothread_set_aio_context_params;
138
- bool free_on_thread_exit)
452
139
-{
453
object_class_property_add(klass, "poll-max-ns", "int",
140
- /* Unlike g_static_private_set() this does not call the GDestroyNotify
454
iothread_get_poll_param,
141
- * if the previous value of the key was NULL. Fortunately we only need
455
@@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
142
- * the GDestroyNotify in the non-NULL key case.
456
iothread_get_poll_param,
143
- */
457
iothread_set_poll_param,
144
- co->free_on_thread_exit = free_on_thread_exit;
458
NULL, &poll_shrink_info);
145
- g_private_replace(&coroutine_key, co);
459
- object_class_property_add(klass, "aio-max-batch", "int",
146
-}
460
- iothread_get_aio_param,
147
-
461
- iothread_set_aio_param,
148
-static inline GThread *create_thread(GThreadFunc func, gpointer data)
462
- NULL, &aio_max_batch_info);
149
-{
463
}
150
- return g_thread_new("coroutine", func, data);
464
151
-}
465
static const TypeInfo iothread_info = {
152
-
466
.name = TYPE_IOTHREAD,
153
-#else
467
- .parent = TYPE_OBJECT,
154
-
468
+ .parent = TYPE_EVENT_LOOP_BASE,
155
-/* Handle older GLib versions */
469
.class_init = iothread_class_init,
156
-
470
.instance_size = sizeof(IOThread),
157
-static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;
471
.instance_init = iothread_instance_init,
158
-
472
.instance_finalize = iothread_instance_finalize,
159
-static inline CoroutineGThread *get_coroutine_key(void)
473
- .interfaces = (InterfaceInfo[]) {
160
-{
474
- {TYPE_USER_CREATABLE},
161
- return g_static_private_get(&coroutine_key);
475
- {}
162
-}
476
- },
163
-
477
};
164
-static inline void set_coroutine_key(CoroutineGThread *co,
478
165
- bool free_on_thread_exit)
479
static void iothread_register_types(void)
166
-{
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
167
- g_static_private_set(&coroutine_key, co,
481
info->poll_max_ns = iothread->poll_max_ns;
168
- free_on_thread_exit ? (GDestroyNotify)g_free : NULL);
482
info->poll_grow = iothread->poll_grow;
169
-}
483
info->poll_shrink = iothread->poll_shrink;
170
-
484
- info->aio_max_batch = iothread->aio_max_batch;
171
-static inline GThread *create_thread(GThreadFunc func, gpointer data)
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
172
-{
486
173
- return g_thread_create_full(func, data, 0, TRUE, TRUE,
487
QAPI_LIST_APPEND(*tail, info);
174
- G_THREAD_PRIORITY_NORMAL, NULL);
488
return 0;
175
-}
176
-
177
-#endif
178
-
179
-
180
-static void __attribute__((constructor)) coroutine_init(void)
181
-{
182
-#if !GLIB_CHECK_VERSION(2, 31, 0)
183
- if (!g_thread_supported()) {
184
- g_thread_init(NULL);
185
- }
186
-#endif
187
-}
188
-
189
-static void coroutine_wait_runnable_locked(CoroutineGThread *co)
190
-{
191
- while (!co->runnable) {
192
- g_cond_wait(&coroutine_cond, &coroutine_lock);
193
- }
194
-}
195
-
196
-static void coroutine_wait_runnable(CoroutineGThread *co)
197
-{
198
- g_mutex_lock(&coroutine_lock);
199
- coroutine_wait_runnable_locked(co);
200
- g_mutex_unlock(&coroutine_lock);
201
-}
202
-
203
-static gpointer coroutine_thread(gpointer opaque)
204
-{
205
- CoroutineGThread *co = opaque;
206
-
207
- set_coroutine_key(co, false);
208
- coroutine_wait_runnable(co);
209
- co->base.entry(co->base.entry_arg);
210
- qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE);
211
- return NULL;
212
-}
213
-
214
-Coroutine *qemu_coroutine_new(void)
215
-{
216
- CoroutineGThread *co;
217
-
218
- co = g_malloc0(sizeof(*co));
219
- co->thread = create_thread(coroutine_thread, co);
220
- if (!co->thread) {
221
- g_free(co);
222
- return NULL;
223
- }
224
- return &co->base;
225
-}
226
-
227
-void qemu_coroutine_delete(Coroutine *co_)
228
-{
229
- CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_);
230
-
231
- g_thread_join(co->thread);
232
- g_free(co);
233
-}
234
-
235
-CoroutineAction qemu_coroutine_switch(Coroutine *from_,
236
- Coroutine *to_,
237
- CoroutineAction action)
238
-{
239
- CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_);
240
- CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_);
241
-
242
- g_mutex_lock(&coroutine_lock);
243
- from->runnable = false;
244
- from->action = action;
245
- to->runnable = true;
246
- to->action = action;
247
- g_cond_broadcast(&coroutine_cond);
248
-
249
- if (action != COROUTINE_TERMINATE) {
250
- coroutine_wait_runnable_locked(from);
251
- }
252
- g_mutex_unlock(&coroutine_lock);
253
- return from->action;
254
-}
255
-
256
-Coroutine *qemu_coroutine_self(void)
257
-{
258
- CoroutineGThread *co = get_coroutine_key();
259
- if (!co) {
260
- co = g_malloc0(sizeof(*co));
261
- co->runnable = true;
262
- set_coroutine_key(co, true);
263
- }
264
-
265
- return &co->base;
266
-}
267
-
268
-bool qemu_in_coroutine(void)
269
-{
270
- CoroutineGThread *co = get_coroutine_key();
271
-
272
- return co && co->base.caller;
273
-}
274
diff --git a/.travis.yml b/.travis.yml
275
index XXXXXXX..XXXXXXX 100644
276
--- a/.travis.yml
277
+++ b/.travis.yml
278
@@ -XXX,XX +XXX,XX @@ matrix:
279
- env: CONFIG="--enable-trace-backends=ust"
280
TEST_CMD=""
281
compiler: gcc
282
- - env: CONFIG="--with-coroutine=gthread"
283
- TEST_CMD=""
284
- compiler: gcc
285
- env: CONFIG=""
286
os: osx
287
compiler: clang
288
@@ -XXX,XX +XXX,XX @@ matrix:
289
compiler: none
290
env:
291
- COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
292
- - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user --with-coroutine=gthread"
293
+ - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
294
- TEST_CMD=""
295
before_script:
296
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
297
--
489
--
298
2.9.3
490
2.35.1
299
300
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
Since we are already in coroutine context during the body of
3
'event-loop-base' provides basic property handling for all 'AioContext'
4
bdrv_co_get_block_status(), we can shave off a few layers of
4
based event loops. So let's define a new 'MainLoopClass' that inherits
5
wrappers when recursing to query the protocol when a format driver
5
from it. This will permit tweaking the main loop's properties through
6
returned BDRV_BLOCK_RAW.
6
qapi as well as through the command line using the '-object' keyword[1].
7
7
Only one instance of 'MainLoopClass' might be created at any time.
8
Note that we are already using the correct recursion later on in
8
9
the same function, when probing whether the protocol layer is sparse
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
10
in order to find out if we can add BDRV_BLOCK_ZERO to an existing
10
mark 'MainLoop' as non-deletable.
11
BDRV_BLOCK_DATA|BDRV_BLOCK_OFFSET_VALID.
11
12
12
[1] For example:
13
Signed-off-by: Eric Blake <eblake@redhat.com>
13
-object main-loop,id=main-loop,aio-max-batch=<value>
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
15
Reviewed-by: Fam Zheng <famz@redhat.com>
15
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
16
Message-id: 20170504173745.27414-1-eblake@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
17
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
18
---
20
---
19
block/io.c | 4 ++--
21
qapi/qom.json | 13 ++++++++
20
1 file changed, 2 insertions(+), 2 deletions(-)
22
meson.build | 3 +-
21
23
include/qemu/main-loop.h | 10 ++++++
22
diff --git a/block/io.c b/block/io.c
24
include/sysemu/event-loop-base.h | 1 +
23
index XXXXXXX..XXXXXXX 100644
25
event-loop-base.c | 13 ++++++++
24
--- a/block/io.c
26
util/main-loop.c | 56 ++++++++++++++++++++++++++++++++
25
+++ b/block/io.c
27
6 files changed, 95 insertions(+), 1 deletion(-)
26
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
28
27
29
diff --git a/qapi/qom.json b/qapi/qom.json
28
if (ret & BDRV_BLOCK_RAW) {
30
index XXXXXXX..XXXXXXX 100644
29
assert(ret & BDRV_BLOCK_OFFSET_VALID);
31
--- a/qapi/qom.json
30
- ret = bdrv_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
32
+++ b/qapi/qom.json
31
- *pnum, pnum, file);
33
@@ -XXX,XX +XXX,XX @@
32
+ ret = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
34
'*poll-grow': 'int',
33
+ *pnum, pnum, file);
35
'*poll-shrink': 'int' } }
34
goto out;
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)
35
}
122
}
36
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
37
--
219
--
38
2.9.3
220
2.35.1
39
40
diff view generated by jsdifflib
1
The main loop uses aio_disable_external()/aio_enable_external() to
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
temporarily disable processing of external AioContext clients like
3
device emulation.
4
2
5
This allows monitor commands to quiesce I/O and prevent the guest from
3
The thread pool regulates itself: when idle, it kills threads until
6
submitting new requests while a monitor command is in progress.
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.
7
9
8
The aio_enable_external() API is currently broken when an IOThread is in
10
In order to mitigate this let's introduce a new 'EventLoopBase'
9
aio_poll() waiting for fd activity when the main loop re-enables
11
property to set the thread pool size. The threads will be created during
10
external clients. Incrementing ctx->external_disable_cnt does not wake
12
the pool's initialization or upon updating the property's value, remain
11
the IOThread from ppoll(2) so fd processing remains suspended and leads
13
available during its lifetime regardless of demand, and destroyed upon
12
to unresponsive emulated devices.
14
freeing it. A properly characterized workload will then be able to
15
configure the pool to avoid any latency spikes.
13
16
14
This patch adds an aio_notify() call to aio_enable_external() so the
17
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
15
IOThread is kicked out of ppoll(2) and will re-arm the file descriptors.
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
16
19
Acked-by: Markus Armbruster <armbru@redhat.com>
17
The bug can be reproduced as follows:
20
Message-id: 20220425075723.20019-4-nsaenzju@redhat.com
18
19
$ qemu -M accel=kvm -m 1024 \
20
-object iothread,id=iothread0 \
21
-device virtio-scsi-pci,iothread=iothread0,id=virtio-scsi-pci0 \
22
-drive if=none,id=drive0,aio=native,cache=none,format=raw,file=test.img \
23
-device scsi-hd,id=scsi-hd0,drive=drive0 \
24
-qmp tcp::5555,server,nowait
25
26
$ scripts/qmp/qmp-shell localhost:5555
27
(qemu) blockdev-snapshot-sync device=drive0 snapshot-file=sn1.qcow2
28
mode=absolute-paths format=qcow2
29
30
After blockdev-snapshot-sync completes the SCSI disk will be
31
unresponsive. This leads to request timeouts inside the guest.
32
33
Reported-by: Qianqian Zhu <qizhu@redhat.com>
34
Reviewed-by: Fam Zheng <famz@redhat.com>
35
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
36
Message-id: 20170508180705.20609-1-stefanha@redhat.com
37
Suggested-by: Fam Zheng <famz@redhat.com>
38
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
39
---
22
---
40
include/block/aio.h | 10 ++++++++--
23
qapi/qom.json | 10 +++++-
41
1 file changed, 8 insertions(+), 2 deletions(-)
24
include/block/aio.h | 10 ++++++
25
include/block/thread-pool.h | 3 ++
26
include/sysemu/event-loop-base.h | 4 +++
27
event-loop-base.c | 23 +++++++++++++
28
iothread.c | 3 ++
29
util/aio-posix.c | 1 +
30
util/async.c | 20 ++++++++++++
31
util/main-loop.c | 9 ++++++
32
util/thread-pool.c | 55 +++++++++++++++++++++++++++++---
33
10 files changed, 133 insertions(+), 5 deletions(-)
42
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:
43
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
44
index XXXXXXX..XXXXXXX 100644
60
index XXXXXXX..XXXXXXX 100644
45
--- a/include/block/aio.h
61
--- a/include/block/aio.h
46
+++ b/include/block/aio.h
62
+++ b/include/block/aio.h
47
@@ -XXX,XX +XXX,XX @@ static inline void aio_disable_external(AioContext *ctx)
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
48
*/
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
49
static inline void aio_enable_external(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);
75
76
+/**
77
+ * aio_context_set_thread_pool_params:
78
+ * @ctx: the aio context
79
+ * @min: min number of threads to have readily available in the thread pool
80
+ * @min: max number of threads the thread pool can contain
81
+ */
82
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
83
+ int64_t max, Error **errp);
84
#endif
85
diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h
86
index XXXXXXX..XXXXXXX 100644
87
--- a/include/block/thread-pool.h
88
+++ b/include/block/thread-pool.h
89
@@ -XXX,XX +XXX,XX @@
90
91
#include "block/block.h"
92
93
+#define THREAD_POOL_MAX_THREADS_DEFAULT 64
94
+
95
typedef int ThreadPoolFunc(void *opaque);
96
97
typedef struct ThreadPool ThreadPool;
98
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
99
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
100
ThreadPoolFunc *func, void *arg);
101
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
102
+void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx);
103
104
#endif
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBase {
110
111
/* AioContext AIO engine parameters */
112
int64_t aio_max_batch;
113
+
114
+ /* AioContext thread pool parameters */
115
+ int64_t thread_pool_min;
116
+ int64_t thread_pool_max;
117
};
118
#endif
119
diff --git a/event-loop-base.c b/event-loop-base.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/event-loop-base.c
122
+++ b/event-loop-base.c
123
@@ -XXX,XX +XXX,XX @@
124
#include "qemu/osdep.h"
125
#include "qom/object_interfaces.h"
126
#include "qapi/error.h"
127
+#include "block/thread-pool.h"
128
#include "sysemu/event-loop-base.h"
129
130
typedef struct {
131
@@ -XXX,XX +XXX,XX @@ typedef struct {
132
ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
133
} EventLoopBaseParamInfo;
134
135
+static void event_loop_base_instance_init(Object *obj)
136
+{
137
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
138
+
139
+ base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
140
+}
141
+
142
static EventLoopBaseParamInfo aio_max_batch_info = {
143
"aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
144
};
145
+static EventLoopBaseParamInfo thread_pool_min_info = {
146
+ "thread-pool-min", offsetof(EventLoopBase, thread_pool_min),
147
+};
148
+static EventLoopBaseParamInfo thread_pool_max_info = {
149
+ "thread-pool-max", offsetof(EventLoopBase, thread_pool_max),
150
+};
151
152
static void event_loop_base_get_param(Object *obj, Visitor *v,
153
const char *name, void *opaque, Error **errp)
154
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
155
event_loop_base_get_param,
156
event_loop_base_set_param,
157
NULL, &aio_max_batch_info);
158
+ object_class_property_add(klass, "thread-pool-min", "int",
159
+ event_loop_base_get_param,
160
+ event_loop_base_set_param,
161
+ NULL, &thread_pool_min_info);
162
+ object_class_property_add(klass, "thread-pool-max", "int",
163
+ event_loop_base_get_param,
164
+ event_loop_base_set_param,
165
+ NULL, &thread_pool_max_info);
166
}
167
168
static const TypeInfo event_loop_base_info = {
169
.name = TYPE_EVENT_LOOP_BASE,
170
.parent = TYPE_OBJECT,
171
.instance_size = sizeof(EventLoopBase),
172
+ .instance_init = event_loop_base_instance_init,
173
.class_size = sizeof(EventLoopBaseClass),
174
.class_init = event_loop_base_class_init,
175
.abstract = true,
176
diff --git a/iothread.c b/iothread.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/iothread.c
179
+++ b/iothread.c
180
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
181
aio_context_set_aio_params(iothread->ctx,
182
iothread->parent_obj.aio_max_batch,
183
errp);
184
+
185
+ aio_context_set_thread_pool_params(iothread->ctx, base->thread_pool_min,
186
+ base->thread_pool_max, errp);
187
}
188
189
190
diff --git a/util/aio-posix.c b/util/aio-posix.c
191
index XXXXXXX..XXXXXXX 100644
192
--- a/util/aio-posix.c
193
+++ b/util/aio-posix.c
194
@@ -XXX,XX +XXX,XX @@
195
196
#include "qemu/osdep.h"
197
#include "block/block.h"
198
+#include "block/thread-pool.h"
199
#include "qemu/main-loop.h"
200
#include "qemu/rcu.h"
201
#include "qemu/rcu_queue.h"
202
diff --git a/util/async.c b/util/async.c
203
index XXXXXXX..XXXXXXX 100644
204
--- a/util/async.c
205
+++ b/util/async.c
206
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
207
208
ctx->aio_max_batch = 0;
209
210
+ ctx->thread_pool_min = 0;
211
+ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
212
+
213
return ctx;
214
fail:
215
g_source_destroy(&ctx->source);
216
@@ -XXX,XX +XXX,XX @@ void qemu_set_current_aio_context(AioContext *ctx)
217
assert(!get_my_aiocontext());
218
set_my_aiocontext(ctx);
219
}
220
+
221
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
222
+ int64_t max, Error **errp)
223
+{
224
+
225
+ if (min > max || !max || min > INT_MAX || max > INT_MAX) {
226
+ error_setg(errp, "bad thread-pool-min/thread-pool-max values");
227
+ return;
228
+ }
229
+
230
+ ctx->thread_pool_min = min;
231
+ ctx->thread_pool_max = max;
232
+
233
+ if (ctx->thread_pool) {
234
+ thread_pool_update_params(ctx->thread_pool, ctx);
235
+ }
236
+}
237
diff --git a/util/main-loop.c b/util/main-loop.c
238
index XXXXXXX..XXXXXXX 100644
239
--- a/util/main-loop.c
240
+++ b/util/main-loop.c
241
@@ -XXX,XX +XXX,XX @@
242
#include "sysemu/replay.h"
243
#include "qemu/main-loop.h"
244
#include "block/aio.h"
245
+#include "block/thread-pool.h"
246
#include "qemu/error-report.h"
247
#include "qemu/queue.h"
248
#include "qemu/compiler.h"
249
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
250
251
static void main_loop_update_params(EventLoopBase *base, Error **errp)
50
{
252
{
51
- assert(ctx->external_disable_cnt > 0);
253
+ ERRP_GUARD();
52
- atomic_dec(&ctx->external_disable_cnt);
254
+
53
+ int old;
255
if (!qemu_aio_context) {
54
+
256
error_setg(errp, "qemu aio context not ready");
55
+ old = atomic_fetch_dec(&ctx->external_disable_cnt);
257
return;
56
+ assert(old > 0);
258
}
57
+ if (old == 1) {
259
58
+ /* Kick event loop so it re-arms file descriptors */
260
aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
59
+ aio_notify(ctx);
261
+ if (*errp) {
60
+ }
262
+ return;
61
}
263
+ }
62
264
+
63
/**
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)
64
--
370
--
65
2.9.3
371
2.35.1
66
67
diff view generated by jsdifflib