1
The following changes since commit dac03af5d5482ec7ee9c23db467bb7230b33c0d9:
1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
2
2
3
Merge remote-tracking branch 'remotes/rth/tags/pull-axp-20190825' into staging (2019-08-27 10:00:51 +0100)
3
Merge tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu into staging (2022-05-04 03:42:49 -0700)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://github.com/stefanha/qemu.git tags/block-pull-request
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
8
8
9
for you to fetch changes up to 5396234b96a2ac743f48644529771498e036e698:
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
10
10
11
block/qcow2: implement .bdrv_co_pwritev(_compressed)_part (2019-08-27 14:58:42 +0100)
11
util/event-loop-base: Introduce options to set the thread pool size (2022-05-04 17:02:19 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Pull request
14
Pull request
15
15
16
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
17
used for async I/O.
18
16
----------------------------------------------------------------
19
----------------------------------------------------------------
17
20
18
Vladimir Sementsov-Ogievskiy (12):
21
Nicolas Saenz Julienne (3):
19
util/iov: introduce qemu_iovec_init_extended
22
Introduce event-loop-base abstract class
20
util/iov: improve qemu_iovec_is_zero
23
util/main-loop: Introduce the main loop into QOM
21
block/io: refactor padding
24
util/event-loop-base: Introduce options to set the thread pool size
22
block: define .*_part io handlers in BlockDriver
23
block/io: bdrv_co_do_copy_on_readv: use and support qiov_offset
24
block/io: bdrv_co_do_copy_on_readv: lazy allocation
25
block/io: bdrv_aligned_preadv: use and support qiov_offset
26
block/io: bdrv_aligned_pwritev: use and support qiov_offset
27
block/io: introduce bdrv_co_p{read, write}v_part
28
block/qcow2: refactor qcow2_co_preadv to use buffer-based io
29
block/qcow2: implement .bdrv_co_preadv_part
30
block/qcow2: implement .bdrv_co_pwritev(_compressed)_part
31
25
32
block/qcow2.h | 1 +
26
qapi/qom.json | 43 ++++++++--
33
include/block/block_int.h | 21 ++
27
meson.build | 26 +++---
34
include/qemu/iov.h | 10 +-
28
include/block/aio.h | 10 +++
35
block/backup.c | 2 +-
29
include/block/thread-pool.h | 3 +
36
block/io.c | 541 +++++++++++++++++++++++---------------
30
include/qemu/main-loop.h | 10 +++
37
block/qcow2-cluster.c | 14 +-
31
include/sysemu/event-loop-base.h | 41 +++++++++
38
block/qcow2.c | 131 +++++----
32
include/sysemu/iothread.h | 6 +-
39
qemu-img.c | 4 +-
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
40
util/iov.c | 153 +++++++++--
34
iothread.c | 68 +++++----------
41
9 files changed, 568 insertions(+), 309 deletions(-)
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.21.0
44
2.35.1
45
46
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
Introduce new initialization API, to create requests with padding. Will
3
Introduce the 'event-loop-base' abstract class, it'll hold the
4
be used in the following patch. New API uses qemu_iovec_init_buf if
4
properties common to all event loops and provide the necessary hooks for
5
resulting io vector has only one element, to avoid extra allocations.
5
their creation and maintenance. Then have iothread inherit from it.
6
So, we need to update qemu_iovec_destroy to support destroying such
6
7
QIOVs.
7
EventLoopBaseClass is defined as user creatable and provides a hook for
8
8
its children to attach themselves to the user creatable class 'complete'
9
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
function. It also provides an update_params() callback to propagate
10
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
10
property changes onto its children.
11
Message-id: 20190604161514.262241-2-vsementsov@virtuozzo.com
11
12
Message-Id: <20190604161514.262241-2-vsementsov@virtuozzo.com>
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
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
39
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
---
40
---
15
include/qemu/iov.h | 7 +++
41
qapi/qom.json | 22 +++++--
16
util/iov.c | 112 +++++++++++++++++++++++++++++++++++++++++++--
42
meson.build | 23 ++++---
17
2 files changed, 114 insertions(+), 5 deletions(-)
43
include/sysemu/event-loop-base.h | 36 +++++++++++
18
44
include/sysemu/iothread.h | 6 +-
19
diff --git a/include/qemu/iov.h b/include/qemu/iov.h
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
20
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
21
--- a/include/qemu/iov.h
53
--- a/qapi/qom.json
22
+++ b/include/qemu/iov.h
54
+++ b/qapi/qom.json
23
@@ -XXX,XX +XXX,XX @@ static inline void *qemu_iovec_buf(QEMUIOVector *qiov)
55
@@ -XXX,XX +XXX,XX @@
24
56
'*repeat': 'bool',
25
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint);
57
'*grab-toggle': 'GrabToggleKeys' } }
26
void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov);
58
27
+void qemu_iovec_init_extended(
59
+##
28
+ QEMUIOVector *qiov,
60
+# @EventLoopBaseProperties:
29
+ void *head_buf, size_t head_len,
61
+#
30
+ QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len,
62
+# Common properties for event loops
31
+ void *tail_buf, size_t tail_len);
63
+#
32
+void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source,
64
+# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
33
+ size_t offset, size_t len);
65
+# 0 means that the engine will use its default.
34
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
66
+# (default: 0)
35
void qemu_iovec_concat(QEMUIOVector *dst,
67
+#
36
QEMUIOVector *src, size_t soffset, size_t sbytes);
68
+# Since: 7.1
37
diff --git a/util/iov.c b/util/iov.c
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
38
index XXXXXXX..XXXXXXX 100644
98
index XXXXXXX..XXXXXXX 100644
39
--- a/util/iov.c
99
--- a/meson.build
40
+++ b/util/iov.c
100
+++ b/meson.build
41
@@ -XXX,XX +XXX,XX @@ void qemu_iovec_concat(QEMUIOVector *dst,
101
@@ -XXX,XX +XXX,XX @@ subdir('qom')
42
qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes);
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);
43
}
353
}
44
354
45
+/*
355
-static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
46
+ * qiov_find_iov
356
+static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
47
+ *
357
{
48
+ * Return pointer to iovec structure, where byte at @offset in original vector
358
+ IOThread *iothread = IOTHREAD(base);
49
+ * @iov exactly is.
359
ERRP_GUARD();
50
+ * Set @remaining_offset to be offset inside that iovec to the same byte.
360
51
+ */
361
+ if (!iothread->ctx) {
52
+static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset,
362
+ return;
53
+ size_t *remaining_offset)
363
+ }
54
+{
364
+
55
+ while (offset > 0 && offset >= iov->iov_len) {
365
aio_context_set_poll_params(iothread->ctx,
56
+ offset -= iov->iov_len;
366
iothread->poll_max_ns,
57
+ iov++;
367
iothread->poll_grow,
58
+ }
368
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
59
+ *remaining_offset = offset;
369
}
60
+
370
61
+ return iov;
371
aio_context_set_aio_params(iothread->ctx,
62
+}
372
- iothread->aio_max_batch,
63
+
373
+ iothread->parent_obj.aio_max_batch,
64
+/*
374
errp);
65
+ * qiov_slice
66
+ *
67
+ * Find subarray of iovec's, containing requested range. @head would
68
+ * be offset in first iov (returned by the function), @tail would be
69
+ * count of extra bytes in last iovec (returned iov + @niov - 1).
70
+ */
71
+static struct iovec *qiov_slice(QEMUIOVector *qiov,
72
+ size_t offset, size_t len,
73
+ size_t *head, size_t *tail, int *niov)
74
+{
75
+ struct iovec *iov, *end_iov;
76
+
77
+ assert(offset + len <= qiov->size);
78
+
79
+ iov = iov_skip_offset(qiov->iov, offset, head);
80
+ end_iov = iov_skip_offset(iov, *head + len, tail);
81
+
82
+ if (*tail > 0) {
83
+ assert(*tail < end_iov->iov_len);
84
+ *tail = end_iov->iov_len - *tail;
85
+ end_iov++;
86
+ }
87
+
88
+ *niov = end_iov - iov;
89
+
90
+ return iov;
91
+}
92
+
93
+/*
94
+ * Compile new iovec, combining @head_buf buffer, sub-qiov of @mid_qiov,
95
+ * and @tail_buf buffer into new qiov.
96
+ */
97
+void qemu_iovec_init_extended(
98
+ QEMUIOVector *qiov,
99
+ void *head_buf, size_t head_len,
100
+ QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len,
101
+ void *tail_buf, size_t tail_len)
102
+{
103
+ size_t mid_head, mid_tail;
104
+ int total_niov, mid_niov = 0;
105
+ struct iovec *p, *mid_iov;
106
+
107
+ if (mid_len) {
108
+ mid_iov = qiov_slice(mid_qiov, mid_offset, mid_len,
109
+ &mid_head, &mid_tail, &mid_niov);
110
+ }
111
+
112
+ total_niov = !!head_len + mid_niov + !!tail_len;
113
+ if (total_niov == 1) {
114
+ qemu_iovec_init_buf(qiov, NULL, 0);
115
+ p = &qiov->local_iov;
116
+ } else {
117
+ qiov->niov = qiov->nalloc = total_niov;
118
+ qiov->size = head_len + mid_len + tail_len;
119
+ p = qiov->iov = g_new(struct iovec, qiov->niov);
120
+ }
121
+
122
+ if (head_len) {
123
+ p->iov_base = head_buf;
124
+ p->iov_len = head_len;
125
+ p++;
126
+ }
127
+
128
+ if (mid_len) {
129
+ memcpy(p, mid_iov, mid_niov * sizeof(*p));
130
+ p[0].iov_base = (uint8_t *)p[0].iov_base + mid_head;
131
+ p[0].iov_len -= mid_head;
132
+ p[mid_niov - 1].iov_len -= mid_tail;
133
+ p += mid_niov;
134
+ }
135
+
136
+ if (tail_len) {
137
+ p->iov_base = tail_buf;
138
+ p->iov_len = tail_len;
139
+ }
140
+}
141
+
142
/*
143
* Check if the contents of the iovecs are all zero
144
*/
145
@@ -XXX,XX +XXX,XX @@ bool qemu_iovec_is_zero(QEMUIOVector *qiov)
146
return true;
147
}
375
}
148
376
149
+void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source,
377
-static void iothread_complete(UserCreatable *obj, Error **errp)
150
+ size_t offset, size_t len)
378
+
151
+{
379
+static void iothread_init(EventLoopBase *base, Error **errp)
152
+ qemu_iovec_init_extended(qiov, NULL, 0, source, offset, len, NULL, 0);
153
+}
154
+
155
void qemu_iovec_destroy(QEMUIOVector *qiov)
156
{
380
{
157
- assert(qiov->nalloc != -1);
381
Error *local_error = NULL;
158
+ if (qiov->nalloc != -1) {
382
- IOThread *iothread = IOTHREAD(obj);
159
+ g_free(qiov->iov);
383
+ IOThread *iothread = IOTHREAD(base);
160
+ }
384
char *thread_name;
161
385
162
- qemu_iovec_reset(qiov);
386
iothread->stopping = false;
163
- g_free(qiov->iov);
387
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
164
- qiov->nalloc = 0;
388
*/
165
- qiov->iov = NULL;
389
iothread_init_gcontext(iothread);
166
+ memset(qiov, 0, sizeof(*qiov));
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
}
167
}
417
}
168
418
169
void qemu_iovec_reset(QEMUIOVector *qiov)
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
420
- const char *name, void *opaque, Error **errp)
421
-{
422
- IOThreadParamInfo *info = opaque;
423
-
424
- iothread_get_param(obj, v, name, info, errp);
425
-}
426
-
427
-static void iothread_set_aio_param(Object *obj, Visitor *v,
428
- const char *name, void *opaque, Error **errp)
429
-{
430
- IOThread *iothread = IOTHREAD(obj);
431
- IOThreadParamInfo *info = opaque;
432
-
433
- if (!iothread_set_param(obj, v, name, info, errp)) {
434
- return;
435
- }
436
-
437
- if (iothread->ctx) {
438
- aio_context_set_aio_params(iothread->ctx,
439
- iothread->aio_max_batch,
440
- errp);
441
- }
442
-}
443
-
444
static void iothread_class_init(ObjectClass *klass, void *class_data)
445
{
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
447
- ucc->complete = iothread_complete;
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
449
+
450
+ bc->init = iothread_init;
451
+ bc->update_params = iothread_set_aio_context_params;
452
453
object_class_property_add(klass, "poll-max-ns", "int",
454
iothread_get_poll_param,
455
@@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
456
iothread_get_poll_param,
457
iothread_set_poll_param,
458
NULL, &poll_shrink_info);
459
- object_class_property_add(klass, "aio-max-batch", "int",
460
- iothread_get_aio_param,
461
- iothread_set_aio_param,
462
- NULL, &aio_max_batch_info);
463
}
464
465
static const TypeInfo iothread_info = {
466
.name = TYPE_IOTHREAD,
467
- .parent = TYPE_OBJECT,
468
+ .parent = TYPE_EVENT_LOOP_BASE,
469
.class_init = iothread_class_init,
470
.instance_size = sizeof(IOThread),
471
.instance_init = iothread_instance_init,
472
.instance_finalize = iothread_instance_finalize,
473
- .interfaces = (InterfaceInfo[]) {
474
- {TYPE_USER_CREATABLE},
475
- {}
476
- },
477
};
478
479
static void iothread_register_types(void)
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
481
info->poll_max_ns = iothread->poll_max_ns;
482
info->poll_grow = iothread->poll_grow;
483
info->poll_shrink = iothread->poll_shrink;
484
- info->aio_max_batch = iothread->aio_max_batch;
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
486
487
QAPI_LIST_APPEND(*tail, info);
488
return 0;
170
--
489
--
171
2.21.0
490
2.35.1
172
173
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
We'll need to check a part of qiov soon, so implement it now.
4
5
Optimization with align down to 4 * sizeof(long) is dropped due to:
6
1. It is strange: it aligns length of the buffer, but where is a
7
guarantee that buffer pointer is aligned itself?
8
2. buffer_is_zero() is a better place for optimizations and it has
9
them.
10
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Message-id: 20190604161514.262241-3-vsementsov@virtuozzo.com
14
Message-Id: <20190604161514.262241-3-vsementsov@virtuozzo.com>
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
16
---
17
include/qemu/iov.h | 2 +-
18
block/io.c | 2 +-
19
util/iov.c | 31 +++++++++++++++++++------------
20
3 files changed, 21 insertions(+), 14 deletions(-)
21
22
diff --git a/include/qemu/iov.h b/include/qemu/iov.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/qemu/iov.h
25
+++ b/include/qemu/iov.h
26
@@ -XXX,XX +XXX,XX @@ void qemu_iovec_concat(QEMUIOVector *dst,
27
size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
28
struct iovec *src_iov, unsigned int src_cnt,
29
size_t soffset, size_t sbytes);
30
-bool qemu_iovec_is_zero(QEMUIOVector *qiov);
31
+bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t qiov_offeset, size_t bytes);
32
void qemu_iovec_destroy(QEMUIOVector *qiov);
33
void qemu_iovec_reset(QEMUIOVector *qiov);
34
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,
35
diff --git a/block/io.c b/block/io.c
36
index XXXXXXX..XXXXXXX 100644
37
--- a/block/io.c
38
+++ b/block/io.c
39
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
40
41
if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF &&
42
!(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_pwrite_zeroes &&
43
- qemu_iovec_is_zero(qiov)) {
44
+ qemu_iovec_is_zero(qiov, 0, qiov->size)) {
45
flags |= BDRV_REQ_ZERO_WRITE;
46
if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) {
47
flags |= BDRV_REQ_MAY_UNMAP;
48
diff --git a/util/iov.c b/util/iov.c
49
index XXXXXXX..XXXXXXX 100644
50
--- a/util/iov.c
51
+++ b/util/iov.c
52
@@ -XXX,XX +XXX,XX @@ void qemu_iovec_init_extended(
53
}
54
55
/*
56
- * Check if the contents of the iovecs are all zero
57
+ * Check if the contents of subrange of qiov data is all zeroes.
58
*/
59
-bool qemu_iovec_is_zero(QEMUIOVector *qiov)
60
+bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes)
61
{
62
- int i;
63
- for (i = 0; i < qiov->niov; i++) {
64
- size_t offs = QEMU_ALIGN_DOWN(qiov->iov[i].iov_len, 4 * sizeof(long));
65
- uint8_t *ptr = qiov->iov[i].iov_base;
66
- if (offs && !buffer_is_zero(qiov->iov[i].iov_base, offs)) {
67
+ struct iovec *iov;
68
+ size_t current_offset;
69
+
70
+ assert(offset + bytes <= qiov->size);
71
+
72
+ iov = iov_skip_offset(qiov->iov, offset, &current_offset);
73
+
74
+ while (bytes) {
75
+ uint8_t *base = (uint8_t *)iov->iov_base + current_offset;
76
+ size_t len = MIN(iov->iov_len - current_offset, bytes);
77
+
78
+ if (!buffer_is_zero(base, len)) {
79
return false;
80
}
81
- for (; offs < qiov->iov[i].iov_len; offs++) {
82
- if (ptr[offs]) {
83
- return false;
84
- }
85
- }
86
+
87
+ current_offset = 0;
88
+ bytes -= len;
89
+ iov++;
90
}
91
+
92
return true;
93
}
94
95
--
96
2.21.0
97
98
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
We have similar padding code in bdrv_co_pwritev,
3
'event-loop-base' provides basic property handling for all 'AioContext'
4
bdrv_co_do_pwrite_zeroes and bdrv_co_preadv. Let's combine and unify
4
based event loops. So let's define a new 'MainLoopClass' that inherits
5
it.
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
[Squashed in Vladimir's qemu-iotests 077 fix
7
Only one instance of 'MainLoopClass' might be created at any time.
8
--Stefan]
8
9
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
mark 'MainLoop' as non-deletable.
11
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
11
12
Message-id: 20190604161514.262241-4-vsementsov@virtuozzo.com
12
[1] For example:
13
Message-Id: <20190604161514.262241-4-vsementsov@virtuozzo.com>
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
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
---
20
---
16
block/io.c | 365 +++++++++++++++++++++++++++++------------------------
21
qapi/qom.json | 13 ++++++++
17
1 file changed, 200 insertions(+), 165 deletions(-)
22
meson.build | 3 +-
18
23
include/qemu/main-loop.h | 10 ++++++
19
diff --git a/block/io.c b/block/io.c
24
include/sysemu/event-loop-base.h | 1 +
20
index XXXXXXX..XXXXXXX 100644
25
event-loop-base.c | 13 ++++++++
21
--- a/block/io.c
26
util/main-loop.c | 56 ++++++++++++++++++++++++++++++++
22
+++ b/block/io.c
27
6 files changed, 95 insertions(+), 1 deletion(-)
23
@@ -XXX,XX +XXX,XX @@ out:
28
29
diff --git a/qapi/qom.json b/qapi/qom.json
30
index XXXXXXX..XXXXXXX 100644
31
--- a/qapi/qom.json
32
+++ b/qapi/qom.json
33
@@ -XXX,XX +XXX,XX @@
34
'*poll-grow': 'int',
35
'*poll-shrink': 'int' } }
36
37
+##
38
+# @MainLoopProperties:
39
+#
40
+# Properties for the main-loop object.
41
+#
42
+# Since: 7.1
43
+##
44
+{ 'struct': 'MainLoopProperties',
45
+ 'base': 'EventLoopBaseProperties',
46
+ 'data': {} }
47
+
48
##
49
# @MemoryBackendProperties:
50
#
51
@@ -XXX,XX +XXX,XX @@
52
{ 'name': 'input-linux',
53
'if': 'CONFIG_LINUX' },
54
'iothread',
55
+ 'main-loop',
56
{ 'name': 'memory-backend-epc',
57
'if': 'CONFIG_LINUX' },
58
'memory-backend-file',
59
@@ -XXX,XX +XXX,XX @@
60
'input-linux': { 'type': 'InputLinuxProperties',
61
'if': 'CONFIG_LINUX' },
62
'iothread': 'IothreadProperties',
63
+ 'main-loop': 'MainLoopProperties',
64
'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties',
65
'if': 'CONFIG_LINUX' },
66
'memory-backend-file': 'MemoryBackendFileProperties',
67
diff --git a/meson.build b/meson.build
68
index XXXXXXX..XXXXXXX 100644
69
--- a/meson.build
70
+++ b/meson.build
71
@@ -XXX,XX +XXX,XX @@ libqemuutil = static_library('qemuutil',
72
sources: util_ss.sources() + stub_ss.sources() + genh,
73
dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman])
74
qemuutil = declare_dependency(link_with: libqemuutil,
75
- sources: genh + version_res)
76
+ sources: genh + version_res,
77
+ dependencies: [event_loop_base])
78
79
if have_system or have_user
80
decodetree = generator(find_program('scripts/decodetree.py'),
81
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/qemu/main-loop.h
84
+++ b/include/qemu/main-loop.h
85
@@ -XXX,XX +XXX,XX @@
86
#define QEMU_MAIN_LOOP_H
87
88
#include "block/aio.h"
89
+#include "qom/object.h"
90
+#include "sysemu/event-loop-base.h"
91
92
#define SIG_IPI SIGUSR1
93
94
+#define TYPE_MAIN_LOOP "main-loop"
95
+OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP)
96
+
97
+struct MainLoop {
98
+ EventLoopBase parent_obj;
99
+};
100
+typedef struct MainLoop MainLoop;
101
+
102
/**
103
* qemu_init_main_loop: Set up the process so that it can run the main loop.
104
*
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBaseClass {
110
111
void (*init)(EventLoopBase *base, Error **errp);
112
void (*update_params)(EventLoopBase *base, Error **errp);
113
+ bool (*can_be_deleted)(EventLoopBase *base);
114
};
115
116
struct EventLoopBase {
117
diff --git a/event-loop-base.c b/event-loop-base.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/event-loop-base.c
120
+++ b/event-loop-base.c
121
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_complete(UserCreatable *uc, Error **errp)
122
}
24
}
123
}
25
124
26
/*
125
+static bool event_loop_base_can_be_deleted(UserCreatable *uc)
27
- * Handle a read request in coroutine context
126
+{
28
+ * Request padding
127
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
29
+ *
128
+ EventLoopBase *backend = EVENT_LOOP_BASE(uc);
30
+ * |<---- align ----->| |<----- align ---->|
129
+
31
+ * |<- head ->|<------------- bytes ------------->|<-- tail -->|
130
+ if (bc->can_be_deleted) {
32
+ * | | | | | |
131
+ return bc->can_be_deleted(backend);
33
+ * -*----------$-------*-------- ... --------*-----$------------*---
34
+ * | | | | | |
35
+ * | offset | | end |
36
+ * ALIGN_DOWN(offset) ALIGN_UP(offset) ALIGN_DOWN(end) ALIGN_UP(end)
37
+ * [buf ... ) [tail_buf )
38
+ *
39
+ * @buf is an aligned allocation needed to store @head and @tail paddings. @head
40
+ * is placed at the beginning of @buf and @tail at the @end.
41
+ *
42
+ * @tail_buf is a pointer to sub-buffer, corresponding to align-sized chunk
43
+ * around tail, if tail exists.
44
+ *
45
+ * @merge_reads is true for small requests,
46
+ * if @buf_len == @head + bytes + @tail. In this case it is possible that both
47
+ * head and tail exist but @buf_len == align and @tail_buf == @buf.
48
*/
49
+typedef struct BdrvRequestPadding {
50
+ uint8_t *buf;
51
+ size_t buf_len;
52
+ uint8_t *tail_buf;
53
+ size_t head;
54
+ size_t tail;
55
+ bool merge_reads;
56
+ QEMUIOVector local_qiov;
57
+} BdrvRequestPadding;
58
+
59
+static bool bdrv_init_padding(BlockDriverState *bs,
60
+ int64_t offset, int64_t bytes,
61
+ BdrvRequestPadding *pad)
62
+{
63
+ uint64_t align = bs->bl.request_alignment;
64
+ size_t sum;
65
+
66
+ memset(pad, 0, sizeof(*pad));
67
+
68
+ pad->head = offset & (align - 1);
69
+ pad->tail = ((offset + bytes) & (align - 1));
70
+ if (pad->tail) {
71
+ pad->tail = align - pad->tail;
72
+ }
132
+ }
73
+
133
+
74
+ if ((!pad->head && !pad->tail) || !bytes) {
134
+ return true;
75
+ return false;
135
+}
136
+
137
static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
138
{
139
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
140
ucc->complete = event_loop_base_complete;
141
+ ucc->can_be_deleted = event_loop_base_can_be_deleted;
142
143
object_class_property_add(klass, "aio-max-batch", "int",
144
event_loop_base_get_param,
145
diff --git a/util/main-loop.c b/util/main-loop.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/util/main-loop.c
148
+++ b/util/main-loop.c
149
@@ -XXX,XX +XXX,XX @@
150
#include "qemu/error-report.h"
151
#include "qemu/queue.h"
152
#include "qemu/compiler.h"
153
+#include "qom/object.h"
154
155
#ifndef _WIN32
156
#include <sys/wait.h>
157
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
158
return 0;
159
}
160
161
+static void main_loop_update_params(EventLoopBase *base, Error **errp)
162
+{
163
+ if (!qemu_aio_context) {
164
+ error_setg(errp, "qemu aio context not ready");
165
+ return;
76
+ }
166
+ }
77
+
167
+
78
+ sum = pad->head + bytes + pad->tail;
168
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
79
+ pad->buf_len = (sum > align && pad->head && pad->tail) ? 2 * align : align;
169
+}
80
+ pad->buf = qemu_blockalign(bs, pad->buf_len);
170
+
81
+ pad->merge_reads = sum == pad->buf_len;
171
+MainLoop *mloop;
82
+ if (pad->tail) {
172
+
83
+ pad->tail_buf = pad->buf + pad->buf_len - align;
173
+static void main_loop_init(EventLoopBase *base, Error **errp)
174
+{
175
+ MainLoop *m = MAIN_LOOP(base);
176
+
177
+ if (mloop) {
178
+ error_setg(errp, "only one main-loop instance allowed");
179
+ return;
84
+ }
180
+ }
85
+
181
+
86
+ return true;
182
+ main_loop_update_params(base, errp);
87
+}
183
+
88
+
184
+ mloop = m;
89
+static int bdrv_padding_rmw_read(BdrvChild *child,
185
+ return;
90
+ BdrvTrackedRequest *req,
186
+}
91
+ BdrvRequestPadding *pad,
187
+
92
+ bool zero_middle)
188
+static bool main_loop_can_be_deleted(EventLoopBase *base)
93
+{
189
+{
94
+ QEMUIOVector local_qiov;
190
+ return false;
95
+ BlockDriverState *bs = child->bs;
191
+}
96
+ uint64_t align = bs->bl.request_alignment;
192
+
97
+ int ret;
193
+static void main_loop_class_init(ObjectClass *oc, void *class_data)
98
+
194
+{
99
+ assert(req->serialising && pad->buf);
195
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc);
100
+
196
+
101
+ if (pad->head || pad->merge_reads) {
197
+ bc->init = main_loop_init;
102
+ uint64_t bytes = pad->merge_reads ? pad->buf_len : align;
198
+ bc->update_params = main_loop_update_params;
103
+
199
+ bc->can_be_deleted = main_loop_can_be_deleted;
104
+ qemu_iovec_init_buf(&local_qiov, pad->buf, bytes);
200
+}
105
+
201
+
106
+ if (pad->head) {
202
+static const TypeInfo main_loop_info = {
107
+ bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
203
+ .name = TYPE_MAIN_LOOP,
108
+ }
204
+ .parent = TYPE_EVENT_LOOP_BASE,
109
+ if (pad->merge_reads && pad->tail) {
205
+ .class_init = main_loop_class_init,
110
+ bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
206
+ .instance_size = sizeof(MainLoop),
111
+ }
207
+};
112
+ ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes,
208
+
113
+ align, &local_qiov, 0);
209
+static void main_loop_register_types(void)
114
+ if (ret < 0) {
210
+{
115
+ return ret;
211
+ type_register_static(&main_loop_info);
116
+ }
212
+}
117
+ if (pad->head) {
213
+
118
+ bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
214
+type_init(main_loop_register_types)
119
+ }
215
+
120
+ if (pad->merge_reads && pad->tail) {
216
static int max_priority;
121
+ bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
217
122
+ }
218
#ifndef _WIN32
123
+
124
+ if (pad->merge_reads) {
125
+ goto zero_mem;
126
+ }
127
+ }
128
+
129
+ if (pad->tail) {
130
+ qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align);
131
+
132
+ bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
133
+ ret = bdrv_aligned_preadv(
134
+ child, req,
135
+ req->overlap_offset + req->overlap_bytes - align,
136
+ align, align, &local_qiov, 0);
137
+ if (ret < 0) {
138
+ return ret;
139
+ }
140
+ bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
141
+ }
142
+
143
+zero_mem:
144
+ if (zero_middle) {
145
+ memset(pad->buf + pad->head, 0, pad->buf_len - pad->head - pad->tail);
146
+ }
147
+
148
+ return 0;
149
+}
150
+
151
+static void bdrv_padding_destroy(BdrvRequestPadding *pad)
152
+{
153
+ if (pad->buf) {
154
+ qemu_vfree(pad->buf);
155
+ qemu_iovec_destroy(&pad->local_qiov);
156
+ }
157
+}
158
+
159
+/*
160
+ * bdrv_pad_request
161
+ *
162
+ * Exchange request parameters with padded request if needed. Don't include RMW
163
+ * read of padding, bdrv_padding_rmw_read() should be called separately if
164
+ * needed.
165
+ *
166
+ * All parameters except @bs are in-out: they represent original request at
167
+ * function call and padded (if padding needed) at function finish.
168
+ *
169
+ * Function always succeeds.
170
+ */
171
+static bool bdrv_pad_request(BlockDriverState *bs, QEMUIOVector **qiov,
172
+ int64_t *offset, unsigned int *bytes,
173
+ BdrvRequestPadding *pad)
174
+{
175
+ if (!bdrv_init_padding(bs, *offset, *bytes, pad)) {
176
+ return false;
177
+ }
178
+
179
+ qemu_iovec_init_extended(&pad->local_qiov, pad->buf, pad->head,
180
+ *qiov, 0, *bytes,
181
+ pad->buf + pad->buf_len - pad->tail, pad->tail);
182
+ *bytes += pad->head + pad->tail;
183
+ *offset -= pad->head;
184
+ *qiov = &pad->local_qiov;
185
+
186
+ return true;
187
+}
188
+
189
int coroutine_fn bdrv_co_preadv(BdrvChild *child,
190
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
191
BdrvRequestFlags flags)
192
{
193
BlockDriverState *bs = child->bs;
194
- BlockDriver *drv = bs->drv;
195
BdrvTrackedRequest req;
196
-
197
- uint64_t align = bs->bl.request_alignment;
198
- uint8_t *head_buf = NULL;
199
- uint8_t *tail_buf = NULL;
200
- QEMUIOVector local_qiov;
201
- bool use_local_qiov = false;
202
+ BdrvRequestPadding pad;
203
int ret;
204
205
- trace_bdrv_co_preadv(child->bs, offset, bytes, flags);
206
-
207
- if (!drv) {
208
- return -ENOMEDIUM;
209
- }
210
+ trace_bdrv_co_preadv(bs, offset, bytes, flags);
211
212
ret = bdrv_check_byte_request(bs, offset, bytes);
213
if (ret < 0) {
214
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
215
flags |= BDRV_REQ_COPY_ON_READ;
216
}
217
218
- /* Align read if necessary by padding qiov */
219
- if (offset & (align - 1)) {
220
- head_buf = qemu_blockalign(bs, align);
221
- qemu_iovec_init(&local_qiov, qiov->niov + 2);
222
- qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1));
223
- qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
224
- use_local_qiov = true;
225
-
226
- bytes += offset & (align - 1);
227
- offset = offset & ~(align - 1);
228
- }
229
-
230
- if ((offset + bytes) & (align - 1)) {
231
- if (!use_local_qiov) {
232
- qemu_iovec_init(&local_qiov, qiov->niov + 1);
233
- qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
234
- use_local_qiov = true;
235
- }
236
- tail_buf = qemu_blockalign(bs, align);
237
- qemu_iovec_add(&local_qiov, tail_buf,
238
- align - ((offset + bytes) & (align - 1)));
239
-
240
- bytes = ROUND_UP(bytes, align);
241
- }
242
+ bdrv_pad_request(bs, &qiov, &offset, &bytes, &pad);
243
244
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ);
245
- ret = bdrv_aligned_preadv(child, &req, offset, bytes, align,
246
- use_local_qiov ? &local_qiov : qiov,
247
- flags);
248
+ ret = bdrv_aligned_preadv(child, &req, offset, bytes,
249
+ bs->bl.request_alignment,
250
+ qiov, flags);
251
tracked_request_end(&req);
252
bdrv_dec_in_flight(bs);
253
254
- if (use_local_qiov) {
255
- qemu_iovec_destroy(&local_qiov);
256
- qemu_vfree(head_buf);
257
- qemu_vfree(tail_buf);
258
- }
259
+ bdrv_padding_destroy(&pad);
260
261
return ret;
262
}
263
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
264
BdrvTrackedRequest *req)
265
{
266
BlockDriverState *bs = child->bs;
267
- uint8_t *buf = NULL;
268
QEMUIOVector local_qiov;
269
uint64_t align = bs->bl.request_alignment;
270
- unsigned int head_padding_bytes, tail_padding_bytes;
271
int ret = 0;
272
+ bool padding;
273
+ BdrvRequestPadding pad;
274
275
- head_padding_bytes = offset & (align - 1);
276
- tail_padding_bytes = (align - (offset + bytes)) & (align - 1);
277
-
278
-
279
- assert(flags & BDRV_REQ_ZERO_WRITE);
280
- if (head_padding_bytes || tail_padding_bytes) {
281
- buf = qemu_blockalign(bs, align);
282
- qemu_iovec_init_buf(&local_qiov, buf, align);
283
- }
284
- if (head_padding_bytes) {
285
- uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes);
286
-
287
- /* RMW the unaligned part before head. */
288
+ padding = bdrv_init_padding(bs, offset, bytes, &pad);
289
+ if (padding) {
290
mark_request_serialising(req, align);
291
wait_serialising_requests(req);
292
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
293
- ret = bdrv_aligned_preadv(child, req, offset & ~(align - 1), align,
294
- align, &local_qiov, 0);
295
- if (ret < 0) {
296
- goto fail;
297
- }
298
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
299
300
- memset(buf + head_padding_bytes, 0, zero_bytes);
301
- ret = bdrv_aligned_pwritev(child, req, offset & ~(align - 1), align,
302
- align, &local_qiov,
303
- flags & ~BDRV_REQ_ZERO_WRITE);
304
- if (ret < 0) {
305
- goto fail;
306
+ bdrv_padding_rmw_read(child, req, &pad, true);
307
+
308
+ if (pad.head || pad.merge_reads) {
309
+ int64_t aligned_offset = offset & ~(align - 1);
310
+ int64_t write_bytes = pad.merge_reads ? pad.buf_len : align;
311
+
312
+ qemu_iovec_init_buf(&local_qiov, pad.buf, write_bytes);
313
+ ret = bdrv_aligned_pwritev(child, req, aligned_offset, write_bytes,
314
+ align, &local_qiov,
315
+ flags & ~BDRV_REQ_ZERO_WRITE);
316
+ if (ret < 0 || pad.merge_reads) {
317
+ /* Error or all work is done */
318
+ goto out;
319
+ }
320
+ offset += write_bytes - pad.head;
321
+ bytes -= write_bytes - pad.head;
322
}
323
- offset += zero_bytes;
324
- bytes -= zero_bytes;
325
}
326
327
assert(!bytes || (offset & (align - 1)) == 0);
328
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
329
ret = bdrv_aligned_pwritev(child, req, offset, aligned_bytes, align,
330
NULL, flags);
331
if (ret < 0) {
332
- goto fail;
333
+ goto out;
334
}
335
bytes -= aligned_bytes;
336
offset += aligned_bytes;
337
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
338
339
assert(!bytes || (offset & (align - 1)) == 0);
340
if (bytes) {
341
- assert(align == tail_padding_bytes + bytes);
342
- /* RMW the unaligned part after tail. */
343
- mark_request_serialising(req, align);
344
- wait_serialising_requests(req);
345
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
346
- ret = bdrv_aligned_preadv(child, req, offset, align,
347
- align, &local_qiov, 0);
348
- if (ret < 0) {
349
- goto fail;
350
- }
351
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
352
+ assert(align == pad.tail + bytes);
353
354
- memset(buf, 0, bytes);
355
+ qemu_iovec_init_buf(&local_qiov, pad.tail_buf, align);
356
ret = bdrv_aligned_pwritev(child, req, offset, align, align,
357
&local_qiov, flags & ~BDRV_REQ_ZERO_WRITE);
358
}
359
-fail:
360
- qemu_vfree(buf);
361
+
362
+out:
363
+ bdrv_padding_destroy(&pad);
364
+
365
return ret;
366
-
367
}
368
369
/*
370
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
371
BlockDriverState *bs = child->bs;
372
BdrvTrackedRequest req;
373
uint64_t align = bs->bl.request_alignment;
374
- uint8_t *head_buf = NULL;
375
- uint8_t *tail_buf = NULL;
376
- QEMUIOVector local_qiov;
377
- bool use_local_qiov = false;
378
+ BdrvRequestPadding pad;
379
int ret;
380
381
trace_bdrv_co_pwritev(child->bs, offset, bytes, flags);
382
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
383
goto out;
384
}
385
386
- if (offset & (align - 1)) {
387
- QEMUIOVector head_qiov;
388
-
389
+ if (bdrv_pad_request(bs, &qiov, &offset, &bytes, &pad)) {
390
mark_request_serialising(&req, align);
391
wait_serialising_requests(&req);
392
-
393
- head_buf = qemu_blockalign(bs, align);
394
- qemu_iovec_init_buf(&head_qiov, head_buf, align);
395
-
396
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
397
- ret = bdrv_aligned_preadv(child, &req, offset & ~(align - 1), align,
398
- align, &head_qiov, 0);
399
- if (ret < 0) {
400
- goto fail;
401
- }
402
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
403
-
404
- qemu_iovec_init(&local_qiov, qiov->niov + 2);
405
- qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1));
406
- qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
407
- use_local_qiov = true;
408
-
409
- bytes += offset & (align - 1);
410
- offset = offset & ~(align - 1);
411
-
412
- /* We have read the tail already if the request is smaller
413
- * than one aligned block.
414
- */
415
- if (bytes < align) {
416
- qemu_iovec_add(&local_qiov, head_buf + bytes, align - bytes);
417
- bytes = align;
418
- }
419
- }
420
-
421
- if ((offset + bytes) & (align - 1)) {
422
- QEMUIOVector tail_qiov;
423
- size_t tail_bytes;
424
- bool waited;
425
-
426
- mark_request_serialising(&req, align);
427
- waited = wait_serialising_requests(&req);
428
- assert(!waited || !use_local_qiov);
429
-
430
- tail_buf = qemu_blockalign(bs, align);
431
- qemu_iovec_init_buf(&tail_qiov, tail_buf, align);
432
-
433
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
434
- ret = bdrv_aligned_preadv(child, &req, (offset + bytes) & ~(align - 1),
435
- align, align, &tail_qiov, 0);
436
- if (ret < 0) {
437
- goto fail;
438
- }
439
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
440
-
441
- if (!use_local_qiov) {
442
- qemu_iovec_init(&local_qiov, qiov->niov + 1);
443
- qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
444
- use_local_qiov = true;
445
- }
446
-
447
- tail_bytes = (offset + bytes) & (align - 1);
448
- qemu_iovec_add(&local_qiov, tail_buf + tail_bytes, align - tail_bytes);
449
-
450
- bytes = ROUND_UP(bytes, align);
451
+ bdrv_padding_rmw_read(child, &req, &pad, false);
452
}
453
454
ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align,
455
- use_local_qiov ? &local_qiov : qiov,
456
- flags);
457
+ qiov, flags);
458
459
-fail:
460
+ bdrv_padding_destroy(&pad);
461
462
- if (use_local_qiov) {
463
- qemu_iovec_destroy(&local_qiov);
464
- }
465
- qemu_vfree(head_buf);
466
- qemu_vfree(tail_buf);
467
out:
468
tracked_request_end(&req);
469
bdrv_dec_in_flight(bs);
470
+
471
return ret;
472
}
473
474
--
219
--
475
2.21.0
220
2.35.1
476
477
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Add handlers supporting qiov_offset parameter:
4
bdrv_co_preadv_part
5
bdrv_co_pwritev_part
6
bdrv_co_pwritev_compressed_part
7
This is used to reduce need of defining local_qiovs and hd_qiovs in all
8
corners of block layer code. The following patches will increase usage
9
of this new API part by part.
10
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Message-id: 20190604161514.262241-5-vsementsov@virtuozzo.com
14
Message-Id: <20190604161514.262241-5-vsementsov@virtuozzo.com>
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
16
---
17
include/block/block_int.h | 15 ++++++
18
block/backup.c | 2 +-
19
block/io.c | 96 +++++++++++++++++++++++++++++++--------
20
qemu-img.c | 4 +-
21
4 files changed, 95 insertions(+), 22 deletions(-)
22
23
diff --git a/include/block/block_int.h b/include/block/block_int.h
24
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/block_int.h
26
+++ b/include/block/block_int.h
27
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
28
*/
29
int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs,
30
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
31
+ int coroutine_fn (*bdrv_co_preadv_part)(BlockDriverState *bs,
32
+ uint64_t offset, uint64_t bytes,
33
+ QEMUIOVector *qiov, size_t qiov_offset, int flags);
34
int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs,
35
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags);
36
/**
37
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
38
*/
39
int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs,
40
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
41
+ int coroutine_fn (*bdrv_co_pwritev_part)(BlockDriverState *bs,
42
+ uint64_t offset, uint64_t bytes,
43
+ QEMUIOVector *qiov, size_t qiov_offset, int flags);
44
45
/*
46
* Efficiently zero a region of the disk image. Typically an image format
47
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
48
49
int coroutine_fn (*bdrv_co_pwritev_compressed)(BlockDriverState *bs,
50
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov);
51
+ int coroutine_fn (*bdrv_co_pwritev_compressed_part)(BlockDriverState *bs,
52
+ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov,
53
+ size_t qiov_offset);
54
55
int (*bdrv_snapshot_create)(BlockDriverState *bs,
56
QEMUSnapshotInfo *sn_info);
57
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
58
const char *const *strong_runtime_opts;
59
};
60
61
+static inline bool block_driver_can_compress(BlockDriver *drv)
62
+{
63
+ return drv->bdrv_co_pwritev_compressed ||
64
+ drv->bdrv_co_pwritev_compressed_part;
65
+}
66
+
67
typedef struct BlockLimits {
68
/* Alignment requirement, in bytes, for offset/length of I/O
69
* requests. Must be a power of 2 less than INT_MAX; defaults to
70
diff --git a/block/backup.c b/block/backup.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/block/backup.c
73
+++ b/block/backup.c
74
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
75
return NULL;
76
}
77
78
- if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
79
+ if (compress && !block_driver_can_compress(target->drv)) {
80
error_setg(errp, "Compression is not supported for this drive %s",
81
bdrv_get_device_name(target));
82
return NULL;
83
diff --git a/block/io.c b/block/io.c
84
index XXXXXXX..XXXXXXX 100644
85
--- a/block/io.c
86
+++ b/block/io.c
87
@@ -XXX,XX +XXX,XX @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
88
89
/* Default alignment based on whether driver has byte interface */
90
bs->bl.request_alignment = (drv->bdrv_co_preadv ||
91
- drv->bdrv_aio_preadv) ? 1 : 512;
92
+ drv->bdrv_aio_preadv ||
93
+ drv->bdrv_co_preadv_part) ? 1 : 512;
94
95
/* Take some limits from the children as a default */
96
if (bs->file) {
97
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_io_em_complete(void *opaque, int ret)
98
99
static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
100
uint64_t offset, uint64_t bytes,
101
- QEMUIOVector *qiov, int flags)
102
+ QEMUIOVector *qiov,
103
+ size_t qiov_offset, int flags)
104
{
105
BlockDriver *drv = bs->drv;
106
int64_t sector_num;
107
unsigned int nb_sectors;
108
+ QEMUIOVector local_qiov;
109
+ int ret;
110
111
assert(!(flags & ~BDRV_REQ_MASK));
112
assert(!(flags & BDRV_REQ_NO_FALLBACK));
113
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
114
return -ENOMEDIUM;
115
}
116
117
+ if (drv->bdrv_co_preadv_part) {
118
+ return drv->bdrv_co_preadv_part(bs, offset, bytes, qiov, qiov_offset,
119
+ flags);
120
+ }
121
+
122
+ if (qiov_offset > 0 || bytes != qiov->size) {
123
+ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes);
124
+ qiov = &local_qiov;
125
+ }
126
+
127
if (drv->bdrv_co_preadv) {
128
- return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags);
129
+ ret = drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags);
130
+ goto out;
131
}
132
133
if (drv->bdrv_aio_preadv) {
134
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
135
acb = drv->bdrv_aio_preadv(bs, offset, bytes, qiov, flags,
136
bdrv_co_io_em_complete, &co);
137
if (acb == NULL) {
138
- return -EIO;
139
+ ret = -EIO;
140
+ goto out;
141
} else {
142
qemu_coroutine_yield();
143
- return co.ret;
144
+ ret = co.ret;
145
+ goto out;
146
}
147
}
148
149
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
150
assert(bytes <= BDRV_REQUEST_MAX_BYTES);
151
assert(drv->bdrv_co_readv);
152
153
- return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
154
+ ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
155
+
156
+out:
157
+ if (qiov == &local_qiov) {
158
+ qemu_iovec_destroy(&local_qiov);
159
+ }
160
+
161
+ return ret;
162
}
163
164
static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
165
uint64_t offset, uint64_t bytes,
166
- QEMUIOVector *qiov, int flags)
167
+ QEMUIOVector *qiov,
168
+ size_t qiov_offset, int flags)
169
{
170
BlockDriver *drv = bs->drv;
171
int64_t sector_num;
172
unsigned int nb_sectors;
173
+ QEMUIOVector local_qiov;
174
int ret;
175
176
assert(!(flags & ~BDRV_REQ_MASK));
177
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
178
return -ENOMEDIUM;
179
}
180
181
+ if (drv->bdrv_co_pwritev_part) {
182
+ ret = drv->bdrv_co_pwritev_part(bs, offset, bytes, qiov, qiov_offset,
183
+ flags & bs->supported_write_flags);
184
+ flags &= ~bs->supported_write_flags;
185
+ goto emulate_flags;
186
+ }
187
+
188
+ if (qiov_offset > 0 || bytes != qiov->size) {
189
+ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes);
190
+ qiov = &local_qiov;
191
+ }
192
+
193
if (drv->bdrv_co_pwritev) {
194
ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov,
195
flags & bs->supported_write_flags);
196
@@ -XXX,XX +XXX,XX @@ emulate_flags:
197
ret = bdrv_co_flush(bs);
198
}
199
200
+ if (qiov == &local_qiov) {
201
+ qemu_iovec_destroy(&local_qiov);
202
+ }
203
+
204
return ret;
205
}
206
207
static int coroutine_fn
208
bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
209
- uint64_t bytes, QEMUIOVector *qiov)
210
+ uint64_t bytes, QEMUIOVector *qiov,
211
+ size_t qiov_offset)
212
{
213
BlockDriver *drv = bs->drv;
214
+ QEMUIOVector local_qiov;
215
+ int ret;
216
217
if (!drv) {
218
return -ENOMEDIUM;
219
}
220
221
- if (!drv->bdrv_co_pwritev_compressed) {
222
+ if (!block_driver_can_compress(drv)) {
223
return -ENOTSUP;
224
}
225
226
- return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov);
227
+ if (drv->bdrv_co_pwritev_compressed_part) {
228
+ return drv->bdrv_co_pwritev_compressed_part(bs, offset, bytes,
229
+ qiov, qiov_offset);
230
+ }
231
+
232
+ if (qiov_offset == 0) {
233
+ return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov);
234
+ }
235
+
236
+ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes);
237
+ ret = drv->bdrv_co_pwritev_compressed(bs, offset, bytes, &local_qiov);
238
+ qemu_iovec_destroy(&local_qiov);
239
+
240
+ return ret;
241
}
242
243
static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
244
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
245
qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum);
246
247
ret = bdrv_driver_preadv(bs, cluster_offset, pnum,
248
- &local_qiov, 0);
249
+ &local_qiov, 0, 0);
250
if (ret < 0) {
251
goto err;
252
}
253
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
254
* necessary to flush even in cache=writethrough mode.
255
*/
256
ret = bdrv_driver_pwritev(bs, cluster_offset, pnum,
257
- &local_qiov,
258
+ &local_qiov, 0,
259
BDRV_REQ_WRITE_UNCHANGED);
260
}
261
262
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
263
qemu_iovec_init(&local_qiov, qiov->niov);
264
qemu_iovec_concat(&local_qiov, qiov, progress, pnum - skip_bytes);
265
ret = bdrv_driver_preadv(bs, offset + progress, local_qiov.size,
266
- &local_qiov, 0);
267
+ &local_qiov, 0, 0);
268
qemu_iovec_destroy(&local_qiov);
269
if (ret < 0) {
270
goto err;
271
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
272
273
max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align);
274
if (bytes <= max_bytes && bytes <= max_transfer) {
275
- ret = bdrv_driver_preadv(bs, offset, bytes, qiov, 0);
276
+ ret = bdrv_driver_preadv(bs, offset, bytes, qiov, 0, 0);
277
goto out;
278
}
279
280
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
281
qemu_iovec_concat(&local_qiov, qiov, bytes - bytes_remaining, num);
282
283
ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining,
284
- num, &local_qiov, 0);
285
+ num, &local_qiov, 0, 0);
286
max_bytes -= num;
287
qemu_iovec_destroy(&local_qiov);
288
} else {
289
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
290
}
291
qemu_iovec_init_buf(&qiov, buf, num);
292
293
- ret = bdrv_driver_pwritev(bs, offset, num, &qiov, write_flags);
294
+ ret = bdrv_driver_pwritev(bs, offset, num, &qiov, 0, write_flags);
295
296
/* Keep bounce buffer around if it is big enough for all
297
* all future requests.
298
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
299
bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO);
300
ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags);
301
} else if (flags & BDRV_REQ_WRITE_COMPRESSED) {
302
- ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, qiov);
303
+ ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, qiov, 0);
304
} else if (bytes <= max_transfer) {
305
bdrv_debug_event(bs, BLKDBG_PWRITEV);
306
- ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, flags);
307
+ ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, 0, flags);
308
} else {
309
bdrv_debug_event(bs, BLKDBG_PWRITEV);
310
while (bytes_remaining) {
311
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
312
qemu_iovec_concat(&local_qiov, qiov, bytes - bytes_remaining, num);
313
314
ret = bdrv_driver_pwritev(bs, offset + bytes - bytes_remaining,
315
- num, &local_qiov, local_flags);
316
+ num, &local_qiov, 0, local_flags);
317
qemu_iovec_destroy(&local_qiov);
318
if (ret < 0) {
319
break;
320
diff --git a/qemu-img.c b/qemu-img.c
321
index XXXXXXX..XXXXXXX 100644
322
--- a/qemu-img.c
323
+++ b/qemu-img.c
324
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
325
const char *preallocation =
326
qemu_opt_get(opts, BLOCK_OPT_PREALLOC);
327
328
- if (drv && !drv->bdrv_co_pwritev_compressed) {
329
+ if (drv && !block_driver_can_compress(drv)) {
330
error_report("Compression not supported for this file format");
331
ret = -1;
332
goto out;
333
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
334
}
335
out_bs = blk_bs(s.target);
336
337
- if (s.compressed && !out_bs->drv->bdrv_co_pwritev_compressed) {
338
+ if (s.compressed && !block_driver_can_compress(out_bs->drv)) {
339
error_report("Compression not supported for this file format");
340
ret = -1;
341
goto out;
342
--
343
2.21.0
344
345
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Use and support new API in bdrv_co_do_copy_on_readv. Note that in case
4
of allocated-in-top we need to shrink read size to MIN(..) by hand, as
5
pre-patch this was actually done implicitly by qemu_iovec_concat (and
6
we used local_qiov.size).
7
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Message-id: 20190604161514.262241-6-vsementsov@virtuozzo.com
11
Message-Id: <20190604161514.262241-6-vsementsov@virtuozzo.com>
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
14
block/io.c | 18 +++++++++---------
15
1 file changed, 9 insertions(+), 9 deletions(-)
16
17
diff --git a/block/io.c b/block/io.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block/io.c
20
+++ b/block/io.c
21
@@ -XXX,XX +XXX,XX @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
22
23
static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
24
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
25
- int flags)
26
+ size_t qiov_offset, int flags)
27
{
28
BlockDriverState *bs = child->bs;
29
30
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
31
void *bounce_buffer;
32
33
BlockDriver *drv = bs->drv;
34
- QEMUIOVector local_qiov;
35
int64_t cluster_offset;
36
int64_t cluster_bytes;
37
size_t skip_bytes;
38
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
39
assert(skip_bytes < pnum);
40
41
if (ret <= 0) {
42
+ QEMUIOVector local_qiov;
43
+
44
/* Must copy-on-read; use the bounce buffer */
45
pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
46
qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum);
47
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
48
}
49
50
if (!(flags & BDRV_REQ_PREFETCH)) {
51
- qemu_iovec_from_buf(qiov, progress, bounce_buffer + skip_bytes,
52
+ qemu_iovec_from_buf(qiov, qiov_offset + progress,
53
+ bounce_buffer + skip_bytes,
54
pnum - skip_bytes);
55
}
56
} else if (!(flags & BDRV_REQ_PREFETCH)) {
57
/* Read directly into the destination */
58
- qemu_iovec_init(&local_qiov, qiov->niov);
59
- qemu_iovec_concat(&local_qiov, qiov, progress, pnum - skip_bytes);
60
- ret = bdrv_driver_preadv(bs, offset + progress, local_qiov.size,
61
- &local_qiov, 0, 0);
62
- qemu_iovec_destroy(&local_qiov);
63
+ ret = bdrv_driver_preadv(bs, offset + progress,
64
+ MIN(pnum - skip_bytes, bytes - progress),
65
+ qiov, qiov_offset + progress, 0);
66
if (ret < 0) {
67
goto err;
68
}
69
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
70
}
71
72
if (!ret || pnum != bytes) {
73
- ret = bdrv_co_do_copy_on_readv(child, offset, bytes, qiov, flags);
74
+ ret = bdrv_co_do_copy_on_readv(child, offset, bytes, qiov, 0, flags);
75
goto out;
76
} else if (flags & BDRV_REQ_PREFETCH) {
77
goto out;
78
--
79
2.21.0
80
81
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Allocate bounce_buffer only if it is really needed. Also, sub-optimize
4
allocation size (why not?).
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-id: 20190604161514.262241-7-vsementsov@virtuozzo.com
9
Message-Id: <20190604161514.262241-7-vsementsov@virtuozzo.com>
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
block/io.c | 21 ++++++++++++---------
13
1 file changed, 12 insertions(+), 9 deletions(-)
14
15
diff --git a/block/io.c b/block/io.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/io.c
18
+++ b/block/io.c
19
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
20
* modifying the image file. This is critical for zero-copy guest I/O
21
* where anything might happen inside guest memory.
22
*/
23
- void *bounce_buffer;
24
+ void *bounce_buffer = NULL;
25
26
BlockDriver *drv = bs->drv;
27
int64_t cluster_offset;
28
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
29
trace_bdrv_co_do_copy_on_readv(bs, offset, bytes,
30
cluster_offset, cluster_bytes);
31
32
- bounce_buffer = qemu_try_blockalign(bs,
33
- MIN(MIN(max_transfer, cluster_bytes),
34
- MAX_BOUNCE_BUFFER));
35
- if (bounce_buffer == NULL) {
36
- ret = -ENOMEM;
37
- goto err;
38
- }
39
-
40
while (cluster_bytes) {
41
int64_t pnum;
42
43
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
44
45
/* Must copy-on-read; use the bounce buffer */
46
pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
47
+ if (!bounce_buffer) {
48
+ int64_t max_we_need = MAX(pnum, cluster_bytes - pnum);
49
+ int64_t max_allowed = MIN(max_transfer, MAX_BOUNCE_BUFFER);
50
+ int64_t bounce_buffer_len = MIN(max_we_need, max_allowed);
51
+
52
+ bounce_buffer = qemu_try_blockalign(bs, bounce_buffer_len);
53
+ if (!bounce_buffer) {
54
+ ret = -ENOMEM;
55
+ goto err;
56
+ }
57
+ }
58
qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum);
59
60
ret = bdrv_driver_preadv(bs, cluster_offset, pnum,
61
--
62
2.21.0
63
64
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Use and support new API in bdrv_co_do_copy_on_readv.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Message-id: 20190604161514.262241-8-vsementsov@virtuozzo.com
8
Message-Id: <20190604161514.262241-8-vsementsov@virtuozzo.com>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
block/io.c | 21 ++++++++-------------
12
1 file changed, 8 insertions(+), 13 deletions(-)
13
14
diff --git a/block/io.c b/block/io.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/io.c
17
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@ err:
19
*/
20
static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
21
BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
22
- int64_t align, QEMUIOVector *qiov, int flags)
23
+ int64_t align, QEMUIOVector *qiov, size_t qiov_offset, int flags)
24
{
25
BlockDriverState *bs = child->bs;
26
int64_t total_bytes, max_bytes;
27
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
28
assert(is_power_of_2(align));
29
assert((offset & (align - 1)) == 0);
30
assert((bytes & (align - 1)) == 0);
31
- assert(!qiov || bytes == qiov->size);
32
assert((bs->open_flags & BDRV_O_NO_IO) == 0);
33
max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX),
34
align);
35
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
36
}
37
38
if (!ret || pnum != bytes) {
39
- ret = bdrv_co_do_copy_on_readv(child, offset, bytes, qiov, 0, flags);
40
+ ret = bdrv_co_do_copy_on_readv(child, offset, bytes,
41
+ qiov, qiov_offset, flags);
42
goto out;
43
} else if (flags & BDRV_REQ_PREFETCH) {
44
goto out;
45
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
46
47
max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align);
48
if (bytes <= max_bytes && bytes <= max_transfer) {
49
- ret = bdrv_driver_preadv(bs, offset, bytes, qiov, 0, 0);
50
+ ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, 0);
51
goto out;
52
}
53
54
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
55
int num;
56
57
if (max_bytes) {
58
- QEMUIOVector local_qiov;
59
-
60
num = MIN(bytes_remaining, MIN(max_bytes, max_transfer));
61
assert(num);
62
- qemu_iovec_init(&local_qiov, qiov->niov);
63
- qemu_iovec_concat(&local_qiov, qiov, bytes - bytes_remaining, num);
64
65
ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining,
66
- num, &local_qiov, 0, 0);
67
+ num, qiov, bytes - bytes_remaining, 0);
68
max_bytes -= num;
69
- qemu_iovec_destroy(&local_qiov);
70
} else {
71
num = bytes_remaining;
72
ret = qemu_iovec_memset(qiov, bytes - bytes_remaining, 0,
73
@@ -XXX,XX +XXX,XX @@ static int bdrv_padding_rmw_read(BdrvChild *child,
74
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
75
}
76
ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes,
77
- align, &local_qiov, 0);
78
+ align, &local_qiov, 0, 0);
79
if (ret < 0) {
80
return ret;
81
}
82
@@ -XXX,XX +XXX,XX @@ static int bdrv_padding_rmw_read(BdrvChild *child,
83
ret = bdrv_aligned_preadv(
84
child, req,
85
req->overlap_offset + req->overlap_bytes - align,
86
- align, align, &local_qiov, 0);
87
+ align, align, &local_qiov, 0, 0);
88
if (ret < 0) {
89
return ret;
90
}
91
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
92
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ);
93
ret = bdrv_aligned_preadv(child, &req, offset, bytes,
94
bs->bl.request_alignment,
95
- qiov, flags);
96
+ qiov, 0, flags);
97
tracked_request_end(&req);
98
bdrv_dec_in_flight(bs);
99
100
--
101
2.21.0
102
103
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Use and support new API in bdrv_aligned_pwritev.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Message-id: 20190604161514.262241-9-vsementsov@virtuozzo.com
8
Message-Id: <20190604161514.262241-9-vsementsov@virtuozzo.com>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
block/io.c | 27 +++++++++++++--------------
12
1 file changed, 13 insertions(+), 14 deletions(-)
13
14
diff --git a/block/io.c b/block/io.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/io.c
17
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes,
19
*/
20
static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
21
BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
22
- int64_t align, QEMUIOVector *qiov, int flags)
23
+ int64_t align, QEMUIOVector *qiov, size_t qiov_offset, int flags)
24
{
25
BlockDriverState *bs = child->bs;
26
BlockDriver *drv = bs->drv;
27
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
28
assert(is_power_of_2(align));
29
assert((offset & (align - 1)) == 0);
30
assert((bytes & (align - 1)) == 0);
31
- assert(!qiov || bytes == qiov->size);
32
+ assert(!qiov || qiov_offset + bytes <= qiov->size);
33
max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX),
34
align);
35
36
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
37
38
if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF &&
39
!(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_pwrite_zeroes &&
40
- qemu_iovec_is_zero(qiov, 0, qiov->size)) {
41
+ qemu_iovec_is_zero(qiov, qiov_offset, bytes)) {
42
flags |= BDRV_REQ_ZERO_WRITE;
43
if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) {
44
flags |= BDRV_REQ_MAY_UNMAP;
45
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
46
bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO);
47
ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags);
48
} else if (flags & BDRV_REQ_WRITE_COMPRESSED) {
49
- ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, qiov, 0);
50
+ ret = bdrv_driver_pwritev_compressed(bs, offset, bytes,
51
+ qiov, qiov_offset);
52
} else if (bytes <= max_transfer) {
53
bdrv_debug_event(bs, BLKDBG_PWRITEV);
54
- ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, 0, flags);
55
+ ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags);
56
} else {
57
bdrv_debug_event(bs, BLKDBG_PWRITEV);
58
while (bytes_remaining) {
59
int num = MIN(bytes_remaining, max_transfer);
60
- QEMUIOVector local_qiov;
61
int local_flags = flags;
62
63
assert(num);
64
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
65
* need to flush on the last iteration */
66
local_flags &= ~BDRV_REQ_FUA;
67
}
68
- qemu_iovec_init(&local_qiov, qiov->niov);
69
- qemu_iovec_concat(&local_qiov, qiov, bytes - bytes_remaining, num);
70
71
ret = bdrv_driver_pwritev(bs, offset + bytes - bytes_remaining,
72
- num, &local_qiov, 0, local_flags);
73
- qemu_iovec_destroy(&local_qiov);
74
+ num, qiov, bytes - bytes_remaining,
75
+ local_flags);
76
if (ret < 0) {
77
break;
78
}
79
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
80
81
qemu_iovec_init_buf(&local_qiov, pad.buf, write_bytes);
82
ret = bdrv_aligned_pwritev(child, req, aligned_offset, write_bytes,
83
- align, &local_qiov,
84
+ align, &local_qiov, 0,
85
flags & ~BDRV_REQ_ZERO_WRITE);
86
if (ret < 0 || pad.merge_reads) {
87
/* Error or all work is done */
88
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
89
/* Write the aligned part in the middle. */
90
uint64_t aligned_bytes = bytes & ~(align - 1);
91
ret = bdrv_aligned_pwritev(child, req, offset, aligned_bytes, align,
92
- NULL, flags);
93
+ NULL, 0, flags);
94
if (ret < 0) {
95
goto out;
96
}
97
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
98
99
qemu_iovec_init_buf(&local_qiov, pad.tail_buf, align);
100
ret = bdrv_aligned_pwritev(child, req, offset, align, align,
101
- &local_qiov, flags & ~BDRV_REQ_ZERO_WRITE);
102
+ &local_qiov, 0,
103
+ flags & ~BDRV_REQ_ZERO_WRITE);
104
}
105
106
out:
107
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
108
}
109
110
ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align,
111
- qiov, flags);
112
+ qiov, 0, flags);
113
114
bdrv_padding_destroy(&pad);
115
116
--
117
2.21.0
118
119
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
Introduce extended variants of bdrv_co_preadv and bdrv_co_pwritev
3
The thread pool regulates itself: when idle, it kills threads until
4
with qiov_offset parameter.
4
empty, when in demand, it creates new threads until full. This behaviour
5
doesn't play well with latency sensitive workloads where the price of
6
creating a new thread is too high. For example, when paired with qemu's
7
'-mlock', or using safety features like SafeStack, creating a new thread
8
has been measured take multiple milliseconds.
5
9
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
In order to mitigate this let's introduce a new 'EventLoopBase'
7
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
11
property to set the thread pool size. The threads will be created during
8
Message-id: 20190604161514.262241-10-vsementsov@virtuozzo.com
12
the pool's initialization or upon updating the property's value, remain
9
Message-Id: <20190604161514.262241-10-vsementsov@virtuozzo.com>
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
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
22
---
12
include/block/block_int.h | 6 ++++++
23
qapi/qom.json | 10 +++++-
13
block/io.c | 29 +++++++++++++++++++++++------
24
include/block/aio.h | 10 ++++++
14
2 files changed, 29 insertions(+), 6 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(-)
15
34
16
diff --git a/include/block/block_int.h b/include/block/block_int.h
35
diff --git a/qapi/qom.json b/qapi/qom.json
17
index XXXXXXX..XXXXXXX 100644
36
index XXXXXXX..XXXXXXX 100644
18
--- a/include/block/block_int.h
37
--- a/qapi/qom.json
19
+++ b/include/block/block_int.h
38
+++ b/qapi/qom.json
20
@@ -XXX,XX +XXX,XX @@ extern BlockDriver bdrv_qcow2;
39
@@ -XXX,XX +XXX,XX @@
21
int coroutine_fn bdrv_co_preadv(BdrvChild *child,
40
# 0 means that the engine will use its default.
22
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
41
# (default: 0)
23
BdrvRequestFlags flags);
42
#
24
+int coroutine_fn bdrv_co_preadv_part(BdrvChild *child,
43
+# @thread-pool-min: minimum number of threads reserved in the thread pool
25
+ int64_t offset, unsigned int bytes,
44
+# (default:0)
26
+ QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags);
45
+#
27
int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
46
+# @thread-pool-max: maximum number of threads the thread pool can contain
28
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
47
+# (default:64)
29
BdrvRequestFlags flags);
48
+#
30
+int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
49
# Since: 7.1
31
+ int64_t offset, unsigned int bytes,
50
##
32
+ QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags);
51
{ 'struct': 'EventLoopBaseProperties',
33
52
- 'data': { '*aio-max-batch': 'int' } }
34
static inline int coroutine_fn bdrv_co_pread(BdrvChild *child,
53
+ 'data': { '*aio-max-batch': 'int',
35
int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags)
54
+ '*thread-pool-min': 'int',
36
diff --git a/block/io.c b/block/io.c
55
+ '*thread-pool-max': 'int' } }
37
index XXXXXXX..XXXXXXX 100644
56
38
--- a/block/io.c
57
##
39
+++ b/block/io.c
58
# @IothreadProperties:
40
@@ -XXX,XX +XXX,XX @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad)
59
diff --git a/include/block/aio.h b/include/block/aio.h
41
*
60
index XXXXXXX..XXXXXXX 100644
42
* Function always succeeds.
61
--- a/include/block/aio.h
43
*/
62
+++ b/include/block/aio.h
44
-static bool bdrv_pad_request(BlockDriverState *bs, QEMUIOVector **qiov,
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
45
+static bool bdrv_pad_request(BlockDriverState *bs,
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
46
+ QEMUIOVector **qiov, size_t *qiov_offset,
65
QEMUBH *co_schedule_bh;
47
int64_t *offset, unsigned int *bytes,
66
48
BdrvRequestPadding *pad)
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)
49
{
252
{
50
@@ -XXX,XX +XXX,XX @@ static bool bdrv_pad_request(BlockDriverState *bs, QEMUIOVector **qiov,
253
+ ERRP_GUARD();
254
+
255
if (!qemu_aio_context) {
256
error_setg(errp, "qemu aio context not ready");
257
return;
51
}
258
}
52
259
53
qemu_iovec_init_extended(&pad->local_qiov, pad->buf, pad->head,
260
aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
54
- *qiov, 0, *bytes,
261
+ if (*errp) {
55
+ *qiov, *qiov_offset, *bytes,
262
+ return;
56
pad->buf + pad->buf_len - pad->tail, pad->tail);
263
+ }
57
*bytes += pad->head + pad->tail;
264
+
58
*offset -= pad->head;
265
+ aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min,
59
*qiov = &pad->local_qiov;
266
+ base->thread_pool_max, errp);
60
+ *qiov_offset = 0;
267
}
61
268
62
return true;
269
MainLoop *mloop;
63
}
270
diff --git a/util/thread-pool.c b/util/thread-pool.c
64
@@ -XXX,XX +XXX,XX @@ static bool bdrv_pad_request(BlockDriverState *bs, QEMUIOVector **qiov,
271
index XXXXXXX..XXXXXXX 100644
65
int coroutine_fn bdrv_co_preadv(BdrvChild *child,
272
--- a/util/thread-pool.c
66
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
273
+++ b/util/thread-pool.c
67
BdrvRequestFlags flags)
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)
68
+{
291
+{
69
+ return bdrv_co_preadv_part(child, offset, bytes, qiov, 0, flags);
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;
70
+}
305
+}
71
+
306
+
72
+int coroutine_fn bdrv_co_preadv_part(BdrvChild *child,
307
static void *worker_thread(void *opaque)
73
+ int64_t offset, unsigned int bytes,
74
+ QEMUIOVector *qiov, size_t qiov_offset,
75
+ BdrvRequestFlags flags)
76
{
308
{
77
BlockDriverState *bs = child->bs;
309
ThreadPool *pool = opaque;
78
BdrvTrackedRequest req;
310
@@ -XXX,XX +XXX,XX @@ static void *worker_thread(void *opaque)
79
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
311
ret = qemu_sem_timedwait(&pool->sem, 10000);
80
flags |= BDRV_REQ_COPY_ON_READ;
312
qemu_mutex_lock(&pool->lock);
81
}
313
pool->idle_threads--;
82
314
- } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list));
83
- bdrv_pad_request(bs, &qiov, &offset, &bytes, &pad);
315
- if (ret == -1 || pool->stopping) {
84
+ bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad);
316
+ } while (back_to_sleep(pool, ret));
85
317
+ if (ret == -1 || pool->stopping ||
86
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ);
318
+ pool->cur_threads > pool->max_threads) {
87
ret = bdrv_aligned_preadv(child, &req, offset, bytes,
319
break;
88
bs->bl.request_alignment,
320
}
89
- qiov, 0, flags);
321
90
+ qiov, qiov_offset, flags);
322
@@ -XXX,XX +XXX,XX @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg)
91
tracked_request_end(&req);
323
thread_pool_submit_aio(pool, func, arg, NULL, NULL);
92
bdrv_dec_in_flight(bs);
324
}
93
325
94
@@ -XXX,XX +XXX,XX @@ out:
326
+void thread_pool_update_params(ThreadPool *pool, AioContext *ctx)
95
int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
96
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
97
BdrvRequestFlags flags)
98
+{
327
+{
99
+ return bdrv_co_pwritev_part(child, offset, bytes, qiov, 0, flags);
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);
100
+}
351
+}
101
+
352
+
102
+int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
353
static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
103
+ int64_t offset, unsigned int bytes, QEMUIOVector *qiov, size_t qiov_offset,
104
+ BdrvRequestFlags flags)
105
{
354
{
106
BlockDriverState *bs = child->bs;
355
if (!ctx) {
107
BdrvTrackedRequest req;
356
@@ -XXX,XX +XXX,XX @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
108
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
357
qemu_mutex_init(&pool->lock);
109
goto out;
358
qemu_cond_init(&pool->worker_stopped);
110
}
359
qemu_sem_init(&pool->sem, 0);
111
360
- pool->max_threads = 64;
112
- if (bdrv_pad_request(bs, &qiov, &offset, &bytes, &pad)) {
361
pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool);
113
+ if (bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad)) {
362
114
mark_request_serialising(&req, align);
363
QLIST_INIT(&pool->head);
115
wait_serialising_requests(&req);
364
QTAILQ_INIT(&pool->request_list);
116
bdrv_padding_rmw_read(child, &req, &pad, false);
365
+
117
}
366
+ thread_pool_update_params(pool, ctx);
118
367
}
119
ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align,
368
120
- qiov, 0, flags);
369
ThreadPool *thread_pool_new(AioContext *ctx)
121
+ qiov, qiov_offset, flags);
122
123
bdrv_padding_destroy(&pad);
124
125
--
370
--
126
2.21.0
371
2.35.1
127
128
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Use buffer based io in encrypted case.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Message-id: 20190604161514.262241-11-vsementsov@virtuozzo.com
8
Message-Id: <20190604161514.262241-11-vsementsov@virtuozzo.com>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
block/qcow2.c | 28 ++++++++++++++++------------
12
1 file changed, 16 insertions(+), 12 deletions(-)
13
14
diff --git a/block/qcow2.c b/block/qcow2.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/qcow2.c
17
+++ b/block/qcow2.c
18
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
19
}
20
21
assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
22
- qemu_iovec_reset(&hd_qiov);
23
- qemu_iovec_add(&hd_qiov, cluster_data, cur_bytes);
24
- }
25
26
- BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
27
- ret = bdrv_co_preadv(s->data_file,
28
- cluster_offset + offset_in_cluster,
29
- cur_bytes, &hd_qiov, 0);
30
- if (ret < 0) {
31
- goto fail;
32
- }
33
- if (bs->encrypted) {
34
- assert(s->crypto);
35
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
36
+ ret = bdrv_co_pread(s->data_file,
37
+ cluster_offset + offset_in_cluster,
38
+ cur_bytes, cluster_data, 0);
39
+ if (ret < 0) {
40
+ goto fail;
41
+ }
42
+
43
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
44
assert((cur_bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
45
if (qcow2_co_decrypt(bs, cluster_offset, offset,
46
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
47
goto fail;
48
}
49
qemu_iovec_from_buf(qiov, bytes_done, cluster_data, cur_bytes);
50
+ } else {
51
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
52
+ ret = bdrv_co_preadv(s->data_file,
53
+ cluster_offset + offset_in_cluster,
54
+ cur_bytes, &hd_qiov, 0);
55
+ if (ret < 0) {
56
+ goto fail;
57
+ }
58
}
59
break;
60
61
--
62
2.21.0
63
64
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Implement and use new interface to get rid of hd_qiov.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Message-id: 20190604161514.262241-12-vsementsov@virtuozzo.com
8
Message-Id: <20190604161514.262241-12-vsementsov@virtuozzo.com>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
block/qcow2-cluster.c | 5 +++--
12
block/qcow2.c | 49 +++++++++++++++++++------------------------
13
2 files changed, 25 insertions(+), 29 deletions(-)
14
15
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/qcow2-cluster.c
18
+++ b/block/qcow2-cluster.c
19
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn do_perform_cow_read(BlockDriverState *bs,
20
* interface. This avoids double I/O throttling and request tracking,
21
* which can lead to deadlock when block layer copy-on-read is enabled.
22
*/
23
- ret = bs->drv->bdrv_co_preadv(bs, src_cluster_offset + offset_in_cluster,
24
- qiov->size, qiov, 0);
25
+ ret = bs->drv->bdrv_co_preadv_part(bs,
26
+ src_cluster_offset + offset_in_cluster,
27
+ qiov->size, qiov, 0, 0);
28
if (ret < 0) {
29
return ret;
30
}
31
diff --git a/block/qcow2.c b/block/qcow2.c
32
index XXXXXXX..XXXXXXX 100644
33
--- a/block/qcow2.c
34
+++ b/block/qcow2.c
35
@@ -XXX,XX +XXX,XX @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
36
uint64_t file_cluster_offset,
37
uint64_t offset,
38
uint64_t bytes,
39
- QEMUIOVector *qiov);
40
+ QEMUIOVector *qiov,
41
+ size_t qiov_offset);
42
43
static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
44
{
45
@@ -XXX,XX +XXX,XX @@ out:
46
return ret;
47
}
48
49
-static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
50
- uint64_t bytes, QEMUIOVector *qiov,
51
- int flags)
52
+static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs,
53
+ uint64_t offset, uint64_t bytes,
54
+ QEMUIOVector *qiov,
55
+ size_t qiov_offset, int flags)
56
{
57
BDRVQcow2State *s = bs->opaque;
58
int offset_in_cluster;
59
int ret;
60
unsigned int cur_bytes; /* number of bytes in current iteration */
61
uint64_t cluster_offset = 0;
62
- uint64_t bytes_done = 0;
63
- QEMUIOVector hd_qiov;
64
uint8_t *cluster_data = NULL;
65
66
- qemu_iovec_init(&hd_qiov, qiov->niov);
67
-
68
while (bytes != 0) {
69
70
/* prepare next request */
71
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
72
73
offset_in_cluster = offset_into_cluster(s, offset);
74
75
- qemu_iovec_reset(&hd_qiov);
76
- qemu_iovec_concat(&hd_qiov, qiov, bytes_done, cur_bytes);
77
-
78
switch (ret) {
79
case QCOW2_CLUSTER_UNALLOCATED:
80
81
if (bs->backing) {
82
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
83
- ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
84
- &hd_qiov, 0);
85
+ ret = bdrv_co_preadv_part(bs->backing, offset, cur_bytes,
86
+ qiov, qiov_offset, 0);
87
if (ret < 0) {
88
goto fail;
89
}
90
} else {
91
/* Note: in this case, no need to wait */
92
- qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
93
+ qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes);
94
}
95
break;
96
97
case QCOW2_CLUSTER_ZERO_PLAIN:
98
case QCOW2_CLUSTER_ZERO_ALLOC:
99
- qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
100
+ qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes);
101
break;
102
103
case QCOW2_CLUSTER_COMPRESSED:
104
ret = qcow2_co_preadv_compressed(bs, cluster_offset,
105
offset, cur_bytes,
106
- &hd_qiov);
107
+ qiov, qiov_offset);
108
if (ret < 0) {
109
goto fail;
110
}
111
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
112
ret = -EIO;
113
goto fail;
114
}
115
- qemu_iovec_from_buf(qiov, bytes_done, cluster_data, cur_bytes);
116
+ qemu_iovec_from_buf(qiov, qiov_offset, cluster_data, cur_bytes);
117
} else {
118
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
119
- ret = bdrv_co_preadv(s->data_file,
120
- cluster_offset + offset_in_cluster,
121
- cur_bytes, &hd_qiov, 0);
122
+ ret = bdrv_co_preadv_part(s->data_file,
123
+ cluster_offset + offset_in_cluster,
124
+ cur_bytes, qiov, qiov_offset, 0);
125
if (ret < 0) {
126
goto fail;
127
}
128
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
129
130
bytes -= cur_bytes;
131
offset += cur_bytes;
132
- bytes_done += cur_bytes;
133
+ qiov_offset += cur_bytes;
134
}
135
ret = 0;
136
137
fail:
138
- qemu_iovec_destroy(&hd_qiov);
139
qemu_vfree(cluster_data);
140
141
return ret;
142
@@ -XXX,XX +XXX,XX @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
143
uint64_t file_cluster_offset,
144
uint64_t offset,
145
uint64_t bytes,
146
- QEMUIOVector *qiov)
147
+ QEMUIOVector *qiov,
148
+ size_t qiov_offset)
149
{
150
BDRVQcow2State *s = bs->opaque;
151
int ret = 0, csize, nb_csectors;
152
@@ -XXX,XX +XXX,XX @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
153
goto fail;
154
}
155
156
- qemu_iovec_from_buf(qiov, 0, out_buf + offset_in_cluster, bytes);
157
+ qemu_iovec_from_buf(qiov, qiov_offset, out_buf + offset_in_cluster, bytes);
158
159
fail:
160
qemu_vfree(out_buf);
161
@@ -XXX,XX +XXX,XX @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
162
BDRVQcow2State *s = bs->opaque;
163
164
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD);
165
- return bs->drv->bdrv_co_preadv(bs, qcow2_vm_state_offset(s) + pos,
166
- qiov->size, qiov, 0);
167
+ return bs->drv->bdrv_co_preadv_part(bs, qcow2_vm_state_offset(s) + pos,
168
+ qiov->size, qiov, 0, 0);
169
}
170
171
/*
172
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
173
.bdrv_has_zero_init_truncate = bdrv_has_zero_init_1,
174
.bdrv_co_block_status = qcow2_co_block_status,
175
176
- .bdrv_co_preadv = qcow2_co_preadv,
177
+ .bdrv_co_preadv_part = qcow2_co_preadv_part,
178
.bdrv_co_pwritev = qcow2_co_pwritev,
179
.bdrv_co_flush_to_os = qcow2_co_flush_to_os,
180
181
--
182
2.21.0
183
184
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Implement and use new interface to get rid of hd_qiov.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Message-id: 20190604161514.262241-13-vsementsov@virtuozzo.com
8
Message-Id: <20190604161514.262241-13-vsementsov@virtuozzo.com>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
block/qcow2.h | 1 +
12
include/qemu/iov.h | 1 +
13
block/qcow2-cluster.c | 9 ++++---
14
block/qcow2.c | 60 +++++++++++++++++++++----------------------
15
util/iov.c | 10 ++++++++
16
5 files changed, 48 insertions(+), 33 deletions(-)
17
18
diff --git a/block/qcow2.h b/block/qcow2.h
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/qcow2.h
21
+++ b/block/qcow2.h
22
@@ -XXX,XX +XXX,XX @@ typedef struct QCowL2Meta
23
* from @cow_start and @cow_end into one single write operation.
24
*/
25
QEMUIOVector *data_qiov;
26
+ size_t data_qiov_offset;
27
28
/** Pointer to next L2Meta of the same write request */
29
struct QCowL2Meta *next;
30
diff --git a/include/qemu/iov.h b/include/qemu/iov.h
31
index XXXXXXX..XXXXXXX 100644
32
--- a/include/qemu/iov.h
33
+++ b/include/qemu/iov.h
34
@@ -XXX,XX +XXX,XX @@ void qemu_iovec_init_extended(
35
void *tail_buf, size_t tail_len);
36
void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source,
37
size_t offset, size_t len);
38
+int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len);
39
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
40
void qemu_iovec_concat(QEMUIOVector *dst,
41
QEMUIOVector *src, size_t soffset, size_t sbytes);
42
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
43
index XXXXXXX..XXXXXXX 100644
44
--- a/block/qcow2-cluster.c
45
+++ b/block/qcow2-cluster.c
46
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
47
assert(start->nb_bytes <= UINT_MAX - end->nb_bytes);
48
assert(start->nb_bytes + end->nb_bytes <= UINT_MAX - data_bytes);
49
assert(start->offset + start->nb_bytes <= end->offset);
50
- assert(!m->data_qiov || m->data_qiov->size == data_bytes);
51
52
if ((start->nb_bytes == 0 && end->nb_bytes == 0) || m->skip_cow) {
53
return 0;
54
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
55
/* The part of the buffer where the end region is located */
56
end_buffer = start_buffer + buffer_size - end->nb_bytes;
57
58
- qemu_iovec_init(&qiov, 2 + (m->data_qiov ? m->data_qiov->niov : 0));
59
+ qemu_iovec_init(&qiov, 2 + (m->data_qiov ?
60
+ qemu_iovec_subvec_niov(m->data_qiov,
61
+ m->data_qiov_offset,
62
+ data_bytes)
63
+ : 0));
64
65
qemu_co_mutex_unlock(&s->lock);
66
/* First we read the existing data from both COW regions. We
67
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
68
if (start->nb_bytes) {
69
qemu_iovec_add(&qiov, start_buffer, start->nb_bytes);
70
}
71
- qemu_iovec_concat(&qiov, m->data_qiov, 0, data_bytes);
72
+ qemu_iovec_concat(&qiov, m->data_qiov, m->data_qiov_offset, data_bytes);
73
if (end->nb_bytes) {
74
qemu_iovec_add(&qiov, end_buffer, end->nb_bytes);
75
}
76
diff --git a/block/qcow2.c b/block/qcow2.c
77
index XXXXXXX..XXXXXXX 100644
78
--- a/block/qcow2.c
79
+++ b/block/qcow2.c
80
@@ -XXX,XX +XXX,XX @@ fail:
81
/* Check if it's possible to merge a write request with the writing of
82
* the data from the COW regions */
83
static bool merge_cow(uint64_t offset, unsigned bytes,
84
- QEMUIOVector *hd_qiov, QCowL2Meta *l2meta)
85
+ QEMUIOVector *qiov, size_t qiov_offset,
86
+ QCowL2Meta *l2meta)
87
{
88
QCowL2Meta *m;
89
90
@@ -XXX,XX +XXX,XX @@ static bool merge_cow(uint64_t offset, unsigned bytes,
91
92
/* Make sure that adding both COW regions to the QEMUIOVector
93
* does not exceed IOV_MAX */
94
- if (hd_qiov->niov > IOV_MAX - 2) {
95
+ if (qemu_iovec_subvec_niov(qiov, qiov_offset, bytes) > IOV_MAX - 2) {
96
continue;
97
}
98
99
- m->data_qiov = hd_qiov;
100
+ m->data_qiov = qiov;
101
+ m->data_qiov_offset = qiov_offset;
102
return true;
103
}
104
105
@@ -XXX,XX +XXX,XX @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta)
106
return 0;
107
}
108
109
-static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
110
- uint64_t bytes, QEMUIOVector *qiov,
111
- int flags)
112
+static coroutine_fn int qcow2_co_pwritev_part(
113
+ BlockDriverState *bs, uint64_t offset, uint64_t bytes,
114
+ QEMUIOVector *qiov, size_t qiov_offset, int flags)
115
{
116
BDRVQcow2State *s = bs->opaque;
117
int offset_in_cluster;
118
int ret;
119
unsigned int cur_bytes; /* number of sectors in current iteration */
120
uint64_t cluster_offset;
121
- QEMUIOVector hd_qiov;
122
+ QEMUIOVector encrypted_qiov;
123
uint64_t bytes_done = 0;
124
uint8_t *cluster_data = NULL;
125
QCowL2Meta *l2meta = NULL;
126
127
trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes);
128
129
- qemu_iovec_init(&hd_qiov, qiov->niov);
130
-
131
qemu_co_mutex_lock(&s->lock);
132
133
while (bytes != 0) {
134
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
135
136
qemu_co_mutex_unlock(&s->lock);
137
138
- qemu_iovec_reset(&hd_qiov);
139
- qemu_iovec_concat(&hd_qiov, qiov, bytes_done, cur_bytes);
140
-
141
if (bs->encrypted) {
142
assert(s->crypto);
143
if (!cluster_data) {
144
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
145
}
146
}
147
148
- assert(hd_qiov.size <=
149
- QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
150
- qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
151
+ assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
152
+ qemu_iovec_to_buf(qiov, qiov_offset + bytes_done,
153
+ cluster_data, cur_bytes);
154
155
if (qcow2_co_encrypt(bs, cluster_offset, offset,
156
cluster_data, cur_bytes) < 0) {
157
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
158
goto out_unlocked;
159
}
160
161
- qemu_iovec_reset(&hd_qiov);
162
- qemu_iovec_add(&hd_qiov, cluster_data, cur_bytes);
163
+ qemu_iovec_init_buf(&encrypted_qiov, cluster_data, cur_bytes);
164
}
165
166
/* Try to efficiently initialize the physical space with zeroes */
167
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
168
* writing of the guest data together with that of the COW regions.
169
* If it's not possible (or not necessary) then write the
170
* guest data now. */
171
- if (!merge_cow(offset, cur_bytes, &hd_qiov, l2meta)) {
172
+ if (!merge_cow(offset, cur_bytes,
173
+ bs->encrypted ? &encrypted_qiov : qiov,
174
+ bs->encrypted ? 0 : qiov_offset + bytes_done, l2meta))
175
+ {
176
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
177
trace_qcow2_writev_data(qemu_coroutine_self(),
178
cluster_offset + offset_in_cluster);
179
- ret = bdrv_co_pwritev(s->data_file,
180
- cluster_offset + offset_in_cluster,
181
- cur_bytes, &hd_qiov, 0);
182
+ ret = bdrv_co_pwritev_part(
183
+ s->data_file, cluster_offset + offset_in_cluster, cur_bytes,
184
+ bs->encrypted ? &encrypted_qiov : qiov,
185
+ bs->encrypted ? 0 : qiov_offset + bytes_done, 0);
186
if (ret < 0) {
187
goto out_unlocked;
188
}
189
@@ -XXX,XX +XXX,XX @@ out_locked:
190
191
qemu_co_mutex_unlock(&s->lock);
192
193
- qemu_iovec_destroy(&hd_qiov);
194
qemu_vfree(cluster_data);
195
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
196
197
@@ -XXX,XX +XXX,XX @@ fail:
198
/* XXX: put compressed sectors first, then all the cluster aligned
199
tables to avoid losing bytes in alignment */
200
static coroutine_fn int
201
-qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
202
- uint64_t bytes, QEMUIOVector *qiov)
203
+qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
204
+ uint64_t offset, uint64_t bytes,
205
+ QEMUIOVector *qiov, size_t qiov_offset)
206
{
207
BDRVQcow2State *s = bs->opaque;
208
int ret;
209
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
210
/* Zero-pad last write if image size is not cluster aligned */
211
memset(buf + bytes, 0, s->cluster_size - bytes);
212
}
213
- qemu_iovec_to_buf(qiov, 0, buf, bytes);
214
+ qemu_iovec_to_buf(qiov, qiov_offset, buf, bytes);
215
216
out_buf = g_malloc(s->cluster_size);
217
218
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
219
buf, s->cluster_size);
220
if (out_len == -ENOMEM) {
221
/* could not compress: write normal cluster */
222
- ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
223
+ ret = qcow2_co_pwritev_part(bs, offset, bytes, qiov, qiov_offset, 0);
224
if (ret < 0) {
225
goto fail;
226
}
227
@@ -XXX,XX +XXX,XX @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
228
BDRVQcow2State *s = bs->opaque;
229
230
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
231
- return bs->drv->bdrv_co_pwritev(bs, qcow2_vm_state_offset(s) + pos,
232
- qiov->size, qiov, 0);
233
+ return bs->drv->bdrv_co_pwritev_part(bs, qcow2_vm_state_offset(s) + pos,
234
+ qiov->size, qiov, 0, 0);
235
}
236
237
static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
238
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
239
.bdrv_co_block_status = qcow2_co_block_status,
240
241
.bdrv_co_preadv_part = qcow2_co_preadv_part,
242
- .bdrv_co_pwritev = qcow2_co_pwritev,
243
+ .bdrv_co_pwritev_part = qcow2_co_pwritev_part,
244
.bdrv_co_flush_to_os = qcow2_co_flush_to_os,
245
246
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
247
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
248
.bdrv_co_copy_range_from = qcow2_co_copy_range_from,
249
.bdrv_co_copy_range_to = qcow2_co_copy_range_to,
250
.bdrv_co_truncate = qcow2_co_truncate,
251
- .bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed,
252
+ .bdrv_co_pwritev_compressed_part = qcow2_co_pwritev_compressed_part,
253
.bdrv_make_empty = qcow2_make_empty,
254
255
.bdrv_snapshot_create = qcow2_snapshot_create,
256
diff --git a/util/iov.c b/util/iov.c
257
index XXXXXXX..XXXXXXX 100644
258
--- a/util/iov.c
259
+++ b/util/iov.c
260
@@ -XXX,XX +XXX,XX @@ static struct iovec *qiov_slice(QEMUIOVector *qiov,
261
return iov;
262
}
263
264
+int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len)
265
+{
266
+ size_t head, tail;
267
+ int niov;
268
+
269
+ qiov_slice(qiov, offset, len, &head, &tail, &niov);
270
+
271
+ return niov;
272
+}
273
+
274
/*
275
* Compile new iovec, combining @head_buf buffer, sub-qiov of @mid_qiov,
276
* and @tail_buf buffer into new qiov.
277
--
278
2.21.0
279
280
diff view generated by jsdifflib