1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
1
The following changes since commit 42f6c9179be4401974dd3a75ee72defd16b5092d:
2
2
3
Merge tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu into staging (2022-05-04 03:42:49 -0700)
3
Merge tag 'pull-ppc-20211112' of https://github.com/legoater/qemu into staging (2021-11-12 12:28:25 +0100)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
7
https://gitlab.com/hreitz/qemu.git tags/pull-block-2021-11-16
8
8
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
9
for you to fetch changes up to 5dbd0ce115c7720268e653fe928bb6a0c1314a80:
10
10
11
util/event-loop-base: Introduce options to set the thread pool size (2022-05-04 17:02:19 +0100)
11
file-posix: Fix alignment after reopen changing O_DIRECT (2021-11-16 11:30:29 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Pull request
14
Block patches for 6.2.0-rc1:
15
15
- Fixes to image streaming job and block layer reconfiguration to make
16
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
16
iotest 030 pass again
17
used for async I/O.
17
- docs: Deprecate incorrectly typed device_add arguments
18
- file-posix: Fix alignment after reopen changing O_DIRECT
18
19
19
----------------------------------------------------------------
20
----------------------------------------------------------------
21
v2:
22
- Fixed iotest 142 (modified by "file-posix: Fix alignment after reopen
23
changing O_DIRECT") -- at least I hope so: for me, it now passes on a
24
4k block device, and the gitlab pipeline passed, too
20
25
21
Nicolas Saenz Julienne (3):
26
- Note that because I had to modify Kevin's pull request, I did not want
22
Introduce event-loop-base abstract class
27
to merge it partially (with a merge commit), but instead decided to
23
util/main-loop: Introduce the main loop into QOM
28
apply all patches from the pull request mails (including their message
24
util/event-loop-base: Introduce options to set the thread pool size
29
IDs)
25
30
26
qapi/qom.json | 43 ++++++++--
31
----------------------------------------------------------------
27
meson.build | 26 +++---
32
Hanna Reitz (10):
28
include/block/aio.h | 10 +++
33
stream: Traverse graph after modification
29
include/block/thread-pool.h | 3 +
34
block: Manipulate children list in .attach/.detach
30
include/qemu/main-loop.h | 10 +++
35
block: Unite remove_empty_child and child_free
31
include/sysemu/event-loop-base.h | 41 +++++++++
36
block: Drop detached child from ignore list
32
include/sysemu/iothread.h | 6 +-
37
block: Pass BdrvChild ** to replace_child_noperm
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
38
block: Restructure remove_file_or_backing_child()
34
iothread.c | 68 +++++----------
39
transactions: Invoke clean() after everything else
35
util/aio-posix.c | 1 +
40
block: Let replace_child_tran keep indirect pointer
36
util/async.c | 20 +++++
41
block: Let replace_child_noperm free children
37
util/main-loop.c | 65 ++++++++++++++
42
iotests/030: Unthrottle parallel jobs in reverse
38
util/thread-pool.c | 55 +++++++++++-
43
39
13 files changed, 419 insertions(+), 69 deletions(-)
44
Kevin Wolf (2):
40
create mode 100644 include/sysemu/event-loop-base.h
45
docs: Deprecate incorrectly typed device_add arguments
41
create mode 100644 event-loop-base.c
46
file-posix: Fix alignment after reopen changing O_DIRECT
47
48
Stefan Hajnoczi (1):
49
softmmu/qdev-monitor: fix use-after-free in qdev_set_id()
50
51
docs/about/deprecated.rst | 14 +++
52
include/qemu/transactions.h | 3 +
53
block.c | 233 +++++++++++++++++++++++++++---------
54
block/file-posix.c | 20 +++-
55
block/stream.c | 7 +-
56
softmmu/qdev-monitor.c | 2 +-
57
util/transactions.c | 8 +-
58
tests/qemu-iotests/030 | 11 +-
59
tests/qemu-iotests/142 | 29 +++++
60
tests/qemu-iotests/142.out | 18 +++
61
10 files changed, 279 insertions(+), 66 deletions(-)
42
62
43
--
63
--
44
2.35.1
64
2.33.1
65
66
diff view generated by jsdifflib
New patch
1
bdrv_cor_filter_drop() modifies the block graph. That means that other
2
parties can also modify the block graph before it returns. Therefore,
3
we cannot assume that the result of a graph traversal we did before
4
remains valid afterwards.
1
5
6
We should thus fetch `base` and `unfiltered_base` afterwards instead of
7
before.
8
9
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Message-Id: <20211111120829.81329-2-hreitz@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Message-Id: <20211115145409.176785-2-kwolf@redhat.com>
15
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
16
---
17
block/stream.c | 7 +++++--
18
1 file changed, 5 insertions(+), 2 deletions(-)
19
20
diff --git a/block/stream.c b/block/stream.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/block/stream.c
23
+++ b/block/stream.c
24
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
25
{
26
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
27
BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs);
28
- BlockDriverState *base = bdrv_filter_or_cow_bs(s->above_base);
29
- BlockDriverState *unfiltered_base = bdrv_skip_filters(base);
30
+ BlockDriverState *base;
31
+ BlockDriverState *unfiltered_base;
32
Error *local_err = NULL;
33
int ret = 0;
34
35
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
36
bdrv_cor_filter_drop(s->cor_filter_bs);
37
s->cor_filter_bs = NULL;
38
39
+ base = bdrv_filter_or_cow_bs(s->above_base);
40
+ unfiltered_base = bdrv_skip_filters(base);
41
+
42
if (bdrv_cow_child(unfiltered_bs)) {
43
const char *base_id = NULL, *base_fmt = NULL;
44
if (unfiltered_base) {
45
--
46
2.33.1
47
48
diff view generated by jsdifflib
New patch
1
The children list is specific to BDS parents. We should not modify it
2
in the general children modification code, but let BDS parents deal with
3
it in their .attach() and .detach() methods.
1
4
5
This also has the advantage that a BdrvChild is removed from the
6
children list before its .bs pointer can become NULL. BDS parents
7
generally assume that their children's .bs pointer is never NULL, so
8
this is actually a bug fix.
9
10
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
11
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Message-Id: <20211111120829.81329-3-hreitz@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Message-Id: <20211115145409.176785-3-kwolf@redhat.com>
15
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
16
---
17
block.c | 14 +++++---------
18
1 file changed, 5 insertions(+), 9 deletions(-)
19
20
diff --git a/block.c b/block.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/block.c
23
+++ b/block.c
24
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child)
25
{
26
BlockDriverState *bs = child->opaque;
27
28
+ QLIST_INSERT_HEAD(&bs->children, child, next);
29
+
30
if (child->role & BDRV_CHILD_COW) {
31
bdrv_backing_attach(child);
32
}
33
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_detach(BdrvChild *child)
34
}
35
36
bdrv_unapply_subtree_drain(child, bs);
37
+
38
+ QLIST_REMOVE(child, next);
39
}
40
41
static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
42
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_free(void *opaque)
43
static void bdrv_remove_empty_child(BdrvChild *child)
44
{
45
assert(!child->bs);
46
- QLIST_SAFE_REMOVE(child, next);
47
+ assert(!child->next.le_prev); /* not in children list */
48
bdrv_child_free(child);
49
}
50
51
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
52
return ret;
53
}
54
55
- QLIST_INSERT_HEAD(&parent_bs->children, *child, next);
56
- /*
57
- * child is removed in bdrv_attach_child_common_abort(), so don't care to
58
- * abort this change separately.
59
- */
60
-
61
return 0;
62
}
63
64
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
65
BdrvRemoveFilterOrCowChild *s = opaque;
66
BlockDriverState *parent_bs = s->child->opaque;
67
68
- QLIST_INSERT_HEAD(&parent_bs->children, s->child, next);
69
if (s->is_backing) {
70
parent_bs->backing = s->child;
71
} else {
72
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
73
};
74
tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
75
76
- QLIST_SAFE_REMOVE(child, next);
77
if (s->is_backing) {
78
bs->backing = NULL;
79
} else {
80
--
81
2.33.1
82
83
diff view generated by jsdifflib
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
1
Now that bdrv_remove_empty_child() no longer removes the child from the
2
parent's children list but only checks that it is not in such a list, it
3
is only a wrapper around bdrv_child_free() that checks that the child is
4
empty and unused. That should apply to all children that we free, so
5
put those checks into bdrv_child_free() and drop
6
bdrv_remove_empty_child().
2
7
3
Introduce the 'event-loop-base' abstract class, it'll hold the
8
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
4
properties common to all event loops and provide the necessary hooks for
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
their creation and maintenance. Then have iothread inherit from it.
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Message-Id: <20211111120829.81329-4-hreitz@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20211115145409.176785-4-kwolf@redhat.com>
14
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
15
---
16
block.c | 26 +++++++++++++-------------
17
1 file changed, 13 insertions(+), 13 deletions(-)
6
18
7
EventLoopBaseClass is defined as user creatable and provides a hook for
19
diff --git a/block.c b/block.c
8
its children to attach themselves to the user creatable class 'complete'
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
39
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
40
---
41
qapi/qom.json | 22 +++++--
42
meson.build | 23 ++++---
43
include/sysemu/event-loop-base.h | 36 +++++++++++
44
include/sysemu/iothread.h | 6 +-
45
event-loop-base.c | 104 +++++++++++++++++++++++++++++++
46
iothread.c | 65 ++++++-------------
47
6 files changed, 192 insertions(+), 64 deletions(-)
48
create mode 100644 include/sysemu/event-loop-base.h
49
create mode 100644 event-loop-base.c
50
51
diff --git a/qapi/qom.json b/qapi/qom.json
52
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
53
--- a/qapi/qom.json
21
--- a/block.c
54
+++ b/qapi/qom.json
22
+++ b/block.c
55
@@ -XXX,XX +XXX,XX @@
23
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
56
'*repeat': 'bool',
57
'*grab-toggle': 'GrabToggleKeys' } }
58
59
+##
60
+# @EventLoopBaseProperties:
61
+#
62
+# Common properties for event loops
63
+#
64
+# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
65
+# 0 means that the engine will use its default.
66
+# (default: 0)
67
+#
68
+# Since: 7.1
69
+##
70
+{ 'struct': 'EventLoopBaseProperties',
71
+ 'data': { '*aio-max-batch': 'int' } }
72
+
73
##
74
# @IothreadProperties:
75
#
76
@@ -XXX,XX +XXX,XX @@
77
# algorithm detects it is spending too long polling without
78
# encountering events. 0 selects a default behaviour (default: 0)
79
#
80
-# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
81
-# 0 means that the engine will use its default
82
-# (default:0, since 6.1)
83
+# The @aio-max-batch option is available since 6.1.
84
#
85
# Since: 2.0
86
##
87
{ 'struct': 'IothreadProperties',
88
+ 'base': 'EventLoopBaseProperties',
89
'data': { '*poll-max-ns': 'int',
90
'*poll-grow': 'int',
91
- '*poll-shrink': 'int',
92
- '*aio-max-batch': 'int' } }
93
+ '*poll-shrink': 'int' } }
94
95
##
96
# @MemoryBackendProperties:
97
diff --git a/meson.build b/meson.build
98
index XXXXXXX..XXXXXXX 100644
99
--- a/meson.build
100
+++ b/meson.build
101
@@ -XXX,XX +XXX,XX @@ subdir('qom')
102
subdir('authz')
103
subdir('crypto')
104
subdir('ui')
105
+subdir('hw')
106
107
108
if enable_modules
109
@@ -XXX,XX +XXX,XX @@ if enable_modules
110
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
111
endif
112
113
+qom_ss = qom_ss.apply(config_host, strict: false)
114
+libqom = static_library('qom', qom_ss.sources() + genh,
115
+ dependencies: [qom_ss.dependencies()],
116
+ name_suffix: 'fa')
117
+qom = declare_dependency(link_whole: libqom)
118
+
119
+event_loop_base = files('event-loop-base.c')
120
+event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh,
121
+ build_by_default: true)
122
+event_loop_base = declare_dependency(link_whole: event_loop_base,
123
+ dependencies: [qom])
124
+
125
stub_ss = stub_ss.apply(config_all, strict: false)
126
127
util_ss.add_all(trace_ss)
128
@@ -XXX,XX +XXX,XX @@ subdir('monitor')
129
subdir('net')
130
subdir('replay')
131
subdir('semihosting')
132
-subdir('hw')
133
subdir('tcg')
134
subdir('fpu')
135
subdir('accel')
136
@@ -XXX,XX +XXX,XX @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms',
137
capture: true,
138
command: [undefsym, nm, '@INPUT@'])
139
140
-qom_ss = qom_ss.apply(config_host, strict: false)
141
-libqom = static_library('qom', qom_ss.sources() + genh,
142
- dependencies: [qom_ss.dependencies()],
143
- name_suffix: 'fa')
144
-
145
-qom = declare_dependency(link_whole: libqom)
146
-
147
authz_ss = authz_ss.apply(config_host, strict: false)
148
libauthz = static_library('authz', authz_ss.sources() + genh,
149
dependencies: [authz_ss.dependencies()],
150
@@ -XXX,XX +XXX,XX @@ libblockdev = static_library('blockdev', blockdev_ss.sources() + genh,
151
build_by_default: false)
152
153
blockdev = declare_dependency(link_whole: [libblockdev],
154
- dependencies: [block])
155
+ dependencies: [block, event_loop_base])
156
157
qmp_ss = qmp_ss.apply(config_host, strict: false)
158
libqmp = static_library('qmp', qmp_ss.sources() + genh,
159
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
160
new file mode 100644
161
index XXXXXXX..XXXXXXX
162
--- /dev/null
163
+++ b/include/sysemu/event-loop-base.h
164
@@ -XXX,XX +XXX,XX @@
165
+/*
166
+ * QEMU event-loop backend
167
+ *
168
+ * Copyright (C) 2022 Red Hat Inc
169
+ *
170
+ * Authors:
171
+ * Nicolas Saenz Julienne <nsaenzju@redhat.com>
172
+ *
173
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
174
+ * See the COPYING file in the top-level directory.
175
+ */
176
+#ifndef QEMU_EVENT_LOOP_BASE_H
177
+#define QEMU_EVENT_LOOP_BASE_H
178
+
179
+#include "qom/object.h"
180
+#include "block/aio.h"
181
+#include "qemu/typedefs.h"
182
+
183
+#define TYPE_EVENT_LOOP_BASE "event-loop-base"
184
+OBJECT_DECLARE_TYPE(EventLoopBase, EventLoopBaseClass,
185
+ EVENT_LOOP_BASE)
186
+
187
+struct EventLoopBaseClass {
188
+ ObjectClass parent_class;
189
+
190
+ void (*init)(EventLoopBase *base, Error **errp);
191
+ void (*update_params)(EventLoopBase *base, Error **errp);
192
+};
193
+
194
+struct EventLoopBase {
195
+ Object parent;
196
+
197
+ /* AioContext AIO engine parameters */
198
+ int64_t aio_max_batch;
199
+};
200
+#endif
201
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
202
index XXXXXXX..XXXXXXX 100644
203
--- a/include/sysemu/iothread.h
204
+++ b/include/sysemu/iothread.h
205
@@ -XXX,XX +XXX,XX @@
206
#include "block/aio.h"
207
#include "qemu/thread.h"
208
#include "qom/object.h"
209
+#include "sysemu/event-loop-base.h"
210
211
#define TYPE_IOTHREAD "iothread"
212
213
struct IOThread {
214
- Object parent_obj;
215
+ EventLoopBase parent_obj;
216
217
QemuThread thread;
218
AioContext *ctx;
219
@@ -XXX,XX +XXX,XX @@ struct IOThread {
220
int64_t poll_max_ns;
221
int64_t poll_grow;
222
int64_t poll_shrink;
223
-
224
- /* AioContext AIO engine parameters */
225
- int64_t aio_max_batch;
226
};
227
typedef struct IOThread IOThread;
228
229
diff --git a/event-loop-base.c b/event-loop-base.c
230
new file mode 100644
231
index XXXXXXX..XXXXXXX
232
--- /dev/null
233
+++ b/event-loop-base.c
234
@@ -XXX,XX +XXX,XX @@
235
+/*
236
+ * QEMU event-loop base
237
+ *
238
+ * Copyright (C) 2022 Red Hat Inc
239
+ *
240
+ * Authors:
241
+ * Stefan Hajnoczi <stefanha@redhat.com>
242
+ * Nicolas Saenz Julienne <nsaenzju@redhat.com>
243
+ *
244
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
245
+ * See the COPYING file in the top-level directory.
246
+ */
247
+
248
+#include "qemu/osdep.h"
249
+#include "qom/object_interfaces.h"
250
+#include "qapi/error.h"
251
+#include "sysemu/event-loop-base.h"
252
+
253
+typedef struct {
254
+ const char *name;
255
+ ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
256
+} EventLoopBaseParamInfo;
257
+
258
+static EventLoopBaseParamInfo aio_max_batch_info = {
259
+ "aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
260
+};
261
+
262
+static void event_loop_base_get_param(Object *obj, Visitor *v,
263
+ const char *name, void *opaque, Error **errp)
264
+{
265
+ EventLoopBase *event_loop_base = EVENT_LOOP_BASE(obj);
266
+ EventLoopBaseParamInfo *info = opaque;
267
+ int64_t *field = (void *)event_loop_base + info->offset;
268
+
269
+ visit_type_int64(v, name, field, errp);
270
+}
271
+
272
+static void event_loop_base_set_param(Object *obj, Visitor *v,
273
+ const char *name, void *opaque, Error **errp)
274
+{
275
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(obj);
276
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
277
+ EventLoopBaseParamInfo *info = opaque;
278
+ int64_t *field = (void *)base + info->offset;
279
+ int64_t value;
280
+
281
+ if (!visit_type_int64(v, name, &value, errp)) {
282
+ return;
283
+ }
284
+
285
+ if (value < 0) {
286
+ error_setg(errp, "%s value must be in range [0, %" PRId64 "]",
287
+ info->name, INT64_MAX);
288
+ return;
289
+ }
290
+
291
+ *field = value;
292
+
293
+ if (bc->update_params) {
294
+ bc->update_params(base, errp);
295
+ }
296
+
297
+ return;
298
+}
299
+
300
+static void event_loop_base_complete(UserCreatable *uc, Error **errp)
301
+{
302
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
303
+ EventLoopBase *base = EVENT_LOOP_BASE(uc);
304
+
305
+ if (bc->init) {
306
+ bc->init(base, errp);
307
+ }
308
+}
309
+
310
+static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
311
+{
312
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
313
+ ucc->complete = event_loop_base_complete;
314
+
315
+ object_class_property_add(klass, "aio-max-batch", "int",
316
+ event_loop_base_get_param,
317
+ event_loop_base_set_param,
318
+ NULL, &aio_max_batch_info);
319
+}
320
+
321
+static const TypeInfo event_loop_base_info = {
322
+ .name = TYPE_EVENT_LOOP_BASE,
323
+ .parent = TYPE_OBJECT,
324
+ .instance_size = sizeof(EventLoopBase),
325
+ .class_size = sizeof(EventLoopBaseClass),
326
+ .class_init = event_loop_base_class_init,
327
+ .abstract = true,
328
+ .interfaces = (InterfaceInfo[]) {
329
+ { TYPE_USER_CREATABLE },
330
+ { }
331
+ }
332
+};
333
+
334
+static void register_types(void)
335
+{
336
+ type_register_static(&event_loop_base_info);
337
+}
338
+type_init(register_types);
339
diff --git a/iothread.c b/iothread.c
340
index XXXXXXX..XXXXXXX 100644
341
--- a/iothread.c
342
+++ b/iothread.c
343
@@ -XXX,XX +XXX,XX @@
344
#include "qemu/module.h"
345
#include "block/aio.h"
346
#include "block/block.h"
347
+#include "sysemu/event-loop-base.h"
348
#include "sysemu/iothread.h"
349
#include "qapi/error.h"
350
#include "qapi/qapi-commands-misc.h"
351
@@ -XXX,XX +XXX,XX @@ static void iothread_init_gcontext(IOThread *iothread)
352
iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE);
353
}
354
355
-static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
356
+static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
357
{
358
+ IOThread *iothread = IOTHREAD(base);
359
ERRP_GUARD();
360
361
+ if (!iothread->ctx) {
362
+ return;
363
+ }
364
+
365
aio_context_set_poll_params(iothread->ctx,
366
iothread->poll_max_ns,
367
iothread->poll_grow,
368
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
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
}
24
}
417
}
25
}
418
26
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
27
-static void bdrv_child_free(void *opaque)
420
- const char *name, void *opaque, Error **errp)
421
-{
28
-{
422
- IOThreadParamInfo *info = opaque;
29
- BdrvChild *c = opaque;
423
-
30
-
424
- iothread_get_param(obj, v, name, info, errp);
31
- g_free(c->name);
32
- g_free(c);
425
-}
33
-}
426
-
34
-
427
-static void iothread_set_aio_param(Object *obj, Visitor *v,
35
-static void bdrv_remove_empty_child(BdrvChild *child)
428
- const char *name, void *opaque, Error **errp)
36
+/**
429
-{
37
+ * Free the given @child.
430
- IOThread *iothread = IOTHREAD(obj);
38
+ *
431
- IOThreadParamInfo *info = opaque;
39
+ * The child must be empty (i.e. `child->bs == NULL`) and it must be
432
-
40
+ * unused (i.e. not in a children list).
433
- if (!iothread_set_param(obj, v, name, info, errp)) {
41
+ */
434
- return;
42
+static void bdrv_child_free(BdrvChild *child)
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
{
43
{
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
44
assert(!child->bs);
447
- ucc->complete = iothread_complete;
45
assert(!child->next.le_prev); /* not in children list */
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
46
- bdrv_child_free(child);
449
+
47
+
450
+ bc->init = iothread_init;
48
+ g_free(child->name);
451
+ bc->update_params = iothread_set_aio_context_params;
49
+ g_free(child);
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
}
50
}
464
51
465
static const TypeInfo iothread_info = {
52
typedef struct BdrvAttachChildCommonState {
466
.name = TYPE_IOTHREAD,
53
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque)
467
- .parent = TYPE_OBJECT,
54
}
468
+ .parent = TYPE_EVENT_LOOP_BASE,
55
469
.class_init = iothread_class_init,
56
bdrv_unref(bs);
470
.instance_size = sizeof(IOThread),
57
- bdrv_remove_empty_child(child);
471
.instance_init = iothread_instance_init,
58
+ bdrv_child_free(child);
472
.instance_finalize = iothread_instance_finalize,
59
*s->child = NULL;
473
- .interfaces = (InterfaceInfo[]) {
60
}
474
- {TYPE_USER_CREATABLE},
61
475
- {}
62
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
476
- },
63
477
};
64
if (ret < 0) {
478
65
error_propagate(errp, local_err);
479
static void iothread_register_types(void)
66
- bdrv_remove_empty_child(new_child);
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
67
+ bdrv_child_free(new_child);
481
info->poll_max_ns = iothread->poll_max_ns;
68
return ret;
482
info->poll_grow = iothread->poll_grow;
69
}
483
info->poll_shrink = iothread->poll_shrink;
70
}
484
- info->aio_max_batch = iothread->aio_max_batch;
71
@@ -XXX,XX +XXX,XX @@ static void bdrv_detach_child(BdrvChild *child)
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
72
BlockDriverState *old_bs = child->bs;
486
73
487
QAPI_LIST_APPEND(*tail, info);
74
bdrv_replace_child_noperm(child, NULL);
488
return 0;
75
- bdrv_remove_empty_child(child);
76
+ bdrv_child_free(child);
77
78
if (old_bs) {
79
/*
489
--
80
--
490
2.35.1
81
2.33.1
82
83
diff view generated by jsdifflib
New patch
1
bdrv_attach_child_common_abort() restores the parent's AioContext. To
2
do so, the child (which was supposed to be attached, but is now detached
3
again by this abort handler) is added to the ignore list for the
4
AioContext changing functions.
1
5
6
However, since we modify a BDS's children list in the BdrvChildClass's
7
.attach and .detach handlers, the child is already effectively detached
8
from the parent by this point. We do not need to put it into the ignore
9
list.
10
11
Use this opportunity to clean up the empty line structure: Keep setting
12
the ignore list, invoking the AioContext function, and freeing the
13
ignore list in blocks separated by empty lines.
14
15
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
16
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
17
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
18
Message-Id: <20211111120829.81329-5-hreitz@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
Message-Id: <20211115145409.176785-5-kwolf@redhat.com>
21
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
22
---
23
block.c | 8 +++++---
24
1 file changed, 5 insertions(+), 3 deletions(-)
25
26
diff --git a/block.c b/block.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/block.c
29
+++ b/block.c
30
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque)
31
}
32
33
if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) {
34
- GSList *ignore = g_slist_prepend(NULL, child);
35
+ GSList *ignore;
36
37
+ /* No need to ignore `child`, because it has been detached already */
38
+ ignore = NULL;
39
child->klass->can_set_aio_ctx(child, s->old_parent_ctx, &ignore,
40
&error_abort);
41
g_slist_free(ignore);
42
- ignore = g_slist_prepend(NULL, child);
43
- child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore);
44
45
+ ignore = NULL;
46
+ child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore);
47
g_slist_free(ignore);
48
}
49
50
--
51
2.33.1
52
53
diff view generated by jsdifflib
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
1
bdrv_replace_child_noperm() modifies BdrvChild.bs, and can potentially
2
set it to NULL. That is dangerous, because BDS parents generally assume
3
that their children's .bs pointer is never NULL. We therefore want to
4
let bdrv_replace_child_noperm() set the corresponding BdrvChild pointer
5
to NULL, too.
2
6
3
The thread pool regulates itself: when idle, it kills threads until
7
This patch lays the foundation for it by passing a BdrvChild ** pointer
4
empty, when in demand, it creates new threads until full. This behaviour
8
to bdrv_replace_child_noperm() so that it can later use it to NULL the
5
doesn't play well with latency sensitive workloads where the price of
9
BdrvChild pointer immediately after setting BdrvChild.bs to NULL.
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.
9
10
10
In order to mitigate this let's introduce a new 'EventLoopBase'
11
(We will still need to undertake some intermediate steps, though.)
11
property to set the thread pool size. The threads will be created during
12
the pool's initialization or upon updating the property's value, remain
13
available during its lifetime regardless of demand, and destroyed upon
14
freeing it. A properly characterized workload will then be able to
15
configure the pool to avoid any latency spikes.
16
12
17
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
13
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Message-Id: <20211111120829.81329-6-hreitz@redhat.com>
19
Acked-by: Markus Armbruster <armbru@redhat.com>
15
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
20
Message-id: 20220425075723.20019-4-nsaenzju@redhat.com
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Message-Id: <20211115145409.176785-6-kwolf@redhat.com>
18
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
22
---
19
---
23
qapi/qom.json | 10 +++++-
20
block.c | 23 ++++++++++++-----------
24
include/block/aio.h | 10 ++++++
21
1 file changed, 12 insertions(+), 11 deletions(-)
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(-)
34
22
35
diff --git a/qapi/qom.json b/qapi/qom.json
23
diff --git a/block.c b/block.c
36
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
37
--- a/qapi/qom.json
25
--- a/block.c
38
+++ b/qapi/qom.json
26
+++ b/block.c
39
@@ -XXX,XX +XXX,XX @@
27
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
40
# 0 means that the engine will use its default.
28
static bool bdrv_recurse_has_child(BlockDriverState *bs,
41
# (default: 0)
29
BlockDriverState *child);
42
#
30
43
+# @thread-pool-min: minimum number of threads reserved in the thread pool
31
-static void bdrv_replace_child_noperm(BdrvChild *child,
44
+# (default:0)
32
+static void bdrv_replace_child_noperm(BdrvChild **child,
45
+#
33
BlockDriverState *new_bs);
46
+# @thread-pool-max: maximum number of threads the thread pool can contain
34
static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
47
+# (default:64)
35
BdrvChild *child,
48
+#
36
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque)
49
# Since: 7.1
37
BlockDriverState *new_bs = s->child->bs;
50
##
38
51
{ 'struct': 'EventLoopBaseProperties',
39
/* old_bs reference is transparently moved from @s to @s->child */
52
- 'data': { '*aio-max-batch': 'int' } }
40
- bdrv_replace_child_noperm(s->child, s->old_bs);
53
+ 'data': { '*aio-max-batch': 'int',
41
+ bdrv_replace_child_noperm(&s->child, s->old_bs);
54
+ '*thread-pool-min': 'int',
42
bdrv_unref(new_bs);
55
+ '*thread-pool-max': 'int' } }
56
57
##
58
# @IothreadProperties:
59
diff --git a/include/block/aio.h b/include/block/aio.h
60
index XXXXXXX..XXXXXXX 100644
61
--- a/include/block/aio.h
62
+++ b/include/block/aio.h
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
65
QEMUBH *co_schedule_bh;
66
67
+ int thread_pool_min;
68
+ int thread_pool_max;
69
/* Thread pool for performing work and receiving completion callbacks.
70
* Has its own locking.
71
*/
72
@@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
73
void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
74
Error **errp);
75
76
+/**
77
+ * aio_context_set_thread_pool_params:
78
+ * @ctx: the aio context
79
+ * @min: min number of threads to have readily available in the thread pool
80
+ * @min: max number of threads the thread pool can contain
81
+ */
82
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
83
+ int64_t max, Error **errp);
84
#endif
85
diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h
86
index XXXXXXX..XXXXXXX 100644
87
--- a/include/block/thread-pool.h
88
+++ b/include/block/thread-pool.h
89
@@ -XXX,XX +XXX,XX @@
90
91
#include "block/block.h"
92
93
+#define THREAD_POOL_MAX_THREADS_DEFAULT 64
94
+
95
typedef int ThreadPoolFunc(void *opaque);
96
97
typedef struct ThreadPool ThreadPool;
98
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
99
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
100
ThreadPoolFunc *func, void *arg);
101
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
102
+void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx);
103
104
#endif
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBase {
110
111
/* AioContext AIO engine parameters */
112
int64_t aio_max_batch;
113
+
114
+ /* AioContext thread pool parameters */
115
+ int64_t thread_pool_min;
116
+ int64_t thread_pool_max;
117
};
118
#endif
119
diff --git a/event-loop-base.c b/event-loop-base.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/event-loop-base.c
122
+++ b/event-loop-base.c
123
@@ -XXX,XX +XXX,XX @@
124
#include "qemu/osdep.h"
125
#include "qom/object_interfaces.h"
126
#include "qapi/error.h"
127
+#include "block/thread-pool.h"
128
#include "sysemu/event-loop-base.h"
129
130
typedef struct {
131
@@ -XXX,XX +XXX,XX @@ typedef struct {
132
ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
133
} EventLoopBaseParamInfo;
134
135
+static void event_loop_base_instance_init(Object *obj)
136
+{
137
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
138
+
139
+ base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
140
+}
141
+
142
static EventLoopBaseParamInfo aio_max_batch_info = {
143
"aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
144
};
145
+static EventLoopBaseParamInfo thread_pool_min_info = {
146
+ "thread-pool-min", offsetof(EventLoopBase, thread_pool_min),
147
+};
148
+static EventLoopBaseParamInfo thread_pool_max_info = {
149
+ "thread-pool-max", offsetof(EventLoopBase, thread_pool_max),
150
+};
151
152
static void event_loop_base_get_param(Object *obj, Visitor *v,
153
const char *name, void *opaque, Error **errp)
154
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
155
event_loop_base_get_param,
156
event_loop_base_set_param,
157
NULL, &aio_max_batch_info);
158
+ object_class_property_add(klass, "thread-pool-min", "int",
159
+ event_loop_base_get_param,
160
+ event_loop_base_set_param,
161
+ NULL, &thread_pool_min_info);
162
+ object_class_property_add(klass, "thread-pool-max", "int",
163
+ event_loop_base_get_param,
164
+ event_loop_base_set_param,
165
+ NULL, &thread_pool_max_info);
166
}
43
}
167
44
168
static const TypeInfo event_loop_base_info = {
45
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
169
.name = TYPE_EVENT_LOOP_BASE,
46
if (new_bs) {
170
.parent = TYPE_OBJECT,
47
bdrv_ref(new_bs);
171
.instance_size = sizeof(EventLoopBase),
48
}
172
+ .instance_init = event_loop_base_instance_init,
49
- bdrv_replace_child_noperm(child, new_bs);
173
.class_size = sizeof(EventLoopBaseClass),
50
+ bdrv_replace_child_noperm(&child, new_bs);
174
.class_init = event_loop_base_class_init,
51
/* old_bs reference is transparently moved from @child to @s */
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
}
52
}
188
53
189
54
@@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
190
diff --git a/util/aio-posix.c b/util/aio-posix.c
55
return permissions[qapi_perm];
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
}
56
}
220
+
57
221
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
58
-static void bdrv_replace_child_noperm(BdrvChild *child,
222
+ int64_t max, Error **errp)
59
+static void bdrv_replace_child_noperm(BdrvChild **childp,
223
+{
60
BlockDriverState *new_bs)
224
+
225
+ if (min > max || !max || min > INT_MAX || max > INT_MAX) {
226
+ error_setg(errp, "bad thread-pool-min/thread-pool-max values");
227
+ return;
228
+ }
229
+
230
+ ctx->thread_pool_min = min;
231
+ ctx->thread_pool_max = max;
232
+
233
+ if (ctx->thread_pool) {
234
+ thread_pool_update_params(ctx->thread_pool, ctx);
235
+ }
236
+}
237
diff --git a/util/main-loop.c b/util/main-loop.c
238
index XXXXXXX..XXXXXXX 100644
239
--- a/util/main-loop.c
240
+++ b/util/main-loop.c
241
@@ -XXX,XX +XXX,XX @@
242
#include "sysemu/replay.h"
243
#include "qemu/main-loop.h"
244
#include "block/aio.h"
245
+#include "block/thread-pool.h"
246
#include "qemu/error-report.h"
247
#include "qemu/queue.h"
248
#include "qemu/compiler.h"
249
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
250
251
static void main_loop_update_params(EventLoopBase *base, Error **errp)
252
{
61
{
253
+ ERRP_GUARD();
62
+ BdrvChild *child = *childp;
254
+
63
BlockDriverState *old_bs = child->bs;
255
if (!qemu_aio_context) {
64
int new_bs_quiesce_counter;
256
error_setg(errp, "qemu aio context not ready");
65
int drain_saldo;
257
return;
66
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque)
67
BdrvChild *child = *s->child;
68
BlockDriverState *bs = child->bs;
69
70
- bdrv_replace_child_noperm(child, NULL);
71
+ bdrv_replace_child_noperm(s->child, NULL);
72
73
if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
74
bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort);
75
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
258
}
76
}
259
77
260
aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
78
bdrv_ref(child_bs);
261
+ if (*errp) {
79
- bdrv_replace_child_noperm(new_child, child_bs);
262
+ return;
80
+ bdrv_replace_child_noperm(&new_child, child_bs);
263
+ }
81
264
+
82
*child = new_child;
265
+ aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min,
83
266
+ base->thread_pool_max, errp);
84
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
85
return 0;
267
}
86
}
268
87
269
MainLoop *mloop;
88
-static void bdrv_detach_child(BdrvChild *child)
270
diff --git a/util/thread-pool.c b/util/thread-pool.c
89
+static void bdrv_detach_child(BdrvChild **childp)
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
{
90
{
309
ThreadPool *pool = opaque;
91
- BlockDriverState *old_bs = child->bs;
310
@@ -XXX,XX +XXX,XX @@ static void *worker_thread(void *opaque)
92
+ BlockDriverState *old_bs = (*childp)->bs;
311
ret = qemu_sem_timedwait(&pool->sem, 10000);
93
312
qemu_mutex_lock(&pool->lock);
94
- bdrv_replace_child_noperm(child, NULL);
313
pool->idle_threads--;
95
- bdrv_child_free(child);
314
- } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list));
96
+ bdrv_replace_child_noperm(childp, NULL);
315
- if (ret == -1 || pool->stopping) {
97
+ bdrv_child_free(*childp);
316
+ } while (back_to_sleep(pool, ret));
98
317
+ if (ret == -1 || pool->stopping ||
99
if (old_bs) {
318
+ pool->cur_threads > pool->max_threads) {
100
/*
319
break;
101
@@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child)
320
}
102
BlockDriverState *child_bs;
321
103
322
@@ -XXX,XX +XXX,XX @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg)
104
child_bs = child->bs;
323
thread_pool_submit_aio(pool, func, arg, NULL, NULL);
105
- bdrv_detach_child(child);
106
+ bdrv_detach_child(&child);
107
bdrv_unref(child_bs);
324
}
108
}
325
109
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)
370
--
110
--
371
2.35.1
111
2.33.1
112
113
diff view generated by jsdifflib
New patch
1
As of a future patch, bdrv_replace_child_tran() will take a BdrvChild **
2
pointer. Prepare for that by getting such a pointer and using it where
3
applicable, and (dereferenced) as a parameter for
4
bdrv_replace_child_tran().
1
5
6
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
7
Message-Id: <20211111120829.81329-7-hreitz@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20211115145409.176785-7-kwolf@redhat.com>
11
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
12
---
13
block.c | 21 ++++++++++++---------
14
1 file changed, 12 insertions(+), 9 deletions(-)
15
16
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
19
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
21
BdrvChild *child,
22
Transaction *tran)
23
{
24
+ BdrvChild **childp;
25
BdrvRemoveFilterOrCowChild *s;
26
27
- assert(child == bs->backing || child == bs->file);
28
-
29
if (!child) {
30
return;
31
}
32
33
+ if (child == bs->backing) {
34
+ childp = &bs->backing;
35
+ } else if (child == bs->file) {
36
+ childp = &bs->file;
37
+ } else {
38
+ g_assert_not_reached();
39
+ }
40
+
41
if (child->bs) {
42
- bdrv_replace_child_tran(child, NULL, tran);
43
+ bdrv_replace_child_tran(*childp, NULL, tran);
44
}
45
46
s = g_new(BdrvRemoveFilterOrCowChild, 1);
47
*s = (BdrvRemoveFilterOrCowChild) {
48
.child = child,
49
- .is_backing = (child == bs->backing),
50
+ .is_backing = (childp == &bs->backing),
51
};
52
tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
53
54
- if (s->is_backing) {
55
- bs->backing = NULL;
56
- } else {
57
- bs->file = NULL;
58
- }
59
+ *childp = NULL;
60
}
61
62
/*
63
--
64
2.33.1
65
66
diff view generated by jsdifflib
New patch
1
Invoke the transaction drivers' .clean() methods only after all
2
.commit() or .abort() handlers are done.
1
3
4
This makes it easier to have nested transactions where the top-level
5
transactions pass objects to lower transactions that the latter can
6
still use throughout their commit/abort phases, while the top-level
7
transaction keeps a reference that is released in its .clean() method.
8
9
(Before this commit, that is also possible, but the top-level
10
transaction would need to take care to invoke tran_add() before the
11
lower-level transaction does. This commit makes the ordering
12
irrelevant, which is just a bit nicer.)
13
14
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
15
Message-Id: <20211111120829.81329-8-hreitz@redhat.com>
16
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Message-Id: <20211115145409.176785-8-kwolf@redhat.com>
19
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
20
---
21
include/qemu/transactions.h | 3 +++
22
util/transactions.c | 8 ++++++--
23
2 files changed, 9 insertions(+), 2 deletions(-)
24
25
diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h
26
index XXXXXXX..XXXXXXX 100644
27
--- a/include/qemu/transactions.h
28
+++ b/include/qemu/transactions.h
29
@@ -XXX,XX +XXX,XX @@
30
* tran_create(), call your "prepare" functions on it, and finally call
31
* tran_abort() or tran_commit() to finalize the transaction by corresponding
32
* finalization actions in reverse order.
33
+ *
34
+ * The clean() functions registered by the drivers in a transaction are called
35
+ * last, after all abort() or commit() functions have been called.
36
*/
37
38
#ifndef QEMU_TRANSACTIONS_H
39
diff --git a/util/transactions.c b/util/transactions.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/util/transactions.c
42
+++ b/util/transactions.c
43
@@ -XXX,XX +XXX,XX @@ void tran_abort(Transaction *tran)
44
{
45
TransactionAction *act, *next;
46
47
- QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
48
+ QSLIST_FOREACH(act, &tran->actions, entry) {
49
if (act->drv->abort) {
50
act->drv->abort(act->opaque);
51
}
52
+ }
53
54
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
55
if (act->drv->clean) {
56
act->drv->clean(act->opaque);
57
}
58
@@ -XXX,XX +XXX,XX @@ void tran_commit(Transaction *tran)
59
{
60
TransactionAction *act, *next;
61
62
- QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
63
+ QSLIST_FOREACH(act, &tran->actions, entry) {
64
if (act->drv->commit) {
65
act->drv->commit(act->opaque);
66
}
67
+ }
68
69
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
70
if (act->drv->clean) {
71
act->drv->clean(act->opaque);
72
}
73
--
74
2.33.1
75
76
diff view generated by jsdifflib
New patch
1
1
As of a future commit, bdrv_replace_child_noperm() will clear the
2
indirect BdrvChild pointer passed to it if the new child BDS is NULL.
3
bdrv_replace_child_tran() will want to let it do that, but revert this
4
change in its abort handler. For that, we need to have it receive a
5
BdrvChild ** pointer, too, and keep it stored in the
6
BdrvReplaceChildState object that we attach to the transaction.
7
8
Note that we do not need to store it in the BdrvReplaceChildState when
9
new_bs is not NULL, because then there is nothing to revert. This is
10
important so that bdrv_replace_node_noperm() can pass a pointer to a
11
loop-local variable to bdrv_replace_child_tran() without worrying that
12
this pointer will outlive one loop iteration.
13
14
(Of course, for that to work, bdrv_replace_node_noperm() and in turn
15
bdrv_replace_node() and its relatives may not be called with a NULL @to
16
node. Luckily, they already are not, but now we should assert this.)
17
18
bdrv_remove_file_or_backing_child() on the other hand needs to ensure
19
that the indirect pointer it passes will stay valid for the duration of
20
the transaction. Ensure this by keeping a strong reference to the BDS
21
whose &bs->backing or &bs->file it passes to bdrv_replace_child_tran(),
22
and giving up that reference only in the transaction .clean() handler.
23
24
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
25
Message-Id: <20211111120829.81329-9-hreitz@redhat.com>
26
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
27
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
28
Message-Id: <20211115145409.176785-9-kwolf@redhat.com>
29
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
30
---
31
block.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
32
1 file changed, 73 insertions(+), 10 deletions(-)
33
34
diff --git a/block.c b/block.c
35
index XXXXXXX..XXXXXXX 100644
36
--- a/block.c
37
+++ b/block.c
38
@@ -XXX,XX +XXX,XX @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
39
40
typedef struct BdrvReplaceChildState {
41
BdrvChild *child;
42
+ BdrvChild **childp;
43
BlockDriverState *old_bs;
44
} BdrvReplaceChildState;
45
46
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque)
47
BdrvReplaceChildState *s = opaque;
48
BlockDriverState *new_bs = s->child->bs;
49
50
- /* old_bs reference is transparently moved from @s to @s->child */
51
+ /*
52
+ * old_bs reference is transparently moved from @s to s->child.
53
+ *
54
+ * Pass &s->child here instead of s->childp, because:
55
+ * (1) s->old_bs must be non-NULL, so bdrv_replace_child_noperm() will not
56
+ * modify the BdrvChild * pointer we indirectly pass to it, i.e. it
57
+ * will not modify s->child. From that perspective, it does not matter
58
+ * whether we pass s->childp or &s->child.
59
+ * (TODO: Right now, bdrv_replace_child_noperm() never modifies that
60
+ * pointer anyway (though it will in the future), so at this point it
61
+ * absolutely does not matter whether we pass s->childp or &s->child.)
62
+ * (2) If new_bs is not NULL, s->childp will be NULL. We then cannot use
63
+ * it here.
64
+ * (3) If new_bs is NULL, *s->childp will have been NULLed by
65
+ * bdrv_replace_child_tran()'s bdrv_replace_child_noperm() call, and we
66
+ * must not pass a NULL *s->childp here.
67
+ * (TODO: In its current state, bdrv_replace_child_noperm() will not
68
+ * have NULLed *s->childp, so this does not apply yet. It will in the
69
+ * future.)
70
+ *
71
+ * So whether new_bs was NULL or not, we cannot pass s->childp here; and in
72
+ * any case, there is no reason to pass it anyway.
73
+ */
74
bdrv_replace_child_noperm(&s->child, s->old_bs);
75
bdrv_unref(new_bs);
76
}
77
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = {
78
* Note: real unref of old_bs is done only on commit.
79
*
80
* The function doesn't update permissions, caller is responsible for this.
81
+ *
82
+ * Note that if new_bs == NULL, @childp is stored in a state object attached
83
+ * to @tran, so that the old child can be reinstated in the abort handler.
84
+ * Therefore, if @new_bs can be NULL, @childp must stay valid until the
85
+ * transaction is committed or aborted.
86
+ *
87
+ * (TODO: The reinstating does not happen yet, but it will once
88
+ * bdrv_replace_child_noperm() NULLs *childp when new_bs is NULL.)
89
*/
90
-static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
91
+static void bdrv_replace_child_tran(BdrvChild **childp,
92
+ BlockDriverState *new_bs,
93
Transaction *tran)
94
{
95
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
96
*s = (BdrvReplaceChildState) {
97
- .child = child,
98
- .old_bs = child->bs,
99
+ .child = *childp,
100
+ .childp = new_bs == NULL ? childp : NULL,
101
+ .old_bs = (*childp)->bs,
102
};
103
tran_add(tran, &bdrv_replace_child_drv, s);
104
105
if (new_bs) {
106
bdrv_ref(new_bs);
107
}
108
- bdrv_replace_child_noperm(&child, new_bs);
109
- /* old_bs reference is transparently moved from @child to @s */
110
+ bdrv_replace_child_noperm(childp, new_bs);
111
+ /* old_bs reference is transparently moved from *childp to @s */
112
}
113
114
/*
115
@@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
116
117
typedef struct BdrvRemoveFilterOrCowChild {
118
BdrvChild *child;
119
+ BlockDriverState *bs;
120
bool is_backing;
121
} BdrvRemoveFilterOrCowChild;
122
123
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child_commit(void *opaque)
124
bdrv_child_free(s->child);
125
}
126
127
+static void bdrv_remove_filter_or_cow_child_clean(void *opaque)
128
+{
129
+ BdrvRemoveFilterOrCowChild *s = opaque;
130
+
131
+ /* Drop the bs reference after the transaction is done */
132
+ bdrv_unref(s->bs);
133
+ g_free(s);
134
+}
135
+
136
static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
137
.abort = bdrv_remove_filter_or_cow_child_abort,
138
.commit = bdrv_remove_filter_or_cow_child_commit,
139
- .clean = g_free,
140
+ .clean = bdrv_remove_filter_or_cow_child_clean,
141
};
142
143
/*
144
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
145
return;
146
}
147
148
+ /*
149
+ * Keep a reference to @bs so @childp will stay valid throughout the
150
+ * transaction (required by bdrv_replace_child_tran())
151
+ */
152
+ bdrv_ref(bs);
153
if (child == bs->backing) {
154
childp = &bs->backing;
155
} else if (child == bs->file) {
156
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
157
}
158
159
if (child->bs) {
160
- bdrv_replace_child_tran(*childp, NULL, tran);
161
+ bdrv_replace_child_tran(childp, NULL, tran);
162
}
163
164
s = g_new(BdrvRemoveFilterOrCowChild, 1);
165
*s = (BdrvRemoveFilterOrCowChild) {
166
.child = child,
167
+ .bs = bs,
168
.is_backing = (childp == &bs->backing),
169
};
170
tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
171
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
172
{
173
BdrvChild *c, *next;
174
175
+ assert(to != NULL);
176
+
177
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
178
assert(c->bs == from);
179
if (!should_update_child(c, to)) {
180
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
181
c->name, from->node_name);
182
return -EPERM;
183
}
184
- bdrv_replace_child_tran(c, to, tran);
185
+
186
+ /*
187
+ * Passing a pointer to the local variable @c is fine here, because
188
+ * @to is not NULL, and so &c will not be attached to the transaction.
189
+ */
190
+ bdrv_replace_child_tran(&c, to, tran);
191
}
192
193
return 0;
194
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
195
*
196
* With @detach_subchain=true @to must be in a backing chain of @from. In this
197
* case backing link of the cow-parent of @to is removed.
198
+ *
199
+ * @to must not be NULL.
200
*/
201
static int bdrv_replace_node_common(BlockDriverState *from,
202
BlockDriverState *to,
203
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
204
BlockDriverState *to_cow_parent = NULL;
205
int ret;
206
207
+ assert(to != NULL);
208
+
209
if (detach_subchain) {
210
assert(bdrv_chain_contains(from, to));
211
assert(from != to);
212
@@ -XXX,XX +XXX,XX @@ out:
213
return ret;
214
}
215
216
+/**
217
+ * Replace node @from by @to (where neither may be NULL).
218
+ */
219
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
220
Error **errp)
221
{
222
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
223
bdrv_drained_begin(old_bs);
224
bdrv_drained_begin(new_bs);
225
226
- bdrv_replace_child_tran(child, new_bs, tran);
227
+ bdrv_replace_child_tran(&child, new_bs, tran);
228
229
found = g_hash_table_new(NULL, NULL);
230
refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs);
231
--
232
2.33.1
233
234
diff view generated by jsdifflib
New patch
1
1
In most of the block layer, especially when traversing down from other
2
BlockDriverStates, we assume that BdrvChild.bs can never be NULL. When
3
it becomes NULL, it is expected that the corresponding BdrvChild pointer
4
also becomes NULL and the BdrvChild object is freed.
5
6
Therefore, once bdrv_replace_child_noperm() sets the BdrvChild.bs
7
pointer to NULL, it should also immediately set the corresponding
8
BdrvChild pointer (like bs->file or bs->backing) to NULL.
9
10
In that context, it also makes sense for this function to free the
11
child. Sometimes we cannot do so, though, because it is called in a
12
transactional context where the caller might still want to reinstate the
13
child in the abort branch (and free it only on commit), so this behavior
14
has to remain optional.
15
16
In bdrv_replace_child_tran()'s abort handler, we now rely on the fact
17
that the BdrvChild passed to bdrv_replace_child_tran() must have had a
18
non-NULL .bs pointer initially. Make a note of that and assert it.
19
20
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
21
Message-Id: <20211111120829.81329-10-hreitz@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
Message-Id: <20211115145409.176785-10-kwolf@redhat.com>
24
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
25
---
26
block.c | 102 +++++++++++++++++++++++++++++++++++++++++++-------------
27
1 file changed, 79 insertions(+), 23 deletions(-)
28
29
diff --git a/block.c b/block.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
32
+++ b/block.c
33
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
34
static bool bdrv_recurse_has_child(BlockDriverState *bs,
35
BlockDriverState *child);
36
37
+static void bdrv_child_free(BdrvChild *child);
38
static void bdrv_replace_child_noperm(BdrvChild **child,
39
- BlockDriverState *new_bs);
40
+ BlockDriverState *new_bs,
41
+ bool free_empty_child);
42
static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
43
BdrvChild *child,
44
Transaction *tran);
45
@@ -XXX,XX +XXX,XX @@ typedef struct BdrvReplaceChildState {
46
BdrvChild *child;
47
BdrvChild **childp;
48
BlockDriverState *old_bs;
49
+ bool free_empty_child;
50
} BdrvReplaceChildState;
51
52
static void bdrv_replace_child_commit(void *opaque)
53
{
54
BdrvReplaceChildState *s = opaque;
55
56
+ if (s->free_empty_child && !s->child->bs) {
57
+ bdrv_child_free(s->child);
58
+ }
59
bdrv_unref(s->old_bs);
60
}
61
62
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque)
63
* modify the BdrvChild * pointer we indirectly pass to it, i.e. it
64
* will not modify s->child. From that perspective, it does not matter
65
* whether we pass s->childp or &s->child.
66
- * (TODO: Right now, bdrv_replace_child_noperm() never modifies that
67
- * pointer anyway (though it will in the future), so at this point it
68
- * absolutely does not matter whether we pass s->childp or &s->child.)
69
* (2) If new_bs is not NULL, s->childp will be NULL. We then cannot use
70
* it here.
71
* (3) If new_bs is NULL, *s->childp will have been NULLed by
72
* bdrv_replace_child_tran()'s bdrv_replace_child_noperm() call, and we
73
* must not pass a NULL *s->childp here.
74
- * (TODO: In its current state, bdrv_replace_child_noperm() will not
75
- * have NULLed *s->childp, so this does not apply yet. It will in the
76
- * future.)
77
*
78
* So whether new_bs was NULL or not, we cannot pass s->childp here; and in
79
* any case, there is no reason to pass it anyway.
80
*/
81
- bdrv_replace_child_noperm(&s->child, s->old_bs);
82
+ bdrv_replace_child_noperm(&s->child, s->old_bs, true);
83
+ /*
84
+ * The child was pre-existing, so s->old_bs must be non-NULL, and
85
+ * s->child thus must not have been freed
86
+ */
87
+ assert(s->child != NULL);
88
+ if (!new_bs) {
89
+ /* As described above, *s->childp was cleared, so restore it */
90
+ assert(s->childp != NULL);
91
+ *s->childp = s->child;
92
+ }
93
bdrv_unref(new_bs);
94
}
95
96
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = {
97
*
98
* The function doesn't update permissions, caller is responsible for this.
99
*
100
+ * (*childp)->bs must not be NULL.
101
+ *
102
* Note that if new_bs == NULL, @childp is stored in a state object attached
103
* to @tran, so that the old child can be reinstated in the abort handler.
104
* Therefore, if @new_bs can be NULL, @childp must stay valid until the
105
* transaction is committed or aborted.
106
*
107
- * (TODO: The reinstating does not happen yet, but it will once
108
- * bdrv_replace_child_noperm() NULLs *childp when new_bs is NULL.)
109
+ * If @free_empty_child is true and @new_bs is NULL, the BdrvChild is
110
+ * freed (on commit). @free_empty_child should only be false if the
111
+ * caller will free the BDrvChild themselves (which may be important
112
+ * if this is in turn called in another transactional context).
113
*/
114
static void bdrv_replace_child_tran(BdrvChild **childp,
115
BlockDriverState *new_bs,
116
- Transaction *tran)
117
+ Transaction *tran,
118
+ bool free_empty_child)
119
{
120
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
121
*s = (BdrvReplaceChildState) {
122
.child = *childp,
123
.childp = new_bs == NULL ? childp : NULL,
124
.old_bs = (*childp)->bs,
125
+ .free_empty_child = free_empty_child,
126
};
127
tran_add(tran, &bdrv_replace_child_drv, s);
128
129
+ /* The abort handler relies on this */
130
+ assert(s->old_bs != NULL);
131
+
132
if (new_bs) {
133
bdrv_ref(new_bs);
134
}
135
- bdrv_replace_child_noperm(childp, new_bs);
136
+ /*
137
+ * Pass free_empty_child=false, we will free the child (if
138
+ * necessary) in bdrv_replace_child_commit() (if our
139
+ * @free_empty_child parameter was true).
140
+ */
141
+ bdrv_replace_child_noperm(childp, new_bs, false);
142
/* old_bs reference is transparently moved from *childp to @s */
143
}
144
145
@@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
146
return permissions[qapi_perm];
147
}
148
149
+/**
150
+ * Replace (*childp)->bs by @new_bs.
151
+ *
152
+ * If @new_bs is NULL, *childp will be set to NULL, too: BDS parents
153
+ * generally cannot handle a BdrvChild with .bs == NULL, so clearing
154
+ * BdrvChild.bs should generally immediately be followed by the
155
+ * BdrvChild pointer being cleared as well.
156
+ *
157
+ * If @free_empty_child is true and @new_bs is NULL, the BdrvChild is
158
+ * freed. @free_empty_child should only be false if the caller will
159
+ * free the BdrvChild themselves (this may be important in a
160
+ * transactional context, where it may only be freed on commit).
161
+ */
162
static void bdrv_replace_child_noperm(BdrvChild **childp,
163
- BlockDriverState *new_bs)
164
+ BlockDriverState *new_bs,
165
+ bool free_empty_child)
166
{
167
BdrvChild *child = *childp;
168
BlockDriverState *old_bs = child->bs;
169
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild **childp,
170
}
171
172
child->bs = new_bs;
173
+ if (!new_bs) {
174
+ *childp = NULL;
175
+ }
176
177
if (new_bs) {
178
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
179
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild **childp,
180
bdrv_parent_drained_end_single(child);
181
drain_saldo++;
182
}
183
+
184
+ if (free_empty_child && !child->bs) {
185
+ bdrv_child_free(child);
186
+ }
187
}
188
189
/**
190
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque)
191
BdrvChild *child = *s->child;
192
BlockDriverState *bs = child->bs;
193
194
- bdrv_replace_child_noperm(s->child, NULL);
195
+ /*
196
+ * Pass free_empty_child=false, because we still need the child
197
+ * for the AioContext operations on the parent below; those
198
+ * BdrvChildClass methods all work on a BdrvChild object, so we
199
+ * need to keep it as an empty shell (after this function, it will
200
+ * not be attached to any parent, and it will not have a .bs).
201
+ */
202
+ bdrv_replace_child_noperm(s->child, NULL, false);
203
204
if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
205
bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort);
206
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque)
207
208
bdrv_unref(bs);
209
bdrv_child_free(child);
210
- *s->child = NULL;
211
}
212
213
static TransactionActionDrv bdrv_attach_child_common_drv = {
214
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
215
}
216
217
bdrv_ref(child_bs);
218
- bdrv_replace_child_noperm(&new_child, child_bs);
219
+ bdrv_replace_child_noperm(&new_child, child_bs, true);
220
+ /* child_bs was non-NULL, so new_child must not have been freed */
221
+ assert(new_child != NULL);
222
223
*child = new_child;
224
225
@@ -XXX,XX +XXX,XX @@ static void bdrv_detach_child(BdrvChild **childp)
226
{
227
BlockDriverState *old_bs = (*childp)->bs;
228
229
- bdrv_replace_child_noperm(childp, NULL);
230
- bdrv_child_free(*childp);
231
+ bdrv_replace_child_noperm(childp, NULL, true);
232
233
if (old_bs) {
234
/*
235
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
236
}
237
238
if (child->bs) {
239
- bdrv_replace_child_tran(childp, NULL, tran);
240
+ /*
241
+ * Pass free_empty_child=false, we will free the child in
242
+ * bdrv_remove_filter_or_cow_child_commit()
243
+ */
244
+ bdrv_replace_child_tran(childp, NULL, tran, false);
245
}
246
247
s = g_new(BdrvRemoveFilterOrCowChild, 1);
248
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
249
.is_backing = (childp == &bs->backing),
250
};
251
tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
252
-
253
- *childp = NULL;
254
}
255
256
/*
257
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
258
* Passing a pointer to the local variable @c is fine here, because
259
* @to is not NULL, and so &c will not be attached to the transaction.
260
*/
261
- bdrv_replace_child_tran(&c, to, tran);
262
+ bdrv_replace_child_tran(&c, to, tran, true);
263
}
264
265
return 0;
266
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
267
bdrv_drained_begin(old_bs);
268
bdrv_drained_begin(new_bs);
269
270
- bdrv_replace_child_tran(&child, new_bs, tran);
271
+ bdrv_replace_child_tran(&child, new_bs, tran, true);
272
+ /* @new_bs must have been non-NULL, so @child must not have been freed */
273
+ assert(child != NULL);
274
275
found = g_hash_table_new(NULL, NULL);
276
refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs);
277
--
278
2.33.1
279
280
diff view generated by jsdifflib
New patch
1
See the comment for why this is necessary.
1
2
3
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
4
Message-Id: <20211111120829.81329-11-hreitz@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20211115145409.176785-11-kwolf@redhat.com>
7
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
8
---
9
tests/qemu-iotests/030 | 11 ++++++++++-
10
1 file changed, 10 insertions(+), 1 deletion(-)
11
12
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
13
index XXXXXXX..XXXXXXX 100755
14
--- a/tests/qemu-iotests/030
15
+++ b/tests/qemu-iotests/030
16
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
17
speed=1024)
18
self.assert_qmp(result, 'return', {})
19
20
- for job in pending_jobs:
21
+ # Do this in reverse: After unthrottling them, some jobs may finish
22
+ # before we have unthrottled all of them. This will drain their
23
+ # subgraph, and this will make jobs above them advance (despite those
24
+ # jobs on top being throttled). In the worst case, all jobs below the
25
+ # top one are finished before we can unthrottle it, and this makes it
26
+ # advance so far that it completes before we can unthrottle it - which
27
+ # results in an error.
28
+ # Starting from the top (i.e. in reverse) does not have this problem:
29
+ # When a job finishes, the ones below it are not advanced.
30
+ for job in reversed(pending_jobs):
31
result = self.vm.qmp('block-job-set-speed', device=job, speed=0)
32
self.assert_qmp(result, 'return', {})
33
34
--
35
2.33.1
36
37
diff view generated by jsdifflib
New patch
1
From: Kevin Wolf <kwolf@redhat.com>
1
2
3
While introducing a non-QemuOpts code path for device creation for JSON
4
-device, we noticed that QMP device_add doesn't check its input
5
correctly (accepting arguments that should have been rejected), and that
6
users may be relying on this behaviour (libvirt did until it was fixed
7
recently).
8
9
Let's use a deprecation period before we fix this bug in QEMU to avoid
10
nasty surprises for users.
11
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20211111143530.18985-1-kwolf@redhat.com>
14
Reviewed-by: Markus Armbruster <armbru@redhat.com>
15
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Message-Id: <20211115145409.176785-12-kwolf@redhat.com>
18
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
19
---
20
docs/about/deprecated.rst | 14 ++++++++++++++
21
1 file changed, 14 insertions(+)
22
23
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
24
index XXXXXXX..XXXXXXX 100644
25
--- a/docs/about/deprecated.rst
26
+++ b/docs/about/deprecated.rst
27
@@ -XXX,XX +XXX,XX @@ options are removed in favor of using explicit ``blockdev-create`` and
28
``blockdev-add`` calls. See :doc:`/interop/live-block-operations` for
29
details.
30
31
+Incorrectly typed ``device_add`` arguments (since 6.2)
32
+''''''''''''''''''''''''''''''''''''''''''''''''''''''
33
+
34
+Due to shortcomings in the internal implementation of ``device_add``, QEMU
35
+incorrectly accepts certain invalid arguments: Any object or list arguments are
36
+silently ignored. Other argument types are not checked, but an implicit
37
+conversion happens, so that e.g. string values can be assigned to integer
38
+device properties or vice versa.
39
+
40
+This is a bug in QEMU that will be fixed in the future so that previously
41
+accepted incorrect commands will return an error. Users should make sure that
42
+all arguments passed to ``device_add`` are consistent with the documented
43
+property types.
44
+
45
System accelerators
46
-------------------
47
48
--
49
2.33.1
50
51
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Reported by Coverity (CID 1465222).
4
5
Fixes: 4a1d937796de0fecd8b22d7dbebf87f38e8282fd ("softmmu/qdev-monitor: add error handling in qdev_set_id")
6
Cc: Damien Hedde <damien.hedde@greensocs.com>
7
Cc: Kevin Wolf <kwolf@redhat.com>
8
Cc: Michael S. Tsirkin <mst@redhat.com>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Message-Id: <20211102163342.31162-1-stefanha@redhat.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
13
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
14
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
15
Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>
16
Reviewed-by: Markus Armbruster <armbru@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Message-Id: <20211115145409.176785-14-kwolf@redhat.com>
19
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
20
---
21
softmmu/qdev-monitor.c | 2 +-
22
1 file changed, 1 insertion(+), 1 deletion(-)
23
24
diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c
25
index XXXXXXX..XXXXXXX 100644
26
--- a/softmmu/qdev-monitor.c
27
+++ b/softmmu/qdev-monitor.c
28
@@ -XXX,XX +XXX,XX @@ const char *qdev_set_id(DeviceState *dev, char *id, Error **errp)
29
if (prop) {
30
dev->id = id;
31
} else {
32
- g_free(id);
33
error_setg(errp, "Duplicate device ID '%s'", id);
34
+ g_free(id);
35
return NULL;
36
}
37
} else {
38
--
39
2.33.1
40
41
diff view generated by jsdifflib
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
1
From: Kevin Wolf <kwolf@redhat.com>
2
2
3
'event-loop-base' provides basic property handling for all 'AioContext'
3
At the end of a reopen, we already call bdrv_refresh_limits(), which
4
based event loops. So let's define a new 'MainLoopClass' that inherits
4
should update bs->request_alignment according to the new file
5
from it. This will permit tweaking the main loop's properties through
5
descriptor. However, raw_probe_alignment() relies on s->needs_alignment
6
qapi as well as through the command line using the '-object' keyword[1].
6
and just uses 1 if it isn't set. We neglected to update this field, so
7
Only one instance of 'MainLoopClass' might be created at any time.
7
starting with cache=writeback and then reopening with cache=none means
8
that we get an incorrect bs->request_alignment == 1 and unaligned
9
requests fail instead of being automatically aligned.
8
10
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
11
Fix this by recalculating s->needs_alignment in raw_refresh_limits()
10
mark 'MainLoop' as non-deletable.
12
before calling raw_probe_alignment().
11
13
12
[1] For example:
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
-object main-loop,id=main-loop,aio-max-batch=<value>
15
Message-Id: <20211104113109.56336-1-kwolf@redhat.com>
16
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
17
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Message-Id: <20211115145409.176785-13-kwolf@redhat.com>
20
[hreitz: Fix iotest 142 for block sizes greater than 512 by operating on
21
a file with a size of 1 MB]
22
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
23
Message-Id: <20211116101431.105252-1-hreitz@redhat.com>
24
---
25
block/file-posix.c | 20 ++++++++++++++++----
26
tests/qemu-iotests/142 | 29 +++++++++++++++++++++++++++++
27
tests/qemu-iotests/142.out | 18 ++++++++++++++++++
28
3 files changed, 63 insertions(+), 4 deletions(-)
14
29
15
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
30
diff --git a/block/file-posix.c b/block/file-posix.c
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
index XXXXXXX..XXXXXXX 100644
31
--- a/qapi/qom.json
32
--- a/block/file-posix.c
32
+++ b/qapi/qom.json
33
+++ b/block/file-posix.c
33
@@ -XXX,XX +XXX,XX @@
34
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
34
'*poll-grow': 'int',
35
int page_cache_inconsistent; /* errno from fdatasync failure */
35
'*poll-shrink': 'int' } }
36
bool has_fallocate;
36
37
bool needs_alignment;
37
+##
38
+ bool force_alignment;
38
+# @MainLoopProperties:
39
bool drop_cache;
39
+#
40
bool check_cache_dropped;
40
+# Properties for the main-loop object.
41
struct {
41
+#
42
@@ -XXX,XX +XXX,XX @@ static bool dio_byte_aligned(int fd)
42
+# Since: 7.1
43
return false;
43
+##
44
}
44
+{ 'struct': 'MainLoopProperties',
45
45
+ 'base': 'EventLoopBaseProperties',
46
+static bool raw_needs_alignment(BlockDriverState *bs)
46
+ 'data': {} }
47
+{
48
+ BDRVRawState *s = bs->opaque;
47
+
49
+
48
##
50
+ if ((bs->open_flags & BDRV_O_NOCACHE) != 0 && !dio_byte_aligned(s->fd)) {
49
# @MemoryBackendProperties:
51
+ return true;
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
+ }
52
+ }
133
+
53
+
134
+ return true;
54
+ return s->force_alignment;
135
+}
55
+}
136
+
56
+
137
static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
57
/* Check if read is allowed with given memory buffer and length.
138
{
58
*
139
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
59
* This function is used to check O_DIRECT memory buffer and request alignment.
140
ucc->complete = event_loop_base_complete;
60
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
141
+ ucc->can_be_deleted = event_loop_base_can_be_deleted;
61
142
62
s->has_discard = true;
143
object_class_property_add(klass, "aio-max-batch", "int",
63
s->has_write_zeroes = true;
144
event_loop_base_get_param,
64
- if ((bs->open_flags & BDRV_O_NOCACHE) != 0 && !dio_byte_aligned(s->fd)) {
145
diff --git a/util/main-loop.c b/util/main-loop.c
65
- s->needs_alignment = true;
66
- }
67
68
if (fstat(s->fd, &st) < 0) {
69
ret = -errno;
70
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
71
* so QEMU makes sure all IO operations on the device are aligned
72
* to sector size, or else FreeBSD will reject them with EINVAL.
73
*/
74
- s->needs_alignment = true;
75
+ s->force_alignment = true;
76
}
77
#endif
78
+ s->needs_alignment = raw_needs_alignment(bs);
79
80
#ifdef CONFIG_XFS
81
if (platform_test_xfs_fd(s->fd)) {
82
@@ -XXX,XX +XXX,XX @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
83
BDRVRawState *s = bs->opaque;
84
struct stat st;
85
86
+ s->needs_alignment = raw_needs_alignment(bs);
87
raw_probe_alignment(bs, s->fd, errp);
88
+
89
bs->bl.min_mem_alignment = s->buf_align;
90
bs->bl.opt_mem_alignment = MAX(s->buf_align, qemu_real_host_page_size);
91
92
diff --git a/tests/qemu-iotests/142 b/tests/qemu-iotests/142
93
index XXXXXXX..XXXXXXX 100755
94
--- a/tests/qemu-iotests/142
95
+++ b/tests/qemu-iotests/142
96
@@ -XXX,XX +XXX,XX @@ info block backing-file"
97
98
echo "$hmp_cmds" | run_qemu -drive "$files","$ids" | grep "Cache"
99
100
+echo
101
+echo "--- Alignment after changing O_DIRECT ---"
102
+echo
103
+
104
+# Directly test the protocol level: Can unaligned requests succeed even if
105
+# O_DIRECT was only enabled through a reopen and vice versa?
106
+
107
+# Ensure image size is a multiple of the sector size (required for O_DIRECT)
108
+$QEMU_IMG create -f file "$TEST_IMG" 1M | _filter_img_create
109
+
110
+# And write some data (not strictly necessary, but it feels better to actually
111
+# have something to be read)
112
+$QEMU_IO -f file -c 'write 0 4096' "$TEST_IMG" | _filter_qemu_io
113
+
114
+$QEMU_IO --cache=writeback -f file $TEST_IMG <<EOF | _filter_qemu_io
115
+read 42 42
116
+reopen -o cache.direct=on
117
+read 42 42
118
+reopen -o cache.direct=off
119
+read 42 42
120
+EOF
121
+$QEMU_IO --cache=none -f file $TEST_IMG <<EOF | _filter_qemu_io
122
+read 42 42
123
+reopen -o cache.direct=off
124
+read 42 42
125
+reopen -o cache.direct=on
126
+read 42 42
127
+EOF
128
+
129
# success, all done
130
echo "*** done"
131
rm -f $seq.full
132
diff --git a/tests/qemu-iotests/142.out b/tests/qemu-iotests/142.out
146
index XXXXXXX..XXXXXXX 100644
133
index XXXXXXX..XXXXXXX 100644
147
--- a/util/main-loop.c
134
--- a/tests/qemu-iotests/142.out
148
+++ b/util/main-loop.c
135
+++ b/tests/qemu-iotests/142.out
149
@@ -XXX,XX +XXX,XX @@
136
@@ -XXX,XX +XXX,XX @@ cache.no-flush=on on backing-file
150
#include "qemu/error-report.h"
137
Cache mode: writeback
151
#include "qemu/queue.h"
138
Cache mode: writeback, direct
152
#include "qemu/compiler.h"
139
Cache mode: writeback, ignore flushes
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
+
140
+
168
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
141
+--- Alignment after changing O_DIRECT ---
169
+}
170
+
142
+
171
+MainLoop *mloop;
143
+Formatting 'TEST_DIR/t.IMGFMT', fmt=file size=1048576
172
+
144
+wrote 4096/4096 bytes at offset 0
173
+static void main_loop_init(EventLoopBase *base, Error **errp)
145
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
174
+{
146
+read 42/42 bytes at offset 42
175
+ MainLoop *m = MAIN_LOOP(base);
147
+42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
176
+
148
+read 42/42 bytes at offset 42
177
+ if (mloop) {
149
+42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
178
+ error_setg(errp, "only one main-loop instance allowed");
150
+read 42/42 bytes at offset 42
179
+ return;
151
+42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
180
+ }
152
+read 42/42 bytes at offset 42
181
+
153
+42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
182
+ main_loop_update_params(base, errp);
154
+read 42/42 bytes at offset 42
183
+
155
+42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
184
+ mloop = m;
156
+read 42/42 bytes at offset 42
185
+ return;
157
+42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
186
+}
158
*** done
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
--
159
--
220
2.35.1
160
2.33.1
161
162
diff view generated by jsdifflib