1
The following changes since commit 1be5a765c08cee3a9587c8a8d3fc2ea247b13f9c:
1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
2
2
3
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging (2022-04-19 18:22:16 -0700)
3
Merge tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu into staging (2022-05-04 03:42:49 -0700)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://gitlab.com/hreitz/qemu.git tags/pull-block-2022-04-20
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
8
8
9
for you to fetch changes up to 0423f75351ab83b844a31349218b0eadd830e07a:
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
10
10
11
qcow2: Add errp to rebuild_refcount_structure() (2022-04-20 12:09:17 +0200)
11
util/event-loop-base: Introduce options to set the thread pool size (2022-05-04 17:02:19 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches:
14
Pull request
15
- Some changes for qcow2's refcount repair algorithm to make it work for
15
16
qcow2 images stored on block devices
16
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
17
- Skip test cases that require zstd when support for it is missing
17
used for async I/O.
18
- Some refactoring in the iotests' meson.build
19
18
20
----------------------------------------------------------------
19
----------------------------------------------------------------
21
Hanna Reitz (6):
22
iotests.py: Add supports_qcow2_zstd_compression()
23
iotests/065: Check for zstd support
24
iotests/303: Check for zstd support
25
qcow2: Improve refcount structure rebuilding
26
iotests/108: Test new refcount rebuild algorithm
27
qcow2: Add errp to rebuild_refcount_structure()
28
20
29
Thomas Huth (2):
21
Nicolas Saenz Julienne (3):
30
tests/qemu-iotests/meson.build: Improve the indentation
22
Introduce event-loop-base abstract class
31
tests/qemu-iotests: Move the bash and sanitizer checks to meson.build
23
util/main-loop: Introduce the main loop into QOM
24
util/event-loop-base: Introduce options to set the thread pool size
32
25
33
block/qcow2-refcount.c | 353 +++++++++++++++++++++++----------
26
qapi/qom.json | 43 ++++++++--
34
tests/check-block.sh | 26 ---
27
meson.build | 26 +++---
35
tests/qemu-iotests/065 | 24 ++-
28
include/block/aio.h | 10 +++
36
tests/qemu-iotests/108 | 259 +++++++++++++++++++++++-
29
include/block/thread-pool.h | 3 +
37
tests/qemu-iotests/108.out | 81 ++++++++
30
include/qemu/main-loop.h | 10 +++
38
tests/qemu-iotests/303 | 4 +-
31
include/sysemu/event-loop-base.h | 41 +++++++++
39
tests/qemu-iotests/iotests.py | 20 ++
32
include/sysemu/iothread.h | 6 +-
40
tests/qemu-iotests/meson.build | 73 ++++---
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
41
8 files changed, 673 insertions(+), 167 deletions(-)
34
iothread.c | 68 +++++----------
35
util/aio-posix.c | 1 +
36
util/async.c | 20 +++++
37
util/main-loop.c | 65 ++++++++++++++
38
util/thread-pool.c | 55 +++++++++++-
39
13 files changed, 419 insertions(+), 69 deletions(-)
40
create mode 100644 include/sysemu/event-loop-base.h
41
create mode 100644 event-loop-base.c
42
42
43
--
43
--
44
2.35.1
44
2.35.1
diff view generated by jsdifflib
Deleted patch
1
From: Thomas Huth <thuth@redhat.com>
2
1
3
By using subdir_done(), we can get rid of one level of indentation
4
in this file. This will make it easier to add more conditions to
5
skip the iotests in future patches.
6
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
8
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
9
Signed-off-by: Thomas Huth <thuth@redhat.com>
10
Message-Id: <20220223093840.2515281-3-thuth@redhat.com>
11
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
12
---
13
tests/qemu-iotests/meson.build | 61 ++++++++++++++++++----------------
14
1 file changed, 32 insertions(+), 29 deletions(-)
15
16
diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build
17
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qemu-iotests/meson.build
19
+++ b/tests/qemu-iotests/meson.build
20
@@ -XXX,XX +XXX,XX @@
21
-if have_tools and targetos != 'windows' and not get_option('gprof')
22
- qemu_iotests_binaries = [qemu_img, qemu_io, qemu_nbd, qsd]
23
- qemu_iotests_env = {'PYTHON': python.full_path()}
24
- qemu_iotests_formats = {
25
- 'qcow2': 'quick',
26
- 'raw': 'slow',
27
- 'qed': 'thorough',
28
- 'vmdk': 'thorough',
29
- 'vpc': 'thorough'
30
- }
31
-
32
- foreach k, v : emulators
33
- if k.startswith('qemu-system-')
34
- qemu_iotests_binaries += v
35
- endif
36
- endforeach
37
- foreach format, speed: qemu_iotests_formats
38
- if speed == 'quick'
39
- suites = 'block'
40
- else
41
- suites = ['block-' + speed, speed]
42
- endif
43
- test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format],
44
- depends: qemu_iotests_binaries, env: qemu_iotests_env,
45
- protocol: 'tap',
46
- suite: suites,
47
- timeout: 0,
48
- is_parallel: false)
49
- endforeach
50
+if not have_tools or targetos == 'windows' or get_option('gprof')
51
+ subdir_done()
52
endif
53
+
54
+qemu_iotests_binaries = [qemu_img, qemu_io, qemu_nbd, qsd]
55
+qemu_iotests_env = {'PYTHON': python.full_path()}
56
+qemu_iotests_formats = {
57
+ 'qcow2': 'quick',
58
+ 'raw': 'slow',
59
+ 'qed': 'thorough',
60
+ 'vmdk': 'thorough',
61
+ 'vpc': 'thorough'
62
+}
63
+
64
+foreach k, v : emulators
65
+ if k.startswith('qemu-system-')
66
+ qemu_iotests_binaries += v
67
+ endif
68
+endforeach
69
+
70
+foreach format, speed: qemu_iotests_formats
71
+ if speed == 'quick'
72
+ suites = 'block'
73
+ else
74
+ suites = ['block-' + speed, speed]
75
+ endif
76
+ test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format],
77
+ depends: qemu_iotests_binaries, env: qemu_iotests_env,
78
+ protocol: 'tap',
79
+ suite: suites,
80
+ timeout: 0,
81
+ is_parallel: false)
82
+endforeach
83
--
84
2.35.1
85
86
diff view generated by jsdifflib
1
When rebuilding the refcount structures (when qemu-img check -r found
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
errors with refcount = 0, but reference count > 0), the new refcount
2
3
table defaults to being put at the image file end[1]. There is no good
3
Introduce the 'event-loop-base' abstract class, it'll hold the
4
reason for that except that it means we will not have to rewrite any
4
properties common to all event loops and provide the necessary hooks for
5
refblocks we already wrote to disk.
5
their creation and maintenance. Then have iothread inherit from it.
6
6
7
Changing the code to rewrite those refblocks is not too difficult,
7
EventLoopBaseClass is defined as user creatable and provides a hook for
8
though, so let us do that. That is beneficial for images on block
8
its children to attach themselves to the user creatable class 'complete'
9
devices, where we cannot really write beyond the end of the image file.
9
function. It also provides an update_params() callback to propagate
10
10
property changes onto its children.
11
Use this opportunity to add extensive comments to the code, and refactor
11
12
it a bit, getting rid of the backwards-jumping goto.
12
The new 'event-loop-base' class will live in the root directory. It is
13
13
built on its own using the 'link_whole' option (there are no direct
14
[1] Unless there is something allocated in the area pointed to by the
14
function dependencies between the class and its children, it all happens
15
last refblock, so we have to write that refblock. In that case, we
15
trough 'constructor' magic). And also imposes new compilation
16
try to put the reftable in there.
16
dependencies:
17
17
18
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1519071
18
qom <- event-loop-base <- blockdev (iothread.c)
19
Closes: https://gitlab.com/qemu-project/qemu/-/issues/941
19
20
Reviewed-by: Eric Blake <eblake@redhat.com>
20
And in subsequent patches:
21
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
21
22
Message-Id: <20220405134652.19278-2-hreitz@redhat.com>
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>
23
---
40
---
24
block/qcow2-refcount.c | 332 +++++++++++++++++++++++++++++------------
41
qapi/qom.json | 22 +++++--
25
1 file changed, 235 insertions(+), 97 deletions(-)
42
meson.build | 23 ++++---
26
43
include/sysemu/event-loop-base.h | 36 +++++++++++
27
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
44
include/sysemu/iothread.h | 6 +-
45
event-loop-base.c | 104 +++++++++++++++++++++++++++++++
46
iothread.c | 65 ++++++-------------
47
6 files changed, 192 insertions(+), 64 deletions(-)
48
create mode 100644 include/sysemu/event-loop-base.h
49
create mode 100644 event-loop-base.c
50
51
diff --git a/qapi/qom.json b/qapi/qom.json
28
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
29
--- a/block/qcow2-refcount.c
53
--- a/qapi/qom.json
30
+++ b/block/qcow2-refcount.c
54
+++ b/qapi/qom.json
31
@@ -XXX,XX +XXX,XX @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs,
55
@@ -XXX,XX +XXX,XX @@
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);
32
}
353
}
33
354
34
/*
355
-static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
35
- * Creates a new refcount structure based solely on the in-memory information
356
+static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
36
- * given through *refcount_table. All necessary allocations will be reflected
37
- * in that array.
38
+ * Helper function for rebuild_refcount_structure().
39
*
40
- * On success, the old refcount structure is leaked (it will be covered by the
41
- * new refcount structure).
42
+ * Scan the range of clusters [first_cluster, end_cluster) for allocated
43
+ * clusters and write all corresponding refblocks to disk. The refblock
44
+ * and allocation data is taken from the in-memory refcount table
45
+ * *refcount_table[] (of size *nb_clusters), which is basically one big
46
+ * (unlimited size) refblock for the whole image.
47
+ *
48
+ * For these refblocks, clusters are allocated using said in-memory
49
+ * refcount table. Care is taken that these allocations are reflected
50
+ * in the refblocks written to disk.
51
+ *
52
+ * The refblocks' offsets are written into a reftable, which is
53
+ * *on_disk_reftable_ptr[] (of size *on_disk_reftable_entries_ptr). If
54
+ * that reftable is of insufficient size, it will be resized to fit.
55
+ * This reftable is not written to disk.
56
+ *
57
+ * (If *on_disk_reftable_ptr is not NULL, the entries within are assumed
58
+ * to point to existing valid refblocks that do not need to be allocated
59
+ * again.)
60
+ *
61
+ * Return whether the on-disk reftable array was resized (true/false),
62
+ * or -errno on error.
63
*/
64
-static int rebuild_refcount_structure(BlockDriverState *bs,
65
- BdrvCheckResult *res,
66
- void **refcount_table,
67
- int64_t *nb_clusters)
68
+static int rebuild_refcounts_write_refblocks(
69
+ BlockDriverState *bs, void **refcount_table, int64_t *nb_clusters,
70
+ int64_t first_cluster, int64_t end_cluster,
71
+ uint64_t **on_disk_reftable_ptr, uint32_t *on_disk_reftable_entries_ptr
72
+ )
73
{
357
{
74
BDRVQcow2State *s = bs->opaque;
358
+ IOThread *iothread = IOTHREAD(base);
75
- int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0;
359
ERRP_GUARD();
76
+ int64_t cluster;
360
77
int64_t refblock_offset, refblock_start, refblock_index;
361
+ if (!iothread->ctx) {
78
- uint32_t reftable_size = 0;
362
+ return;
79
- uint64_t *on_disk_reftable = NULL;
363
+ }
80
+ int64_t first_free_cluster = 0;
364
+
81
+ uint64_t *on_disk_reftable = *on_disk_reftable_ptr;
365
aio_context_set_poll_params(iothread->ctx,
82
+ uint32_t on_disk_reftable_entries = *on_disk_reftable_entries_ptr;
366
iothread->poll_max_ns,
83
void *on_disk_refblock;
367
iothread->poll_grow,
84
- int ret = 0;
368
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
85
- struct {
86
- uint64_t reftable_offset;
87
- uint32_t reftable_clusters;
88
- } QEMU_PACKED reftable_offset_and_clusters;
89
-
90
- qcow2_cache_empty(bs, s->refcount_block_cache);
91
+ bool reftable_grown = false;
92
+ int ret;
93
94
-write_refblocks:
95
- for (; cluster < *nb_clusters; cluster++) {
96
+ for (cluster = first_cluster; cluster < end_cluster; cluster++) {
97
+ /* Check all clusters to find refblocks that contain non-zero entries */
98
if (!s->get_refcount(*refcount_table, cluster)) {
99
continue;
100
}
101
102
+ /*
103
+ * This cluster is allocated, so we need to create a refblock
104
+ * for it. The data we will write to disk is just the
105
+ * respective slice from *refcount_table, so it will contain
106
+ * accurate refcounts for all clusters belonging to this
107
+ * refblock. After we have written it, we will therefore skip
108
+ * all remaining clusters in this refblock.
109
+ */
110
+
111
refblock_index = cluster >> s->refcount_block_bits;
112
refblock_start = refblock_index << s->refcount_block_bits;
113
114
- /* Don't allocate a cluster in a refblock already written to disk */
115
- if (first_free_cluster < refblock_start) {
116
- first_free_cluster = refblock_start;
117
- }
118
- refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table,
119
- nb_clusters, &first_free_cluster);
120
- if (refblock_offset < 0) {
121
- fprintf(stderr, "ERROR allocating refblock: %s\n",
122
- strerror(-refblock_offset));
123
- res->check_errors++;
124
- ret = refblock_offset;
125
- goto fail;
126
- }
127
+ if (on_disk_reftable_entries > refblock_index &&
128
+ on_disk_reftable[refblock_index])
129
+ {
130
+ /*
131
+ * We can get here after a `goto write_refblocks`: We have a
132
+ * reftable from a previous run, and the refblock is already
133
+ * allocated. No need to allocate it again.
134
+ */
135
+ refblock_offset = on_disk_reftable[refblock_index];
136
+ } else {
137
+ int64_t refblock_cluster_index;
138
139
- if (reftable_size <= refblock_index) {
140
- uint32_t old_reftable_size = reftable_size;
141
- uint64_t *new_on_disk_reftable;
142
+ /* Don't allocate a cluster in a refblock already written to disk */
143
+ if (first_free_cluster < refblock_start) {
144
+ first_free_cluster = refblock_start;
145
+ }
146
+ refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table,
147
+ nb_clusters,
148
+ &first_free_cluster);
149
+ if (refblock_offset < 0) {
150
+ fprintf(stderr, "ERROR allocating refblock: %s\n",
151
+ strerror(-refblock_offset));
152
+ return refblock_offset;
153
+ }
154
155
- reftable_size = ROUND_UP((refblock_index + 1) * REFTABLE_ENTRY_SIZE,
156
- s->cluster_size) / REFTABLE_ENTRY_SIZE;
157
- new_on_disk_reftable = g_try_realloc(on_disk_reftable,
158
- reftable_size *
159
- REFTABLE_ENTRY_SIZE);
160
- if (!new_on_disk_reftable) {
161
- res->check_errors++;
162
- ret = -ENOMEM;
163
- goto fail;
164
+ refblock_cluster_index = refblock_offset / s->cluster_size;
165
+ if (refblock_cluster_index >= end_cluster) {
166
+ /*
167
+ * We must write the refblock that holds this refblock's
168
+ * refcount
169
+ */
170
+ end_cluster = refblock_cluster_index + 1;
171
}
172
- on_disk_reftable = new_on_disk_reftable;
173
174
- memset(on_disk_reftable + old_reftable_size, 0,
175
- (reftable_size - old_reftable_size) * REFTABLE_ENTRY_SIZE);
176
+ if (on_disk_reftable_entries <= refblock_index) {
177
+ on_disk_reftable_entries =
178
+ ROUND_UP((refblock_index + 1) * REFTABLE_ENTRY_SIZE,
179
+ s->cluster_size) / REFTABLE_ENTRY_SIZE;
180
+ on_disk_reftable =
181
+ g_try_realloc(on_disk_reftable,
182
+ on_disk_reftable_entries *
183
+ REFTABLE_ENTRY_SIZE);
184
+ if (!on_disk_reftable) {
185
+ return -ENOMEM;
186
+ }
187
188
- /* The offset we have for the reftable is now no longer valid;
189
- * this will leak that range, but we can easily fix that by running
190
- * a leak-fixing check after this rebuild operation */
191
- reftable_offset = -1;
192
- } else {
193
- assert(on_disk_reftable);
194
- }
195
- on_disk_reftable[refblock_index] = refblock_offset;
196
+ memset(on_disk_reftable + *on_disk_reftable_entries_ptr, 0,
197
+ (on_disk_reftable_entries -
198
+ *on_disk_reftable_entries_ptr) *
199
+ REFTABLE_ENTRY_SIZE);
200
201
- /* If this is apparently the last refblock (for now), try to squeeze the
202
- * reftable in */
203
- if (refblock_index == (*nb_clusters - 1) >> s->refcount_block_bits &&
204
- reftable_offset < 0)
205
- {
206
- uint64_t reftable_clusters = size_to_clusters(s, reftable_size *
207
- REFTABLE_ENTRY_SIZE);
208
- reftable_offset = alloc_clusters_imrt(bs, reftable_clusters,
209
- refcount_table, nb_clusters,
210
- &first_free_cluster);
211
- if (reftable_offset < 0) {
212
- fprintf(stderr, "ERROR allocating reftable: %s\n",
213
- strerror(-reftable_offset));
214
- res->check_errors++;
215
- ret = reftable_offset;
216
- goto fail;
217
+ *on_disk_reftable_ptr = on_disk_reftable;
218
+ *on_disk_reftable_entries_ptr = on_disk_reftable_entries;
219
+
220
+ reftable_grown = true;
221
+ } else {
222
+ assert(on_disk_reftable);
223
}
224
+ on_disk_reftable[refblock_index] = refblock_offset;
225
}
226
227
+ /* Refblock is allocated, write it to disk */
228
+
229
ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset,
230
s->cluster_size, false);
231
if (ret < 0) {
232
fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
233
- goto fail;
234
+ return ret;
235
}
236
237
- /* The size of *refcount_table is always cluster-aligned, therefore the
238
- * write operation will not overflow */
239
+ /*
240
+ * The refblock is simply a slice of *refcount_table.
241
+ * Note that the size of *refcount_table is always aligned to
242
+ * whole clusters, so the write operation will not result in
243
+ * out-of-bounds accesses.
244
+ */
245
on_disk_refblock = (void *)((char *) *refcount_table +
246
refblock_index * s->cluster_size);
247
248
@@ -XXX,XX +XXX,XX @@ write_refblocks:
249
s->cluster_size);
250
if (ret < 0) {
251
fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
252
- goto fail;
253
+ return ret;
254
}
255
256
- /* Go to the end of this refblock */
257
+ /* This refblock is done, skip to its end */
258
cluster = refblock_start + s->refcount_block_size - 1;
259
}
369
}
260
370
261
- if (reftable_offset < 0) {
371
aio_context_set_aio_params(iothread->ctx,
262
- uint64_t post_refblock_start, reftable_clusters;
372
- iothread->aio_max_batch,
263
+ return reftable_grown;
373
+ iothread->parent_obj.aio_max_batch,
264
+}
374
errp);
265
+
375
}
266
+/*
376
267
+ * Creates a new refcount structure based solely on the in-memory information
377
-static void iothread_complete(UserCreatable *obj, Error **errp)
268
+ * given through *refcount_table (this in-memory information is basically just
378
+
269
+ * the concatenation of all refblocks). All necessary allocations will be
379
+static void iothread_init(EventLoopBase *base, Error **errp)
270
+ * reflected in that array.
380
{
271
+ *
381
Error *local_error = NULL;
272
+ * On success, the old refcount structure is leaked (it will be covered by the
382
- IOThread *iothread = IOTHREAD(obj);
273
+ * new refcount structure).
383
+ IOThread *iothread = IOTHREAD(base);
274
+ */
384
char *thread_name;
275
+static int rebuild_refcount_structure(BlockDriverState *bs,
385
276
+ BdrvCheckResult *res,
386
iothread->stopping = false;
277
+ void **refcount_table,
387
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
278
+ int64_t *nb_clusters)
388
*/
279
+{
389
iothread_init_gcontext(iothread);
280
+ BDRVQcow2State *s = bs->opaque;
390
281
+ int64_t reftable_offset = -1;
391
- iothread_set_aio_context_params(iothread, &local_error);
282
+ int64_t reftable_length = 0;
392
+ iothread_set_aio_context_params(base, &local_error);
283
+ int64_t reftable_clusters;
393
if (local_error) {
284
+ int64_t refblock_index;
394
error_propagate(errp, local_error);
285
+ uint32_t on_disk_reftable_entries = 0;
395
aio_context_unref(iothread->ctx);
286
+ uint64_t *on_disk_reftable = NULL;
396
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
287
+ int ret = 0;
397
* to inherit.
288
+ int reftable_size_changed = 0;
398
*/
289
+ struct {
399
thread_name = g_strdup_printf("IO %s",
290
+ uint64_t reftable_offset;
400
- object_get_canonical_path_component(OBJECT(obj)));
291
+ uint32_t reftable_clusters;
401
+ object_get_canonical_path_component(OBJECT(base)));
292
+ } QEMU_PACKED reftable_offset_and_clusters;
402
qemu_thread_create(&iothread->thread, thread_name, iothread_run,
293
+
403
iothread, QEMU_THREAD_JOINABLE);
294
+ qcow2_cache_empty(bs, s->refcount_block_cache);
404
g_free(thread_name);
295
+
405
@@ -XXX,XX +XXX,XX @@ static IOThreadParamInfo poll_grow_info = {
296
+ /*
406
static IOThreadParamInfo poll_shrink_info = {
297
+ * For each refblock containing entries, we try to allocate a
407
"poll-shrink", offsetof(IOThread, poll_shrink),
298
+ * cluster (in the in-memory refcount table) and write its offset
408
};
299
+ * into on_disk_reftable[]. We then write the whole refblock to
409
-static IOThreadParamInfo aio_max_batch_info = {
300
+ * disk (as a slice of the in-memory refcount table).
410
- "aio-max-batch", offsetof(IOThread, aio_max_batch),
301
+ * This is done by rebuild_refcounts_write_refblocks().
411
-};
302
+ *
412
303
+ * Once we have scanned all clusters, we try to find space for the
413
static void iothread_get_param(Object *obj, Visitor *v,
304
+ * reftable. This will dirty the in-memory refcount table (i.e.
414
const char *name, IOThreadParamInfo *info, Error **errp)
305
+ * make it differ from the refblocks we have already written), so we
415
@@ -XXX,XX +XXX,XX @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
306
+ * need to run rebuild_refcounts_write_refblocks() again for the
416
}
307
+ * range of clusters where the reftable has been allocated.
417
}
308
+ *
418
309
+ * This second run might make the reftable grow again, in which case
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
310
+ * we will need to allocate another space for it, which is why we
420
- const char *name, void *opaque, Error **errp)
311
+ * repeat all this until the reftable stops growing.
421
-{
312
+ *
422
- IOThreadParamInfo *info = opaque;
313
+ * (This loop will terminate, because with every cluster the
423
-
314
+ * reftable grows, it can accomodate a multitude of more refcounts,
424
- iothread_get_param(obj, v, name, info, errp);
315
+ * so that at some point this must be able to cover the reftable
425
-}
316
+ * and all refblocks describing it.)
426
-
317
+ *
427
-static void iothread_set_aio_param(Object *obj, Visitor *v,
318
+ * We then convert the reftable to big-endian and write it to disk.
428
- const char *name, void *opaque, Error **errp)
319
+ *
429
-{
320
+ * Note that we never free any reftable allocations. Doing so would
430
- IOThread *iothread = IOTHREAD(obj);
321
+ * needlessly complicate the algorithm: The eventual second check
431
- IOThreadParamInfo *info = opaque;
322
+ * run we do will clean up all leaks we have caused.
432
-
323
+ */
433
- if (!iothread_set_param(obj, v, name, info, errp)) {
324
+
434
- return;
325
+ reftable_size_changed =
326
+ rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters,
327
+ 0, *nb_clusters,
328
+ &on_disk_reftable,
329
+ &on_disk_reftable_entries);
330
+ if (reftable_size_changed < 0) {
331
+ res->check_errors++;
332
+ ret = reftable_size_changed;
333
+ goto fail;
334
+ }
335
+
336
+ /*
337
+ * There was no reftable before, so rebuild_refcounts_write_refblocks()
338
+ * must have increased its size (from 0 to something).
339
+ */
340
+ assert(reftable_size_changed);
341
+
342
+ do {
343
+ int64_t reftable_start_cluster, reftable_end_cluster;
344
+ int64_t first_free_cluster = 0;
345
+
346
+ reftable_length = on_disk_reftable_entries * REFTABLE_ENTRY_SIZE;
347
+ reftable_clusters = size_to_clusters(s, reftable_length);
348
349
- post_refblock_start = ROUND_UP(*nb_clusters, s->refcount_block_size);
350
- reftable_clusters =
351
- size_to_clusters(s, reftable_size * REFTABLE_ENTRY_SIZE);
352
- /* Not pretty but simple */
353
- if (first_free_cluster < post_refblock_start) {
354
- first_free_cluster = post_refblock_start;
355
- }
356
reftable_offset = alloc_clusters_imrt(bs, reftable_clusters,
357
refcount_table, nb_clusters,
358
&first_free_cluster);
359
@@ -XXX,XX +XXX,XX @@ write_refblocks:
360
goto fail;
361
}
362
363
- goto write_refblocks;
364
- }
435
- }
365
+ /*
436
-
366
+ * We need to update the affected refblocks, so re-run the
437
- if (iothread->ctx) {
367
+ * write_refblocks loop for the reftable's range of clusters.
438
- aio_context_set_aio_params(iothread->ctx,
368
+ */
439
- iothread->aio_max_batch,
369
+ assert(offset_into_cluster(s, reftable_offset) == 0);
440
- errp);
370
+ reftable_start_cluster = reftable_offset / s->cluster_size;
441
- }
371
+ reftable_end_cluster = reftable_start_cluster + reftable_clusters;
442
-}
372
+ reftable_size_changed =
443
-
373
+ rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters,
444
static void iothread_class_init(ObjectClass *klass, void *class_data)
374
+ reftable_start_cluster,
445
{
375
+ reftable_end_cluster,
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
376
+ &on_disk_reftable,
447
- ucc->complete = iothread_complete;
377
+ &on_disk_reftable_entries);
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
378
+ if (reftable_size_changed < 0) {
449
+
379
+ res->check_errors++;
450
+ bc->init = iothread_init;
380
+ ret = reftable_size_changed;
451
+ bc->update_params = iothread_set_aio_context_params;
381
+ goto fail;
452
382
+ }
453
object_class_property_add(klass, "poll-max-ns", "int",
383
+
454
iothread_get_poll_param,
384
+ /*
455
@@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
385
+ * If the reftable size has changed, we will need to find a new
456
iothread_get_poll_param,
386
+ * allocation, repeating the loop.
457
iothread_set_poll_param,
387
+ */
458
NULL, &poll_shrink_info);
388
+ } while (reftable_size_changed);
459
- object_class_property_add(klass, "aio-max-batch", "int",
389
460
- iothread_get_aio_param,
390
- for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) {
461
- iothread_set_aio_param,
391
+ /* The above loop must have run at least once */
462
- NULL, &aio_max_batch_info);
392
+ assert(reftable_offset >= 0);
463
}
393
+
464
394
+ /*
465
static const TypeInfo iothread_info = {
395
+ * All allocations are done, all refblocks are written, convert the
466
.name = TYPE_IOTHREAD,
396
+ * reftable to big-endian and write it to disk.
467
- .parent = TYPE_OBJECT,
397
+ */
468
+ .parent = TYPE_EVENT_LOOP_BASE,
398
+
469
.class_init = iothread_class_init,
399
+ for (refblock_index = 0; refblock_index < on_disk_reftable_entries;
470
.instance_size = sizeof(IOThread),
400
+ refblock_index++)
471
.instance_init = iothread_instance_init,
401
+ {
472
.instance_finalize = iothread_instance_finalize,
402
cpu_to_be64s(&on_disk_reftable[refblock_index]);
473
- .interfaces = (InterfaceInfo[]) {
403
}
474
- {TYPE_USER_CREATABLE},
404
475
- {}
405
- ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset,
476
- },
406
- reftable_size * REFTABLE_ENTRY_SIZE,
477
};
407
+ ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, reftable_length,
478
408
false);
479
static void iothread_register_types(void)
409
if (ret < 0) {
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
410
fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
481
info->poll_max_ns = iothread->poll_max_ns;
411
goto fail;
482
info->poll_grow = iothread->poll_grow;
412
}
483
info->poll_shrink = iothread->poll_shrink;
413
484
- info->aio_max_batch = iothread->aio_max_batch;
414
- assert(reftable_size < INT_MAX / REFTABLE_ENTRY_SIZE);
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
415
+ assert(reftable_length < INT_MAX);
486
416
ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable,
487
QAPI_LIST_APPEND(*tail, info);
417
- reftable_size * REFTABLE_ENTRY_SIZE);
418
+ reftable_length);
419
if (ret < 0) {
420
fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
421
goto fail;
422
@@ -XXX,XX +XXX,XX @@ write_refblocks:
423
/* Enter new reftable into the image header */
424
reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset);
425
reftable_offset_and_clusters.reftable_clusters =
426
- cpu_to_be32(size_to_clusters(s, reftable_size * REFTABLE_ENTRY_SIZE));
427
+ cpu_to_be32(reftable_clusters);
428
ret = bdrv_pwrite_sync(bs->file,
429
offsetof(QCowHeader, refcount_table_offset),
430
&reftable_offset_and_clusters,
431
@@ -XXX,XX +XXX,XX @@ write_refblocks:
432
goto fail;
433
}
434
435
- for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) {
436
+ for (refblock_index = 0; refblock_index < on_disk_reftable_entries;
437
+ refblock_index++)
438
+ {
439
be64_to_cpus(&on_disk_reftable[refblock_index]);
440
}
441
s->refcount_table = on_disk_reftable;
442
s->refcount_table_offset = reftable_offset;
443
- s->refcount_table_size = reftable_size;
444
+ s->refcount_table_size = on_disk_reftable_entries;
445
update_max_refcount_table_index(s);
446
447
return 0;
488
return 0;
448
--
489
--
449
2.35.1
490
2.35.1
diff view generated by jsdifflib
1
From: Thomas Huth <thuth@redhat.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
We want to get rid of check-block.sh in the long run, so let's move
3
'event-loop-base' provides basic property handling for all 'AioContext'
4
the checks for the bash version and sanitizers from check-block.sh
4
based event loops. So let's define a new 'MainLoopClass' that inherits
5
into the meson.build file instead.
5
from it. This will permit tweaking the main loop's properties through
6
6
qapi as well as through the command line using the '-object' keyword[1].
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
Only one instance of 'MainLoopClass' might be created at any time.
8
Message-Id: <20220223093840.2515281-4-thuth@redhat.com>
8
9
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
10
mark 'MainLoop' as non-deletable.
11
12
[1] For example:
13
-object main-loop,id=main-loop,aio-max-batch=<value>
14
15
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
16
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Acked-by: Markus Armbruster <armbru@redhat.com>
18
Message-id: 20220425075723.20019-3-nsaenzju@redhat.com
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
20
---
11
tests/check-block.sh | 26 --------------------------
21
qapi/qom.json | 13 ++++++++
12
tests/qemu-iotests/meson.build | 14 ++++++++++++++
22
meson.build | 3 +-
13
2 files changed, 14 insertions(+), 26 deletions(-)
23
include/qemu/main-loop.h | 10 ++++++
14
24
include/sysemu/event-loop-base.h | 1 +
15
diff --git a/tests/check-block.sh b/tests/check-block.sh
25
event-loop-base.c | 13 ++++++++
16
index XXXXXXX..XXXXXXX 100755
26
util/main-loop.c | 56 ++++++++++++++++++++++++++++++++
17
--- a/tests/check-block.sh
27
6 files changed, 95 insertions(+), 1 deletion(-)
18
+++ b/tests/check-block.sh
28
19
@@ -XXX,XX +XXX,XX @@ skip() {
29
diff --git a/qapi/qom.json b/qapi/qom.json
20
exit 0
30
index XXXXXXX..XXXXXXX 100644
31
--- a/qapi/qom.json
32
+++ b/qapi/qom.json
33
@@ -XXX,XX +XXX,XX @@
34
'*poll-grow': 'int',
35
'*poll-shrink': 'int' } }
36
37
+##
38
+# @MainLoopProperties:
39
+#
40
+# Properties for the main-loop object.
41
+#
42
+# Since: 7.1
43
+##
44
+{ 'struct': 'MainLoopProperties',
45
+ 'base': 'EventLoopBaseProperties',
46
+ 'data': {} }
47
+
48
##
49
# @MemoryBackendProperties:
50
#
51
@@ -XXX,XX +XXX,XX @@
52
{ 'name': 'input-linux',
53
'if': 'CONFIG_LINUX' },
54
'iothread',
55
+ 'main-loop',
56
{ 'name': 'memory-backend-epc',
57
'if': 'CONFIG_LINUX' },
58
'memory-backend-file',
59
@@ -XXX,XX +XXX,XX @@
60
'input-linux': { 'type': 'InputLinuxProperties',
61
'if': 'CONFIG_LINUX' },
62
'iothread': 'IothreadProperties',
63
+ 'main-loop': 'MainLoopProperties',
64
'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties',
65
'if': 'CONFIG_LINUX' },
66
'memory-backend-file': 'MemoryBackendFileProperties',
67
diff --git a/meson.build b/meson.build
68
index XXXXXXX..XXXXXXX 100644
69
--- a/meson.build
70
+++ b/meson.build
71
@@ -XXX,XX +XXX,XX @@ libqemuutil = static_library('qemuutil',
72
sources: util_ss.sources() + stub_ss.sources() + genh,
73
dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman])
74
qemuutil = declare_dependency(link_with: libqemuutil,
75
- sources: genh + version_res)
76
+ sources: genh + version_res,
77
+ dependencies: [event_loop_base])
78
79
if have_system or have_user
80
decodetree = generator(find_program('scripts/decodetree.py'),
81
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/qemu/main-loop.h
84
+++ b/include/qemu/main-loop.h
85
@@ -XXX,XX +XXX,XX @@
86
#define QEMU_MAIN_LOOP_H
87
88
#include "block/aio.h"
89
+#include "qom/object.h"
90
+#include "sysemu/event-loop-base.h"
91
92
#define SIG_IPI SIGUSR1
93
94
+#define TYPE_MAIN_LOOP "main-loop"
95
+OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP)
96
+
97
+struct MainLoop {
98
+ EventLoopBase parent_obj;
99
+};
100
+typedef struct MainLoop MainLoop;
101
+
102
/**
103
* qemu_init_main_loop: Set up the process so that it can run the main loop.
104
*
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBaseClass {
110
111
void (*init)(EventLoopBase *base, Error **errp);
112
void (*update_params)(EventLoopBase *base, Error **errp);
113
+ bool (*can_be_deleted)(EventLoopBase *base);
114
};
115
116
struct EventLoopBase {
117
diff --git a/event-loop-base.c b/event-loop-base.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/event-loop-base.c
120
+++ b/event-loop-base.c
121
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_complete(UserCreatable *uc, Error **errp)
122
}
21
}
123
}
22
124
23
-# Disable tests with any sanitizer except for specific ones
125
+static bool event_loop_base_can_be_deleted(UserCreatable *uc)
24
-SANITIZE_FLAGS=$( grep "CFLAGS.*-fsanitize" config-host.mak 2>/dev/null )
126
+{
25
-ALLOWED_SANITIZE_FLAGS="safe-stack cfi-icall"
127
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
26
-#Remove all occurrencies of allowed Sanitize flags
128
+ EventLoopBase *backend = EVENT_LOOP_BASE(uc);
27
-for j in ${ALLOWED_SANITIZE_FLAGS}; do
129
+
28
- TMP_FLAGS=${SANITIZE_FLAGS}
130
+ if (bc->can_be_deleted) {
29
- SANITIZE_FLAGS=""
131
+ return bc->can_be_deleted(backend);
30
- for i in ${TMP_FLAGS}; do
132
+ }
31
- if ! echo ${i} | grep -q "${j}" 2>/dev/null; then
133
+
32
- SANITIZE_FLAGS="${SANITIZE_FLAGS} ${i}"
134
+ return true;
33
- fi
135
+}
34
- done
136
+
35
-done
137
static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
36
-if echo ${SANITIZE_FLAGS} | grep -q "\-fsanitize" 2>/dev/null; then
138
{
37
- # Have a sanitize flag that is not allowed, stop
139
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
38
- skip "Sanitizers are enabled ==> Not running the qemu-iotests."
140
ucc->complete = event_loop_base_complete;
39
-fi
141
+ ucc->can_be_deleted = event_loop_base_can_be_deleted;
40
-
142
41
if [ -z "$(find . -name 'qemu-system-*' -print)" ]; then
143
object_class_property_add(klass, "aio-max-batch", "int",
42
skip "No qemu-system binary available ==> Not running the qemu-iotests."
144
event_loop_base_get_param,
43
fi
145
diff --git a/util/main-loop.c b/util/main-loop.c
44
146
index XXXXXXX..XXXXXXX 100644
45
-if ! command -v bash >/dev/null 2>&1 ; then
147
--- a/util/main-loop.c
46
- skip "bash not available ==> Not running the qemu-iotests."
148
+++ b/util/main-loop.c
47
-fi
149
@@ -XXX,XX +XXX,XX @@
48
-
150
#include "qemu/error-report.h"
49
-if LANG=C bash --version | grep -q 'GNU bash, version [123]' ; then
151
#include "qemu/queue.h"
50
- skip "bash version too old ==> Not running the qemu-iotests."
152
#include "qemu/compiler.h"
51
-fi
153
+#include "qom/object.h"
52
-
154
53
cd tests/qemu-iotests
155
#ifndef _WIN32
54
156
#include <sys/wait.h>
55
# QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests
157
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
56
diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build
158
return 0;
57
index XXXXXXX..XXXXXXX 100644
159
}
58
--- a/tests/qemu-iotests/meson.build
160
59
+++ b/tests/qemu-iotests/meson.build
161
+static void main_loop_update_params(EventLoopBase *base, Error **errp)
60
@@ -XXX,XX +XXX,XX @@ if not have_tools or targetos == 'windows' or get_option('gprof')
162
+{
61
subdir_done()
163
+ if (!qemu_aio_context) {
62
endif
164
+ error_setg(errp, "qemu aio context not ready");
63
165
+ return;
64
+foreach cflag: config_host['QEMU_CFLAGS'].split()
166
+ }
65
+ if cflag.startswith('-fsanitize') and \
167
+
66
+ not cflag.contains('safe-stack') and not cflag.contains('cfi-icall')
168
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
67
+ message('Sanitizers are enabled ==> Disabled the qemu-iotests.')
169
+}
68
+ subdir_done()
170
+
69
+ endif
171
+MainLoop *mloop;
70
+endforeach
172
+
71
+
173
+static void main_loop_init(EventLoopBase *base, Error **errp)
72
+bash = find_program('bash', required: false, version: '>= 4.0')
174
+{
73
+if not bash.found()
175
+ MainLoop *m = MAIN_LOOP(base);
74
+ message('bash >= v4.0 not available ==> Disabled the qemu-iotests.')
176
+
75
+ subdir_done()
177
+ if (mloop) {
76
+endif
178
+ error_setg(errp, "only one main-loop instance allowed");
77
+
179
+ return;
78
qemu_iotests_binaries = [qemu_img, qemu_io, qemu_nbd, qsd]
180
+ }
79
qemu_iotests_env = {'PYTHON': python.full_path()}
181
+
80
qemu_iotests_formats = {
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
81
--
219
--
82
2.35.1
220
2.35.1
diff view generated by jsdifflib
Deleted patch
1
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
3
Message-Id: <20220323105522.53660-2-hreitz@redhat.com>
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
5
---
6
tests/qemu-iotests/iotests.py | 20 ++++++++++++++++++++
7
1 file changed, 20 insertions(+)
8
1
9
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
10
index XXXXXXX..XXXXXXX 100644
11
--- a/tests/qemu-iotests/iotests.py
12
+++ b/tests/qemu-iotests/iotests.py
13
@@ -XXX,XX +XXX,XX @@ def verify_working_luks():
14
if not working:
15
notrun(reason)
16
17
+def supports_qcow2_zstd_compression() -> bool:
18
+ img_file = f'{test_dir}/qcow2-zstd-test.qcow2'
19
+ res = qemu_img('create', '-f', 'qcow2', '-o', 'compression_type=zstd',
20
+ img_file, '0',
21
+ check=False)
22
+ try:
23
+ os.remove(img_file)
24
+ except OSError:
25
+ pass
26
+
27
+ if res.returncode == 1 and \
28
+ "'compression-type' does not accept value 'zstd'" in res.stdout:
29
+ return False
30
+ else:
31
+ return True
32
+
33
+def verify_qcow2_zstd_compression():
34
+ if not supports_qcow2_zstd_compression():
35
+ notrun('zstd compression not supported')
36
+
37
def qemu_pipe(*args: str) -> str:
38
"""
39
Run qemu with an option to print something and exit (e.g. a help option).
40
--
41
2.35.1
diff view generated by jsdifflib
Deleted patch
1
Some test cases run in iotest 065 want to run with zstd compression just
2
for added coverage. Run them with zlib if there is no zstd support
3
compiled in.
4
1
5
Reported-by: Thomas Huth <thuth@redhat.com>
6
Fixes: 12a936171d71f839dc907ff ("iotest 065: explicit compression type")
7
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
8
Message-Id: <20220323105522.53660-3-hreitz@redhat.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
10
---
11
tests/qemu-iotests/065 | 24 ++++++++++++++++++------
12
1 file changed, 18 insertions(+), 6 deletions(-)
13
14
diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065
15
index XXXXXXX..XXXXXXX 100755
16
--- a/tests/qemu-iotests/065
17
+++ b/tests/qemu-iotests/065
18
@@ -XXX,XX +XXX,XX @@ import os
19
import re
20
import json
21
import iotests
22
-from iotests import qemu_img, qemu_img_info
23
+from iotests import qemu_img, qemu_img_info, supports_qcow2_zstd_compression
24
import unittest
25
26
test_img = os.path.join(iotests.test_dir, 'test.img')
27
@@ -XXX,XX +XXX,XX @@ class TestQCow2(TestQemuImgInfo):
28
29
class TestQCow3NotLazy(TestQemuImgInfo):
30
'''Testing a qcow2 version 3 image with lazy refcounts disabled'''
31
- img_options = 'compat=1.1,lazy_refcounts=off,compression_type=zstd'
32
+ if supports_qcow2_zstd_compression():
33
+ compression_type = 'zstd'
34
+ else:
35
+ compression_type = 'zlib'
36
+
37
+ img_options = 'compat=1.1,lazy_refcounts=off'
38
+ img_options += f',compression_type={compression_type}'
39
json_compare = { 'compat': '1.1', 'lazy-refcounts': False,
40
'refcount-bits': 16, 'corrupt': False,
41
- 'compression-type': 'zstd', 'extended-l2': False }
42
- human_compare = [ 'compat: 1.1', 'compression type: zstd',
43
+ 'compression-type': compression_type, 'extended-l2': False }
44
+ human_compare = [ 'compat: 1.1', f'compression type: {compression_type}',
45
'lazy refcounts: false', 'refcount bits: 16',
46
'corrupt: false', 'extended l2: false' ]
47
48
@@ -XXX,XX +XXX,XX @@ class TestQCow3NotLazyQMP(TestQMP):
49
class TestQCow3LazyQMP(TestQMP):
50
'''Testing a qcow2 version 3 image with lazy refcounts enabled, opening
51
with lazy refcounts disabled'''
52
- img_options = 'compat=1.1,lazy_refcounts=on,compression_type=zstd'
53
+ if supports_qcow2_zstd_compression():
54
+ compression_type = 'zstd'
55
+ else:
56
+ compression_type = 'zlib'
57
+
58
+ img_options = 'compat=1.1,lazy_refcounts=on'
59
+ img_options += f',compression_type={compression_type}'
60
qemu_options = 'lazy-refcounts=off'
61
compare = { 'compat': '1.1', 'lazy-refcounts': True,
62
'refcount-bits': 16, 'corrupt': False,
63
- 'compression-type': 'zstd', 'extended-l2': False }
64
+ 'compression-type': compression_type, 'extended-l2': False }
65
66
TestImageInfoSpecific = None
67
TestQemuImgInfo = None
68
--
69
2.35.1
diff view generated by jsdifflib
Deleted patch
1
303 runs two test cases, one of which requires zstd support.
2
Unfortunately, given that this is not a unittest-style test, we cannot
3
easily skip that single case, and instead can only skip the whole test.
4
1
5
(Alternatively, we could split this test into a zlib and a zstd part,
6
but that seems excessive, given that this test is not in auto and thus
7
likely only run by developers who have zstd support compiled in.)
8
9
Fixes: 677e0bae686e7c670a71d1f ("iotest 303: explicit compression type")
10
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
11
Reviewed-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
12
Message-Id: <20220323105522.53660-4-hreitz@redhat.com>
13
---
14
tests/qemu-iotests/303 | 4 +++-
15
1 file changed, 3 insertions(+), 1 deletion(-)
16
17
diff --git a/tests/qemu-iotests/303 b/tests/qemu-iotests/303
18
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/303
20
+++ b/tests/qemu-iotests/303
21
@@ -XXX,XX +XXX,XX @@
22
23
import iotests
24
import subprocess
25
-from iotests import qemu_img_create, qemu_io, file_path, log, filter_qemu_io
26
+from iotests import qemu_img_create, qemu_io, file_path, log, filter_qemu_io, \
27
+ verify_qcow2_zstd_compression
28
29
iotests.script_initialize(supported_fmts=['qcow2'],
30
unsupported_imgopts=['refcount_bits', 'compat'])
31
+verify_qcow2_zstd_compression()
32
33
disk = file_path('disk')
34
chunk = 1024 * 1024
35
--
36
2.35.1
diff view generated by jsdifflib
Deleted patch
1
One clear problem with how qcow2's refcount structure rebuild algorithm
2
used to be before "qcow2: Improve refcount structure rebuilding" was
3
that it is prone to failure for qcow2 images on block devices: There is
4
generally unused space after the actual image, and if that exceeds what
5
one refblock covers, the old algorithm would invariably write the
6
reftable past the block device's end, which cannot work. The new
7
algorithm does not have this problem.
8
1
9
Test it with three tests:
10
(1) Create an image with more empty space at the end than what one
11
refblock covers, see whether rebuilding the refcount structures
12
results in a change in the image file length. (It should not.)
13
14
(2) Leave precisely enough space somewhere at the beginning of the image
15
for the new reftable (and the refblock for that place), see whether
16
the new algorithm puts the reftable there. (It should.)
17
18
(3) Test the original problem: Create (something like) a block device
19
with a fixed size, then create a qcow2 image in there, write some
20
data, and then have qemu-img check rebuild the refcount structures.
21
Before HEAD^, the reftable would have been written past the image
22
file end, i.e. outside of what the block device provides, which
23
cannot work. HEAD^ should have fixed that.
24
("Something like a block device" means a loop device if we can use
25
one ("sudo -n losetup" works), or a FUSE block export with
26
growable=false otherwise.)
27
28
Reviewed-by: Eric Blake <eblake@redhat.com>
29
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
30
Message-Id: <20220405134652.19278-3-hreitz@redhat.com>
31
---
32
tests/qemu-iotests/108 | 259 ++++++++++++++++++++++++++++++++++++-
33
tests/qemu-iotests/108.out | 81 ++++++++++++
34
2 files changed, 339 insertions(+), 1 deletion(-)
35
36
diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108
37
index XXXXXXX..XXXXXXX 100755
38
--- a/tests/qemu-iotests/108
39
+++ b/tests/qemu-iotests/108
40
@@ -XXX,XX +XXX,XX @@ status=1    # failure is the default!
41
42
_cleanup()
43
{
44
-    _cleanup_test_img
45
+ _cleanup_test_img
46
+ if [ -f "$TEST_DIR/qsd.pid" ]; then
47
+ qsd_pid=$(cat "$TEST_DIR/qsd.pid")
48
+ kill -KILL "$qsd_pid"
49
+ fusermount -u "$TEST_DIR/fuse-export" &>/dev/null
50
+ fi
51
+ rm -f "$TEST_DIR/fuse-export"
52
}
53
trap "_cleanup; exit \$status" 0 1 2 3 15
54
55
# get standard environment, filters and checks
56
. ./common.rc
57
. ./common.filter
58
+. ./common.qemu
59
60
# This tests qcow2-specific low-level functionality
61
_supported_fmt qcow2
62
@@ -XXX,XX +XXX,XX @@ _supported_os Linux
63
# files
64
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file
65
66
+# This test either needs sudo -n losetup or FUSE exports to work
67
+if sudo -n losetup &>/dev/null; then
68
+ loopdev=true
69
+else
70
+ loopdev=false
71
+
72
+ # QSD --export fuse will either yield "Parameter 'id' is missing"
73
+ # or "Invalid parameter 'fuse'", depending on whether there is
74
+ # FUSE support or not.
75
+ error=$($QSD --export fuse 2>&1)
76
+ if [[ $error = *"'fuse'"* ]]; then
77
+ _notrun 'Passwordless sudo for losetup or FUSE support required, but' \
78
+ 'neither is available'
79
+ fi
80
+fi
81
+
82
echo
83
echo '=== Repairing an image without any refcount table ==='
84
echo
85
@@ -XXX,XX +XXX,XX @@ _make_test_img 64M
86
poke_file "$TEST_IMG" $((0x10008)) "\xff\xff\xff\xff\xff\xff\x00\x00"
87
_check_test_img -r all
88
89
+echo
90
+echo '=== Check rebuilt reftable location ==='
91
+
92
+# In an earlier version of the refcount rebuild algorithm, the
93
+# reftable was generally placed at the image end (unless something was
94
+# allocated in the area covered by the refblock right before the image
95
+# file end, then we would try to place the reftable in that refblock).
96
+# This was later changed so the reftable would be placed in the
97
+# earliest possible location. Test this.
98
+
99
+echo
100
+echo '--- Does the image size increase? ---'
101
+echo
102
+
103
+# First test: Just create some image, write some data to it, and
104
+# resize it so there is free space at the end of the image (enough
105
+# that it spans at least one full refblock, which for cluster_size=512
106
+# images, spans 128k). With the old algorithm, the reftable would
107
+# have then been placed at the end of the image file, but with the new
108
+# one, it will be put in that free space.
109
+# We want to check whether the size of the image file increases due to
110
+# rebuilding the refcount structures (it should not).
111
+
112
+_make_test_img -o 'cluster_size=512' 1M
113
+# Write something
114
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
115
+
116
+# Add free space
117
+file_len=$(stat -c '%s' "$TEST_IMG")
118
+truncate -s $((file_len + 256 * 1024)) "$TEST_IMG"
119
+
120
+# Corrupt the image by saying the image header was not allocated
121
+rt_offset=$(peek_file_be "$TEST_IMG" 48 8)
122
+rb_offset=$(peek_file_be "$TEST_IMG" $rt_offset 8)
123
+poke_file "$TEST_IMG" $rb_offset "\x00\x00"
124
+
125
+# Check whether rebuilding the refcount structures increases the image
126
+# file size
127
+file_len=$(stat -c '%s' "$TEST_IMG")
128
+echo
129
+# The only leaks there can be are the old refcount structures that are
130
+# leaked during rebuilding, no need to clutter the output with them
131
+_check_test_img -r all | grep -v '^Repairing cluster.*refcount=1 reference=0'
132
+echo
133
+post_repair_file_len=$(stat -c '%s' "$TEST_IMG")
134
+
135
+if [[ $file_len -eq $post_repair_file_len ]]; then
136
+ echo 'OK: Image size did not change'
137
+else
138
+ echo 'ERROR: Image size differs' \
139
+ "($file_len before, $post_repair_file_len after)"
140
+fi
141
+
142
+echo
143
+echo '--- Will the reftable occupy a hole specifically left for it? ---'
144
+echo
145
+
146
+# Note: With cluster_size=512, every refblock covers 128k.
147
+# The reftable covers 8M per reftable cluster.
148
+
149
+# Create an image that requires two reftable clusters (just because
150
+# this is more interesting than a single-clustered reftable).
151
+_make_test_img -o 'cluster_size=512' 9M
152
+$QEMU_IO -c 'write 0 8M' "$TEST_IMG" | _filter_qemu_io
153
+
154
+# Writing 8M will have resized the reftable. Unfortunately, doing so
155
+# will leave holes in the file, so we need to fill them up so we can
156
+# be sure the whole file is allocated. Do that by writing
157
+# consecutively smaller chunks starting from 8 MB, until the file
158
+# length increases even with a chunk size of 512. Then we must have
159
+# filled all holes.
160
+ofs=$((8 * 1024 * 1024))
161
+block_len=$((16 * 1024))
162
+while [[ $block_len -ge 512 ]]; do
163
+ file_len=$(stat -c '%s' "$TEST_IMG")
164
+ while [[ $(stat -c '%s' "$TEST_IMG") -eq $file_len ]]; do
165
+ # Do not include this in the reference output, it does not
166
+ # really matter which qemu-io calls we do here exactly
167
+ $QEMU_IO -c "write $ofs $block_len" "$TEST_IMG" >/dev/null
168
+ ofs=$((ofs + block_len))
169
+ done
170
+ block_len=$((block_len / 2))
171
+done
172
+
173
+# Fill up to 9M (do not include this in the reference output either,
174
+# $ofs is random for all we know)
175
+$QEMU_IO -c "write $ofs $((9 * 1024 * 1024 - ofs))" "$TEST_IMG" >/dev/null
176
+
177
+# Make space as follows:
178
+# - For the first refblock: Right at the beginning of the image (this
179
+# refblock is placed in the first place possible),
180
+# - For the reftable somewhere soon afterwards, still near the
181
+# beginning of the image (i.e. covered by the first refblock); the
182
+# reftable too is placed in the first place possible, but only after
183
+# all refblocks have been placed)
184
+# No space is needed for the other refblocks, because no refblock is
185
+# put before the space it covers. In this test case, we do not mind
186
+# if they are placed at the image file's end.
187
+
188
+# Before we make that space, we have to find out the host offset of
189
+# the area that belonged to the two data clusters at guest offset 4k,
190
+# because we expect the reftable to be placed there, and we will have
191
+# to verify that it is.
192
+
193
+l1_offset=$(peek_file_be "$TEST_IMG" 40 8)
194
+l2_offset=$(peek_file_be "$TEST_IMG" $l1_offset 8)
195
+l2_offset=$((l2_offset & 0x00fffffffffffe00))
196
+data_4k_offset=$(peek_file_be "$TEST_IMG" \
197
+ $((l2_offset + 4096 / 512 * 8)) 8)
198
+data_4k_offset=$((data_4k_offset & 0x00fffffffffffe00))
199
+
200
+$QEMU_IO -c "discard 0 512" -c "discard 4k 1k" "$TEST_IMG" | _filter_qemu_io
201
+
202
+# Corrupt the image by saying the image header was not allocated
203
+rt_offset=$(peek_file_be "$TEST_IMG" 48 8)
204
+rb_offset=$(peek_file_be "$TEST_IMG" $rt_offset 8)
205
+poke_file "$TEST_IMG" $rb_offset "\x00\x00"
206
+
207
+echo
208
+# The only leaks there can be are the old refcount structures that are
209
+# leaked during rebuilding, no need to clutter the output with them
210
+_check_test_img -r all | grep -v '^Repairing cluster.*refcount=1 reference=0'
211
+echo
212
+
213
+# Check whether the reftable was put where we expected
214
+rt_offset=$(peek_file_be "$TEST_IMG" 48 8)
215
+if [[ $rt_offset -eq $data_4k_offset ]]; then
216
+ echo 'OK: Reftable is where we expect it'
217
+else
218
+ echo "ERROR: Reftable is at $rt_offset, but was expected at $data_4k_offset"
219
+fi
220
+
221
+echo
222
+echo '--- Rebuilding refcount structures on block devices ---'
223
+echo
224
+
225
+# A block device cannot really grow, at least not during qemu-img
226
+# check. As mentioned in the above cases, rebuilding the refcount
227
+# structure may lead to new refcount structures being written after
228
+# the end of the image, and in the past that happened even if there
229
+# was more than sufficient space in the image. Such post-EOF writes
230
+# will not work on block devices, so test that the new algorithm
231
+# avoids it.
232
+
233
+# If we have passwordless sudo and losetup, we can use those to create
234
+# a block device. Otherwise, we can resort to qemu's FUSE export to
235
+# create a file that isn't growable, which effectively tests the same
236
+# thing.
237
+
238
+_cleanup_test_img
239
+truncate -s $((64 * 1024 * 1024)) "$TEST_IMG"
240
+
241
+if $loopdev; then
242
+ export_mp=$(sudo -n losetup --show -f "$TEST_IMG")
243
+ export_mp_driver=host_device
244
+ sudo -n chmod go+rw "$export_mp"
245
+else
246
+ # Create non-growable FUSE export that is a bit like an empty
247
+ # block device
248
+ export_mp="$TEST_DIR/fuse-export"
249
+ export_mp_driver=file
250
+ touch "$export_mp"
251
+
252
+ $QSD \
253
+ --blockdev file,node-name=export-node,filename="$TEST_IMG" \
254
+ --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=off \
255
+ --pidfile "$TEST_DIR/qsd.pid" \
256
+ --daemonize
257
+fi
258
+
259
+# Now create a qcow2 image on the device -- unfortunately, qemu-img
260
+# create force-creates the file, so we have to resort to the
261
+# blockdev-create job.
262
+_launch_qemu \
263
+ --blockdev $export_mp_driver,node-name=file,filename="$export_mp"
264
+
265
+_send_qemu_cmd \
266
+ $QEMU_HANDLE \
267
+ '{ "execute": "qmp_capabilities" }' \
268
+ 'return'
269
+
270
+# Small cluster size again, so the image needs multiple refblocks
271
+_send_qemu_cmd \
272
+ $QEMU_HANDLE \
273
+ '{ "execute": "blockdev-create",
274
+ "arguments": {
275
+ "job-id": "create",
276
+ "options": {
277
+ "driver": "qcow2",
278
+ "file": "file",
279
+ "size": '$((64 * 1024 * 1024))',
280
+ "cluster-size": 512
281
+ } } }' \
282
+ '"concluded"'
283
+
284
+_send_qemu_cmd \
285
+ $QEMU_HANDLE \
286
+ '{ "execute": "job-dismiss", "arguments": { "id": "create" } }' \
287
+ 'return'
288
+
289
+_send_qemu_cmd \
290
+ $QEMU_HANDLE \
291
+ '{ "execute": "quit" }' \
292
+ 'return'
293
+
294
+wait=y _cleanup_qemu
295
+echo
296
+
297
+# Write some data
298
+$QEMU_IO -c 'write 0 64k' "$export_mp" | _filter_qemu_io
299
+
300
+# Corrupt the image by saying the image header was not allocated
301
+rt_offset=$(peek_file_be "$export_mp" 48 8)
302
+rb_offset=$(peek_file_be "$export_mp" $rt_offset 8)
303
+poke_file "$export_mp" $rb_offset "\x00\x00"
304
+
305
+# Repairing such a simple case should just work
306
+# (We used to put the reftable at the end of the image file, which can
307
+# never work for non-growable devices.)
308
+echo
309
+TEST_IMG="$export_mp" _check_test_img -r all \
310
+ | grep -v '^Repairing cluster.*refcount=1 reference=0'
311
+
312
+if $loopdev; then
313
+ sudo -n losetup -d "$export_mp"
314
+else
315
+ qsd_pid=$(cat "$TEST_DIR/qsd.pid")
316
+ kill -TERM "$qsd_pid"
317
+ # Wait for process to exit (cannot `wait` because the QSD is daemonized)
318
+ while [ -f "$TEST_DIR/qsd.pid" ]; do
319
+ true
320
+ done
321
+fi
322
+
323
# success, all done
324
echo '*** done'
325
rm -f $seq.full
326
diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out
327
index XXXXXXX..XXXXXXX 100644
328
--- a/tests/qemu-iotests/108.out
329
+++ b/tests/qemu-iotests/108.out
330
@@ -XXX,XX +XXX,XX @@ The following inconsistencies were found and repaired:
331
0 leaked clusters
332
1 corruptions
333
334
+Double checking the fixed image now...
335
+No errors were found on the image.
336
+
337
+=== Check rebuilt reftable location ===
338
+
339
+--- Does the image size increase? ---
340
+
341
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
342
+wrote 65536/65536 bytes at offset 0
343
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
344
+
345
+ERROR cluster 0 refcount=0 reference=1
346
+Rebuilding refcount structure
347
+The following inconsistencies were found and repaired:
348
+
349
+ 0 leaked clusters
350
+ 1 corruptions
351
+
352
+Double checking the fixed image now...
353
+No errors were found on the image.
354
+
355
+OK: Image size did not change
356
+
357
+--- Will the reftable occupy a hole specifically left for it? ---
358
+
359
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=9437184
360
+wrote 8388608/8388608 bytes at offset 0
361
+8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
362
+discard 512/512 bytes at offset 0
363
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
364
+discard 1024/1024 bytes at offset 4096
365
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
366
+
367
+ERROR cluster 0 refcount=0 reference=1
368
+Rebuilding refcount structure
369
+The following inconsistencies were found and repaired:
370
+
371
+ 0 leaked clusters
372
+ 1 corruptions
373
+
374
+Double checking the fixed image now...
375
+No errors were found on the image.
376
+
377
+OK: Reftable is where we expect it
378
+
379
+--- Rebuilding refcount structures on block devices ---
380
+
381
+{ "execute": "qmp_capabilities" }
382
+{"return": {}}
383
+{ "execute": "blockdev-create",
384
+ "arguments": {
385
+ "job-id": "create",
386
+ "options": {
387
+ "driver": "IMGFMT",
388
+ "file": "file",
389
+ "size": 67108864,
390
+ "cluster-size": 512
391
+ } } }
392
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "create"}}
393
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "create"}}
394
+{"return": {}}
395
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "create"}}
396
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "create"}}
397
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "create"}}
398
+{ "execute": "job-dismiss", "arguments": { "id": "create" } }
399
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "create"}}
400
+{"return": {}}
401
+{ "execute": "quit" }
402
+{"return": {}}
403
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
404
+
405
+wrote 65536/65536 bytes at offset 0
406
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
407
+
408
+ERROR cluster 0 refcount=0 reference=1
409
+Rebuilding refcount structure
410
+The following inconsistencies were found and repaired:
411
+
412
+ 0 leaked clusters
413
+ 1 corruptions
414
+
415
Double checking the fixed image now...
416
No errors were found on the image.
417
*** done
418
--
419
2.35.1
diff view generated by jsdifflib
1
Instead of fprint()-ing error messages in rebuild_refcount_structure()
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
and its rebuild_refcounts_write_refblocks() helper, pass them through an
3
Error object to qcow2_check_refcounts() (which will then print it).
4
2
5
Suggested-by: Eric Blake <eblake@redhat.com>
3
The thread pool regulates itself: when idle, it kills threads until
6
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
4
empty, when in demand, it creates new threads until full. This behaviour
7
Message-Id: <20220405134652.19278-4-hreitz@redhat.com>
5
doesn't play well with latency sensitive workloads where the price of
8
Reviewed-by: Eric Blake <eblake@redhat.com>
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
In order to mitigate this let's introduce a new 'EventLoopBase'
11
property to set the thread pool size. The threads will be created during
12
the pool's initialization or upon updating the property's value, remain
13
available during its lifetime regardless of demand, and destroyed upon
14
freeing it. A properly characterized workload will then be able to
15
configure the pool to avoid any latency spikes.
16
17
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Acked-by: Markus Armbruster <armbru@redhat.com>
20
Message-id: 20220425075723.20019-4-nsaenzju@redhat.com
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
22
---
10
block/qcow2-refcount.c | 33 +++++++++++++++++++--------------
23
qapi/qom.json | 10 +++++-
11
1 file changed, 19 insertions(+), 14 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(-)
12
34
13
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
35
diff --git a/qapi/qom.json b/qapi/qom.json
14
index XXXXXXX..XXXXXXX 100644
36
index XXXXXXX..XXXXXXX 100644
15
--- a/block/qcow2-refcount.c
37
--- a/qapi/qom.json
16
+++ b/block/qcow2-refcount.c
38
+++ b/qapi/qom.json
17
@@ -XXX,XX +XXX,XX @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs,
39
@@ -XXX,XX +XXX,XX @@
18
static int rebuild_refcounts_write_refblocks(
40
# 0 means that the engine will use its default.
19
BlockDriverState *bs, void **refcount_table, int64_t *nb_clusters,
41
# (default: 0)
20
int64_t first_cluster, int64_t end_cluster,
42
#
21
- uint64_t **on_disk_reftable_ptr, uint32_t *on_disk_reftable_entries_ptr
43
+# @thread-pool-min: minimum number of threads reserved in the thread pool
22
+ uint64_t **on_disk_reftable_ptr, uint32_t *on_disk_reftable_entries_ptr,
44
+# (default:0)
23
+ Error **errp
45
+#
24
)
46
+# @thread-pool-max: maximum number of threads the thread pool can contain
47
+# (default:64)
48
+#
49
# Since: 7.1
50
##
51
{ 'struct': 'EventLoopBaseProperties',
52
- 'data': { '*aio-max-batch': 'int' } }
53
+ 'data': { '*aio-max-batch': 'int',
54
+ '*thread-pool-min': 'int',
55
+ '*thread-pool-max': 'int' } }
56
57
##
58
# @IothreadProperties:
59
diff --git a/include/block/aio.h b/include/block/aio.h
60
index XXXXXXX..XXXXXXX 100644
61
--- a/include/block/aio.h
62
+++ b/include/block/aio.h
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
65
QEMUBH *co_schedule_bh;
66
67
+ int thread_pool_min;
68
+ int thread_pool_max;
69
/* Thread pool for performing work and receiving completion callbacks.
70
* Has its own locking.
71
*/
72
@@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
73
void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
74
Error **errp);
75
76
+/**
77
+ * aio_context_set_thread_pool_params:
78
+ * @ctx: the aio context
79
+ * @min: min number of threads to have readily available in the thread pool
80
+ * @min: max number of threads the thread pool can contain
81
+ */
82
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
83
+ int64_t max, Error **errp);
84
#endif
85
diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h
86
index XXXXXXX..XXXXXXX 100644
87
--- a/include/block/thread-pool.h
88
+++ b/include/block/thread-pool.h
89
@@ -XXX,XX +XXX,XX @@
90
91
#include "block/block.h"
92
93
+#define THREAD_POOL_MAX_THREADS_DEFAULT 64
94
+
95
typedef int ThreadPoolFunc(void *opaque);
96
97
typedef struct ThreadPool ThreadPool;
98
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
99
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
100
ThreadPoolFunc *func, void *arg);
101
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
102
+void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx);
103
104
#endif
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBase {
110
111
/* AioContext AIO engine parameters */
112
int64_t aio_max_batch;
113
+
114
+ /* AioContext thread pool parameters */
115
+ int64_t thread_pool_min;
116
+ int64_t thread_pool_max;
117
};
118
#endif
119
diff --git a/event-loop-base.c b/event-loop-base.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/event-loop-base.c
122
+++ b/event-loop-base.c
123
@@ -XXX,XX +XXX,XX @@
124
#include "qemu/osdep.h"
125
#include "qom/object_interfaces.h"
126
#include "qapi/error.h"
127
+#include "block/thread-pool.h"
128
#include "sysemu/event-loop-base.h"
129
130
typedef struct {
131
@@ -XXX,XX +XXX,XX @@ typedef struct {
132
ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
133
} EventLoopBaseParamInfo;
134
135
+static void event_loop_base_instance_init(Object *obj)
136
+{
137
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
138
+
139
+ base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
140
+}
141
+
142
static EventLoopBaseParamInfo aio_max_batch_info = {
143
"aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
144
};
145
+static EventLoopBaseParamInfo thread_pool_min_info = {
146
+ "thread-pool-min", offsetof(EventLoopBase, thread_pool_min),
147
+};
148
+static EventLoopBaseParamInfo thread_pool_max_info = {
149
+ "thread-pool-max", offsetof(EventLoopBase, thread_pool_max),
150
+};
151
152
static void event_loop_base_get_param(Object *obj, Visitor *v,
153
const char *name, void *opaque, Error **errp)
154
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
155
event_loop_base_get_param,
156
event_loop_base_set_param,
157
NULL, &aio_max_batch_info);
158
+ object_class_property_add(klass, "thread-pool-min", "int",
159
+ event_loop_base_get_param,
160
+ event_loop_base_set_param,
161
+ NULL, &thread_pool_min_info);
162
+ object_class_property_add(klass, "thread-pool-max", "int",
163
+ event_loop_base_get_param,
164
+ event_loop_base_set_param,
165
+ NULL, &thread_pool_max_info);
166
}
167
168
static const TypeInfo event_loop_base_info = {
169
.name = TYPE_EVENT_LOOP_BASE,
170
.parent = TYPE_OBJECT,
171
.instance_size = sizeof(EventLoopBase),
172
+ .instance_init = event_loop_base_instance_init,
173
.class_size = sizeof(EventLoopBaseClass),
174
.class_init = event_loop_base_class_init,
175
.abstract = true,
176
diff --git a/iothread.c b/iothread.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/iothread.c
179
+++ b/iothread.c
180
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
181
aio_context_set_aio_params(iothread->ctx,
182
iothread->parent_obj.aio_max_batch,
183
errp);
184
+
185
+ aio_context_set_thread_pool_params(iothread->ctx, base->thread_pool_min,
186
+ base->thread_pool_max, errp);
187
}
188
189
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)
25
{
252
{
26
BDRVQcow2State *s = bs->opaque;
253
+ ERRP_GUARD();
27
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcounts_write_refblocks(
254
+
28
nb_clusters,
255
if (!qemu_aio_context) {
29
&first_free_cluster);
256
error_setg(errp, "qemu aio context not ready");
30
if (refblock_offset < 0) {
257
return;
31
- fprintf(stderr, "ERROR allocating refblock: %s\n",
258
}
32
- strerror(-refblock_offset));
259
33
+ error_setg_errno(errp, -refblock_offset,
260
aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
34
+ "ERROR allocating refblock");
261
+ if (*errp) {
35
return refblock_offset;
262
+ return;
36
}
263
+ }
37
264
+
38
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcounts_write_refblocks(
265
+ aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min,
39
on_disk_reftable_entries *
266
+ base->thread_pool_max, errp);
40
REFTABLE_ENTRY_SIZE);
267
}
41
if (!on_disk_reftable) {
268
42
+ error_setg(errp, "ERROR allocating reftable memory");
269
MainLoop *mloop;
43
return -ENOMEM;
270
diff --git a/util/thread-pool.c b/util/thread-pool.c
44
}
271
index XXXXXXX..XXXXXXX 100644
45
272
--- a/util/thread-pool.c
46
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcounts_write_refblocks(
273
+++ b/util/thread-pool.c
47
ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset,
274
@@ -XXX,XX +XXX,XX @@ struct ThreadPool {
48
s->cluster_size, false);
275
QemuMutex lock;
49
if (ret < 0) {
276
QemuCond worker_stopped;
50
- fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
277
QemuSemaphore sem;
51
+ error_setg_errno(errp, -ret, "ERROR writing refblock");
278
- int max_threads;
52
return ret;
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;
53
}
320
}
54
321
55
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcounts_write_refblocks(
322
@@ -XXX,XX +XXX,XX @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg)
56
ret = bdrv_pwrite(bs->file, refblock_offset, on_disk_refblock,
323
thread_pool_submit_aio(pool, func, arg, NULL, NULL);
57
s->cluster_size);
324
}
58
if (ret < 0) {
325
59
- fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
326
+void thread_pool_update_params(ThreadPool *pool, AioContext *ctx)
60
+ error_setg_errno(errp, -ret, "ERROR writing refblock");
327
+{
61
return ret;
328
+ qemu_mutex_lock(&pool->lock);
62
}
329
+
63
330
+ pool->min_threads = ctx->thread_pool_min;
64
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcounts_write_refblocks(
331
+ pool->max_threads = ctx->thread_pool_max;
65
static int rebuild_refcount_structure(BlockDriverState *bs,
332
+
66
BdrvCheckResult *res,
333
+ /*
67
void **refcount_table,
334
+ * We either have to:
68
- int64_t *nb_clusters)
335
+ * - Increase the number available of threads until over the min_threads
69
+ int64_t *nb_clusters,
336
+ * threshold.
70
+ Error **errp)
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)
71
{
354
{
72
BDRVQcow2State *s = bs->opaque;
355
if (!ctx) {
73
int64_t reftable_offset = -1;
356
@@ -XXX,XX +XXX,XX @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
74
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcount_structure(BlockDriverState *bs,
357
qemu_mutex_init(&pool->lock);
75
rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters,
358
qemu_cond_init(&pool->worker_stopped);
76
0, *nb_clusters,
359
qemu_sem_init(&pool->sem, 0);
77
&on_disk_reftable,
360
- pool->max_threads = 64;
78
- &on_disk_reftable_entries);
361
pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool);
79
+ &on_disk_reftable_entries, errp);
362
80
if (reftable_size_changed < 0) {
363
QLIST_INIT(&pool->head);
81
res->check_errors++;
364
QTAILQ_INIT(&pool->request_list);
82
ret = reftable_size_changed;
365
+
83
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcount_structure(BlockDriverState *bs,
366
+ thread_pool_update_params(pool, ctx);
84
refcount_table, nb_clusters,
367
}
85
&first_free_cluster);
368
86
if (reftable_offset < 0) {
369
ThreadPool *thread_pool_new(AioContext *ctx)
87
- fprintf(stderr, "ERROR allocating reftable: %s\n",
88
- strerror(-reftable_offset));
89
+ error_setg_errno(errp, -reftable_offset,
90
+ "ERROR allocating reftable");
91
res->check_errors++;
92
ret = reftable_offset;
93
goto fail;
94
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcount_structure(BlockDriverState *bs,
95
reftable_start_cluster,
96
reftable_end_cluster,
97
&on_disk_reftable,
98
- &on_disk_reftable_entries);
99
+ &on_disk_reftable_entries, errp);
100
if (reftable_size_changed < 0) {
101
res->check_errors++;
102
ret = reftable_size_changed;
103
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcount_structure(BlockDriverState *bs,
104
ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, reftable_length,
105
false);
106
if (ret < 0) {
107
- fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
108
+ error_setg_errno(errp, -ret, "ERROR writing reftable");
109
goto fail;
110
}
111
112
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcount_structure(BlockDriverState *bs,
113
ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable,
114
reftable_length);
115
if (ret < 0) {
116
- fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
117
+ error_setg_errno(errp, -ret, "ERROR writing reftable");
118
goto fail;
119
}
120
121
@@ -XXX,XX +XXX,XX @@ static int rebuild_refcount_structure(BlockDriverState *bs,
122
&reftable_offset_and_clusters,
123
sizeof(reftable_offset_and_clusters));
124
if (ret < 0) {
125
- fprintf(stderr, "ERROR setting reftable: %s\n", strerror(-ret));
126
+ error_setg_errno(errp, -ret, "ERROR setting reftable");
127
goto fail;
128
}
129
130
@@ -XXX,XX +XXX,XX @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
131
if (rebuild && (fix & BDRV_FIX_ERRORS)) {
132
BdrvCheckResult old_res = *res;
133
int fresh_leaks = 0;
134
+ Error *local_err = NULL;
135
136
fprintf(stderr, "Rebuilding refcount structure\n");
137
ret = rebuild_refcount_structure(bs, res, &refcount_table,
138
- &nb_clusters);
139
+ &nb_clusters, &local_err);
140
if (ret < 0) {
141
+ error_report_err(local_err);
142
goto fail;
143
}
144
145
--
370
--
146
2.35.1
371
2.35.1
diff view generated by jsdifflib