1
The following changes since commit 95b31d709ba343ad237c3630047ee7438bac4065:
1
The following changes since commit afc9fcde55296b83f659de9da3cdf044812a6eeb:
2
2
3
Merge remote-tracking branch 'remotes/awilliam/tags/vfio-updates-20170331.0' into staging (2017-03-31 18:06:13 +0100)
3
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging (2021-10-20 06:10:51 -0700)
4
4
5
are available in the git repository at:
5
are available in the Git repository at:
6
6
7
git://github.com/codyprime/qemu-kvm-jtc.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 34634ca28688652198f77d7001c0f1e204434663:
9
for you to fetch changes up to 4b2b3d2653f255ef4259a7689af1956536565901:
10
10
11
block/curl: Check protocol prefix (2017-03-31 15:53:22 -0400)
11
coroutine: resize pool periodically instead of limiting size (2021-10-21 18:40:07 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches for -rc3
14
Pull request
15
16
Performance optimization when guest applications submit a lot of parallel I/O.
17
This has also been found to improve clang SafeStack performance.
18
15
----------------------------------------------------------------
19
----------------------------------------------------------------
16
20
17
Eric Blake (1):
21
Stefan Hajnoczi (1):
18
rbd: Fix regression in legacy key/values containing escaped :
22
coroutine: resize pool periodically instead of limiting size
19
23
20
Max Reitz (2):
24
include/qemu/coroutine-pool-timer.h | 36 ++++++++++++++++
21
qapi/curl: Extend and fix blockdev-add schema
25
include/qemu/coroutine.h | 7 ++++
22
block/curl: Check protocol prefix
26
iothread.c | 6 +++
23
27
util/coroutine-pool-timer.c | 35 ++++++++++++++++
24
block/curl.c | 10 +++++
28
util/main-loop.c | 5 +++
25
block/rbd.c | 83 +++++++++++++++++++++--------------------
29
util/qemu-coroutine.c | 64 ++++++++++++++++-------------
26
qapi/block-core.json | 103 ++++++++++++++++++++++++++++++++++++++++++++++-----
30
util/meson.build | 1 +
27
3 files changed, 146 insertions(+), 50 deletions(-)
31
7 files changed, 125 insertions(+), 29 deletions(-)
32
create mode 100644 include/qemu/coroutine-pool-timer.h
33
create mode 100644 util/coroutine-pool-timer.c
28
34
29
--
35
--
30
2.9.3
36
2.31.1
31
37
32
38
39
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
It was reported that enabling SafeStack reduces IOPS significantly
2
2
(>25%) with the following fio benchmark on virtio-blk using a NVMe host
3
Commit c7cacb3 accidentally broke legacy key-value parsing through
3
block device:
4
pseudo-filename parsing of -drive file=rbd://..., for any key that
4
5
contains an escaped ':'. Such a key is surprisingly common, thanks
5
# fio --rw=randrw --bs=4k --iodepth=64 --runtime=1m --direct=1 \
6
to mon_host specifying a 'host:port' string. The break happens
6
    --filename=/dev/vdb --name=job1 --ioengine=libaio --thread \
7
because passing things from QDict through QemuOpts back to another
7
    --group_reporting --numjobs=16 --time_based \
8
QDict requires that we pack our parsed key/value pairs into a string,
8
--output=/tmp/fio_result
9
and then reparse that string, but the intermediate string that we
9
10
created ("key1=value1:key2=value2") lost the \: escaping that was
10
Serge Guelton and I found that SafeStack is not really at fault, it just
11
present in the original, so that we could no longer see which : were
11
increases the cost of coroutine creation. This fio workload exhausts the
12
used as separators vs. those used as part of the original input.
12
coroutine pool and coroutine creation becomes a bottleneck. Previous
13
13
work by Honghao Wang also pointed to excessive coroutine creation.
14
Fix it by collecting the key/value pairs through a QList, and
14
15
sending that list on a round trip through a JSON QString (as in
15
Creating new coroutines is expensive due to allocating new stacks with
16
'["key1","value1","key2","value2"]') on its way through QemuOpts,
16
mmap(2) and mprotect(2). Currently there are thread-local and global
17
rather than hand-rolling our own string. Since the string is only
17
pools that recycle old Coroutine objects and their stacks but the
18
handled internally, this was faster than creating a full-blown
18
hardcoded size limit of 64 for thread-local pools and 128 for the global
19
struct of '[{"key1":"value1"},{"key2":"value2"}]', and safer at
19
pool is insufficient for the fio benchmark shown above.
20
guaranteeing order compared to '{"key1":"value1","key2":"value2"}'.
20
21
21
This patch changes the coroutine pool algorithm to a simple thread-local
22
It would be nicer if we didn't have to round-trip through QemuOpts
22
pool without a maximum size limit. Threads periodically shrink the pool
23
in the first place, but that's a much bigger task for later.
23
down to a size sufficient for the maximum observed number of coroutines.
24
24
25
Reproducer:
25
The global pool is removed by this patch. It can help to hide the fact
26
./x86_64-softmmu/qemu-system-x86_64 -nodefaults -nographic -qmp stdio \
26
that local pools are easily exhausted, but it's doesn't fix the root
27
-drive 'file=rbd:volumes/volume-ea141b5c-cdb3-4765-910d-e7008b209a70'\
27
cause. I don't think there is a need for a global pool because QEMU's
28
':id=compute:key=AQAVkvxXAAAAABAA9ZxWFYdRmV+DSwKr7BKKXg=='\
28
threads are long-lived, so let's keep things simple.
29
':auth_supported=cephx\;none:mon_host=192.168.1.2\:6789'\
29
30
',format=raw,if=none,id=drive-virtio-disk0,'\
30
Performance of the above fio benchmark is as follows:
31
'serial=ea141b5c-cdb3-4765-910d-e7008b209a70,cache=writeback'
31
32
32
Before After
33
Even without an RBD setup, this serves a test of whether we get
33
IOPS 60k 97k
34
the incorrect parser error of:
34
35
qemu-system-x86_64: -drive file=rbd:...cache=writeback: conf option 6789 has no value
35
Memory usage varies over time as needed by the workload:
36
or the correct behavior of hanging while trying to connect to
36
37
the requested mon_host of 192.168.1.2:6789.
37
VSZ (KB) RSS (KB)
38
38
Before fio 4705248 843128
39
Reported-by: Alexandru Avadanii <Alexandru.Avadanii@enea.com>
39
During fio 5747668 (+ ~100 MB) 849280
40
Signed-off-by: Eric Blake <eblake@redhat.com>
40
After fio 4694996 (- ~100 MB) 845184
41
Reviewed-by: Jeff Cody <jcody@redhat.com>
41
42
Reviewed-by: Max Reitz <mreitz@redhat.com>
42
This confirms that coroutines are indeed being freed when no longer
43
Message-id: 20170331152730.12514-1-eblake@redhat.com
43
needed.
44
Signed-off-by: Jeff Cody <jcody@redhat.com>
44
45
Thanks to Serge Guelton for working on identifying the bottleneck with
46
me!
47
48
Reported-by: Tingting Mao <timao@redhat.com>
49
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
50
Message-id: 20210913153524.1190696-1-stefanha@redhat.com
51
Cc: Serge Guelton <sguelton@redhat.com>
52
Cc: Honghao Wang <wanghonghao@bytedance.com>
53
Cc: Paolo Bonzini <pbonzini@redhat.com>
54
Cc: Daniele Buono <dbuono@linux.vnet.ibm.com>
55
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
56
57
[Moved atexit notifier to coroutine_delete() after GitLab CI reported a
58
memory leak in tests/unit/test-aio-multithread because the Coroutine
59
object was created in the main thread but runs in an IOThread (where
60
it's also deleted).
61
--Stefan]
45
---
62
---
46
block/rbd.c | 83 +++++++++++++++++++++++++++++++------------------------------
63
include/qemu/coroutine-pool-timer.h | 36 ++++++++++++++++
47
1 file changed, 42 insertions(+), 41 deletions(-)
64
include/qemu/coroutine.h | 7 ++++
48
65
iothread.c | 6 +++
49
diff --git a/block/rbd.c b/block/rbd.c
66
util/coroutine-pool-timer.c | 35 ++++++++++++++++
50
index XXXXXXX..XXXXXXX 100644
67
util/main-loop.c | 5 +++
51
--- a/block/rbd.c
68
util/qemu-coroutine.c | 64 ++++++++++++++++-------------
52
+++ b/block/rbd.c
69
util/meson.build | 1 +
53
@@ -XXX,XX +XXX,XX @@
70
7 files changed, 125 insertions(+), 29 deletions(-)
54
#include "crypto/secret.h"
71
create mode 100644 include/qemu/coroutine-pool-timer.h
55
#include "qemu/cutils.h"
72
create mode 100644 util/coroutine-pool-timer.c
56
#include "qapi/qmp/qstring.h"
73
57
+#include "qapi/qmp/qjson.h"
74
diff --git a/include/qemu/coroutine-pool-timer.h b/include/qemu/coroutine-pool-timer.h
58
75
new file mode 100644
59
/*
76
index XXXXXXX..XXXXXXX
60
* When specifying the image filename use:
77
--- /dev/null
61
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
78
+++ b/include/qemu/coroutine-pool-timer.h
62
Error **errp)
79
@@ -XXX,XX +XXX,XX @@
80
+/*
81
+ * QEMU coroutine pool timer
82
+ *
83
+ * Copyright (c) 2021 Red Hat, Inc.
84
+ *
85
+ * SPDX-License-Identifier: LGPL-2.1-or-later
86
+ *
87
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
88
+ * See the COPYING.LIB file in the top-level directory.
89
+ *
90
+ */
91
+#ifndef COROUTINE_POOL_TIMER_H
92
+#define COROUTINE_POOL_TIMER_H
93
+
94
+#include "qemu/osdep.h"
95
+#include "block/aio.h"
96
+
97
+/**
98
+ * A timer that periodically resizes this thread's coroutine pool, freeing
99
+ * memory if there are too many unused coroutines.
100
+ *
101
+ * Threads that make heavy use of coroutines should use this. Failure to resize
102
+ * the coroutine pool can lead to large amounts of memory sitting idle and
103
+ * never being used after the first time.
104
+ */
105
+typedef struct {
106
+ QEMUTimer *timer;
107
+} CoroutinePoolTimer;
108
+
109
+/* Call this before the thread runs the AioContext */
110
+void coroutine_pool_timer_init(CoroutinePoolTimer *pt, AioContext *ctx);
111
+
112
+/* Call this before the AioContext from the init function is destroyed */
113
+void coroutine_pool_timer_cleanup(CoroutinePoolTimer *pt);
114
+
115
+#endif /* COROUTINE_POOL_TIMER_H */
116
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
117
index XXXXXXX..XXXXXXX 100644
118
--- a/include/qemu/coroutine.h
119
+++ b/include/qemu/coroutine.h
120
@@ -XXX,XX +XXX,XX @@ bool qemu_in_coroutine(void);
121
*/
122
bool qemu_coroutine_entered(Coroutine *co);
123
124
+/**
125
+ * Optionally call this function periodically to shrink the thread-local pool
126
+ * down. Spiky workloads can create many coroutines and then never reach that
127
+ * level again. Shrinking the pool reclaims memory in this case.
128
+ */
129
+void qemu_coroutine_pool_periodic_resize(void);
130
+
131
/**
132
* Provides a mutex that can be used to synchronise coroutines
133
*/
134
diff --git a/iothread.c b/iothread.c
135
index XXXXXXX..XXXXXXX 100644
136
--- a/iothread.c
137
+++ b/iothread.c
138
@@ -XXX,XX +XXX,XX @@
139
#include "qemu/error-report.h"
140
#include "qemu/rcu.h"
141
#include "qemu/main-loop.h"
142
+#include "qemu/coroutine-pool-timer.h"
143
144
typedef ObjectClass IOThreadClass;
145
146
@@ -XXX,XX +XXX,XX @@ DECLARE_CLASS_CHECKERS(IOThreadClass, IOTHREAD,
147
static void *iothread_run(void *opaque)
63
{
148
{
64
const char *start;
149
IOThread *iothread = opaque;
65
- char *p, *buf, *keypairs;
150
+ CoroutinePoolTimer co_pool_timer;
66
+ char *p, *buf;
151
67
+ QList *keypairs = NULL;
152
rcu_register_thread();
68
char *found_str;
153
/*
69
- size_t max_keypair_size;
154
@@ -XXX,XX +XXX,XX @@ static void *iothread_run(void *opaque)
70
155
iothread->thread_id = qemu_get_thread_id();
71
if (!strstart(filename, "rbd:", &start)) {
156
qemu_sem_post(&iothread->init_done_sem);
72
error_setg(errp, "File name must start with 'rbd:'");
157
73
return;
158
+ coroutine_pool_timer_init(&co_pool_timer, iothread->ctx);
74
}
159
+
75
160
while (iothread->running) {
76
- max_keypair_size = strlen(start) + 1;
161
/*
77
buf = g_strdup(start);
162
* Note: from functional-wise the g_main_loop_run() below can
78
- keypairs = g_malloc0(max_keypair_size);
163
@@ -XXX,XX +XXX,XX @@ static void *iothread_run(void *opaque)
79
p = buf;
80
81
found_str = qemu_rbd_next_tok(p, '/', &p);
82
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
83
} else if (!strcmp(name, "id")) {
84
qdict_put(options, "user" , qstring_from_str(value));
85
} else {
86
- /* FIXME: This is pretty ugly, and not the right way to do this.
87
- * These should be contained in a structure, and then
88
- * passed explicitly as individual key/value pairs to
89
- * rados. Consider this legacy code that needs to be
90
- * updated. */
91
- char *tmp = g_malloc0(max_keypair_size);
92
- /* only use a delimiter if it is not the first keypair found */
93
- /* These are sets of unknown key/value pairs we'll pass along
94
- * to ceph */
95
- if (keypairs[0]) {
96
- snprintf(tmp, max_keypair_size, ":%s=%s", name, value);
97
- pstrcat(keypairs, max_keypair_size, tmp);
98
- } else {
99
- snprintf(keypairs, max_keypair_size, "%s=%s", name, value);
100
+ /*
101
+ * We pass these internally to qemu_rbd_set_keypairs(), so
102
+ * we can get away with the simpler list of [ "key1",
103
+ * "value1", "key2", "value2" ] rather than a raw dict
104
+ * { "key1": "value1", "key2": "value2" } where we can't
105
+ * guarantee order, or even a more correct but complex
106
+ * [ { "key1": "value1" }, { "key2": "value2" } ]
107
+ */
108
+ if (!keypairs) {
109
+ keypairs = qlist_new();
110
}
111
- g_free(tmp);
112
+ qlist_append(keypairs, qstring_from_str(name));
113
+ qlist_append(keypairs, qstring_from_str(value));
114
}
164
}
115
}
165
}
116
166
117
- if (keypairs[0]) {
167
+ coroutine_pool_timer_cleanup(&co_pool_timer);
118
- qdict_put(options, "=keyvalue-pairs", qstring_from_str(keypairs));
168
+
119
+ if (keypairs) {
169
g_main_context_pop_thread_default(iothread->worker_context);
120
+ qdict_put(options, "=keyvalue-pairs",
170
rcu_unregister_thread();
121
+ qobject_to_json(QOBJECT(keypairs)));
171
return NULL;
122
}
172
diff --git a/util/coroutine-pool-timer.c b/util/coroutine-pool-timer.c
123
173
new file mode 100644
124
-
174
index XXXXXXX..XXXXXXX
125
done:
175
--- /dev/null
126
g_free(buf);
176
+++ b/util/coroutine-pool-timer.c
127
- g_free(keypairs);
177
@@ -XXX,XX +XXX,XX @@
128
+ QDECREF(keypairs);
178
+/*
129
return;
179
+ * QEMU coroutine pool timer
130
}
180
+ *
131
181
+ * Copyright (c) 2021 Red Hat, Inc.
132
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
182
+ *
183
+ * SPDX-License-Identifier: LGPL-2.1-or-later
184
+ *
185
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
186
+ * See the COPYING.LIB file in the top-level directory.
187
+ *
188
+ */
189
+#include "qemu/coroutine-pool-timer.h"
190
+
191
+static void coroutine_pool_timer_cb(void *opaque)
192
+{
193
+ CoroutinePoolTimer *pt = opaque;
194
+ int64_t expiry_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) +
195
+ 15 * NANOSECONDS_PER_SECOND;
196
+
197
+ qemu_coroutine_pool_periodic_resize();
198
+ timer_mod(pt->timer, expiry_time_ns);
199
+}
200
+
201
+void coroutine_pool_timer_init(CoroutinePoolTimer *pt, AioContext *ctx)
202
+{
203
+ pt->timer = aio_timer_new(ctx, QEMU_CLOCK_REALTIME, SCALE_NS,
204
+ coroutine_pool_timer_cb, pt);
205
+ coroutine_pool_timer_cb(pt);
206
+}
207
+
208
+void coroutine_pool_timer_cleanup(CoroutinePoolTimer *pt)
209
+{
210
+ timer_free(pt->timer);
211
+ pt->timer = NULL;
212
+}
213
diff --git a/util/main-loop.c b/util/main-loop.c
214
index XXXXXXX..XXXXXXX 100644
215
--- a/util/main-loop.c
216
+++ b/util/main-loop.c
217
@@ -XXX,XX +XXX,XX @@
218
#include "qemu/error-report.h"
219
#include "qemu/queue.h"
220
#include "qemu/compiler.h"
221
+#include "qemu/coroutine-pool-timer.h"
222
223
#ifndef _WIN32
224
#include <sys/wait.h>
225
@@ -XXX,XX +XXX,XX @@ static int qemu_signal_init(Error **errp)
226
227
static AioContext *qemu_aio_context;
228
static QEMUBH *qemu_notify_bh;
229
+static CoroutinePoolTimer main_loop_co_pool_timer;
230
231
static void notify_event_cb(void *opaque)
232
{
233
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
234
g_source_set_name(src, "io-handler");
235
g_source_attach(src, NULL);
236
g_source_unref(src);
237
+
238
+ coroutine_pool_timer_init(&main_loop_co_pool_timer, qemu_aio_context);
239
+
133
return 0;
240
return 0;
134
}
241
}
135
242
136
-static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs,
243
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
137
+static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
244
index XXXXXXX..XXXXXXX 100644
138
Error **errp)
245
--- a/util/qemu-coroutine.c
246
+++ b/util/qemu-coroutine.c
247
@@ -XXX,XX +XXX,XX @@
248
#include "block/aio.h"
249
250
enum {
251
- POOL_BATCH_SIZE = 64,
252
+ /*
253
+ * qemu_coroutine_pool_periodic_resize() keeps at least this many
254
+ * coroutines around.
255
+ */
256
+ ALLOC_POOL_MIN = 64,
257
};
258
259
+
260
/** Free list to speed up creation */
261
-static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool);
262
-static unsigned int release_pool_size;
263
static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool);
264
static __thread unsigned int alloc_pool_size;
265
+static __thread unsigned int num_coroutines;
266
+static __thread unsigned int max_coroutines_this_slice;
267
static __thread Notifier coroutine_pool_cleanup_notifier;
268
269
static void coroutine_pool_cleanup(Notifier *n, void *value)
270
@@ -XXX,XX +XXX,XX @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque)
271
272
if (CONFIG_COROUTINE_POOL) {
273
co = QSLIST_FIRST(&alloc_pool);
274
- if (!co) {
275
- if (release_pool_size > POOL_BATCH_SIZE) {
276
- /* Slow path; a good place to register the destructor, too. */
277
- if (!coroutine_pool_cleanup_notifier.notify) {
278
- coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup;
279
- qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier);
280
- }
281
-
282
- /* This is not exact; there could be a little skew between
283
- * release_pool_size and the actual size of release_pool. But
284
- * it is just a heuristic, it does not need to be perfect.
285
- */
286
- alloc_pool_size = qatomic_xchg(&release_pool_size, 0);
287
- QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool);
288
- co = QSLIST_FIRST(&alloc_pool);
289
- }
290
- }
291
if (co) {
292
QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
293
alloc_pool_size--;
294
}
295
+
296
+ num_coroutines++;
297
+ if (num_coroutines > max_coroutines_this_slice) {
298
+ max_coroutines_this_slice = num_coroutines;
299
+ }
300
}
301
302
if (!co) {
303
@@ -XXX,XX +XXX,XX @@ static void coroutine_delete(Coroutine *co)
304
co->caller = NULL;
305
306
if (CONFIG_COROUTINE_POOL) {
307
- if (release_pool_size < POOL_BATCH_SIZE * 2) {
308
- QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next);
309
- qatomic_inc(&release_pool_size);
310
- return;
311
- }
312
- if (alloc_pool_size < POOL_BATCH_SIZE) {
313
- QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next);
314
- alloc_pool_size++;
315
- return;
316
+ if (!coroutine_pool_cleanup_notifier.notify) {
317
+ coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup;
318
+ qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier);
319
}
320
+
321
+ num_coroutines--;
322
+ QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next);
323
+ alloc_pool_size++;
324
+ return;
325
}
326
327
qemu_coroutine_delete(co);
328
}
329
330
+void qemu_coroutine_pool_periodic_resize(void)
331
+{
332
+ unsigned pool_size_target =
333
+ MAX(ALLOC_POOL_MIN, max_coroutines_this_slice) - num_coroutines;
334
+ max_coroutines_this_slice = num_coroutines;
335
+
336
+ while (alloc_pool_size > pool_size_target) {
337
+ Coroutine *co = QSLIST_FIRST(&alloc_pool);
338
+ QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
339
+ qemu_coroutine_delete(co);
340
+ alloc_pool_size--;
341
+ }
342
+}
343
+
344
void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co)
139
{
345
{
140
- char *p, *buf;
346
QSIMPLEQ_HEAD(, Coroutine) pending = QSIMPLEQ_HEAD_INITIALIZER(pending);
141
- char *name;
347
diff --git a/util/meson.build b/util/meson.build
142
- char *value;
348
index XXXXXXX..XXXXXXX 100644
143
+ QList *keypairs;
349
--- a/util/meson.build
144
+ QString *name;
350
+++ b/util/meson.build
145
+ QString *value;
351
@@ -XXX,XX +XXX,XX @@ if have_block
146
+ const char *key;
352
util_ss.add(files('buffer.c'))
147
+ size_t remaining;
353
util_ss.add(files('bufferiszero.c'))
148
int ret = 0;
354
util_ss.add(files('coroutine-@0@.c'.format(config_host['CONFIG_COROUTINE_BACKEND'])))
149
355
+ util_ss.add(files('coroutine-pool-timer.c'))
150
- buf = g_strdup(keypairs);
356
util_ss.add(files('hbitmap.c'))
151
- p = buf;
357
util_ss.add(files('hexdump.c'))
152
+ if (!keypairs_json) {
358
util_ss.add(files('iova-tree.c'))
153
+ return ret;
154
+ }
155
+ keypairs = qobject_to_qlist(qobject_from_json(keypairs_json,
156
+ &error_abort));
157
+ remaining = qlist_size(keypairs) / 2;
158
+ assert(remaining);
159
160
- while (p) {
161
- name = qemu_rbd_next_tok(p, '=', &p);
162
- if (!p) {
163
- error_setg(errp, "conf option %s has no value", name);
164
- ret = -EINVAL;
165
- break;
166
- }
167
+ while (remaining--) {
168
+ name = qobject_to_qstring(qlist_pop(keypairs));
169
+ value = qobject_to_qstring(qlist_pop(keypairs));
170
+ assert(name && value);
171
+ key = qstring_get_str(name);
172
173
- value = qemu_rbd_next_tok(p, ':', &p);
174
-
175
- ret = rados_conf_set(cluster, name, value);
176
+ ret = rados_conf_set(cluster, key, qstring_get_str(value));
177
+ QDECREF(name);
178
+ QDECREF(value);
179
if (ret < 0) {
180
- error_setg_errno(errp, -ret, "invalid conf option %s", name);
181
+ error_setg_errno(errp, -ret, "invalid conf option %s", key);
182
ret = -EINVAL;
183
break;
184
}
185
}
186
187
- g_free(buf);
188
+ QDECREF(keypairs);
189
return ret;
190
}
191
192
--
359
--
193
2.9.3
360
2.31.1
194
361
195
362
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
The curl block driver accepts more options than just "filename"; also,
4
the URL is actually expected to be passed through the "url" option
5
instead of "filename".
6
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Jeff Cody <jcody@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Message-id: 20170331120431.1767-2-mreitz@redhat.com
11
Signed-off-by: Jeff Cody <jcody@redhat.com>
12
---
13
qapi/block-core.json | 103 ++++++++++++++++++++++++++++++++++++++++++++++-----
14
1 file changed, 94 insertions(+), 9 deletions(-)
15
16
diff --git a/qapi/block-core.json b/qapi/block-core.json
17
index XXXXXXX..XXXXXXX 100644
18
--- a/qapi/block-core.json
19
+++ b/qapi/block-core.json
20
@@ -XXX,XX +XXX,XX @@
21
'*debug': 'int' } }
22
23
##
24
-# @BlockdevOptionsCurl:
25
+# @BlockdevOptionsCurlBase:
26
#
27
-# Driver specific block device options for the curl backend.
28
+# Driver specific block device options shared by all protocols supported by the
29
+# curl backend.
30
#
31
-# @filename: path to the image file
32
+# @url: URL of the image file
33
+#
34
+# @readahead: Size of the read-ahead cache; must be a multiple of
35
+# 512 (defaults to 256 kB)
36
+#
37
+# @timeout: Timeout for connections, in seconds (defaults to 5)
38
+#
39
+# @username: Username for authentication (defaults to none)
40
+#
41
+# @password-secret: ID of a QCryptoSecret object providing a password
42
+# for authentication (defaults to no password)
43
+#
44
+# @proxy-username: Username for proxy authentication (defaults to none)
45
+#
46
+# @proxy-password-secret: ID of a QCryptoSecret object providing a password
47
+# for proxy authentication (defaults to no password)
48
+#
49
+# Since: 2.9
50
+##
51
+{ 'struct': 'BlockdevOptionsCurlBase',
52
+ 'data': { 'url': 'str',
53
+ '*readahead': 'int',
54
+ '*timeout': 'int',
55
+ '*username': 'str',
56
+ '*password-secret': 'str',
57
+ '*proxy-username': 'str',
58
+ '*proxy-password-secret': 'str' } }
59
+
60
+##
61
+# @BlockdevOptionsCurlHttp:
62
+#
63
+# Driver specific block device options for HTTP connections over the curl
64
+# backend. URLs must start with "http://".
65
+#
66
+# @cookie: List of cookies to set; format is
67
+# "name1=content1; name2=content2;" as explained by
68
+# CURLOPT_COOKIE(3). Defaults to no cookies.
69
+#
70
+# Since: 2.9
71
+##
72
+{ 'struct': 'BlockdevOptionsCurlHttp',
73
+ 'base': 'BlockdevOptionsCurlBase',
74
+ 'data': { '*cookie': 'str' } }
75
+
76
+##
77
+# @BlockdevOptionsCurlHttps:
78
+#
79
+# Driver specific block device options for HTTPS connections over the curl
80
+# backend. URLs must start with "https://".
81
+#
82
+# @cookie: List of cookies to set; format is
83
+# "name1=content1; name2=content2;" as explained by
84
+# CURLOPT_COOKIE(3). Defaults to no cookies.
85
+#
86
+# @sslverify: Whether to verify the SSL certificate's validity (defaults to
87
+# true)
88
+#
89
+# Since: 2.9
90
+##
91
+{ 'struct': 'BlockdevOptionsCurlHttps',
92
+ 'base': 'BlockdevOptionsCurlBase',
93
+ 'data': { '*cookie': 'str',
94
+ '*sslverify': 'bool' } }
95
+
96
+##
97
+# @BlockdevOptionsCurlFtp:
98
+#
99
+# Driver specific block device options for FTP connections over the curl
100
+# backend. URLs must start with "ftp://".
101
+#
102
+# Since: 2.9
103
+##
104
+{ 'struct': 'BlockdevOptionsCurlFtp',
105
+ 'base': 'BlockdevOptionsCurlBase',
106
+ 'data': { } }
107
+
108
+##
109
+# @BlockdevOptionsCurlFtps:
110
+#
111
+# Driver specific block device options for FTPS connections over the curl
112
+# backend. URLs must start with "ftps://".
113
+#
114
+# @sslverify: Whether to verify the SSL certificate's validity (defaults to
115
+# true)
116
#
117
# Since: 2.9
118
##
119
-{ 'struct': 'BlockdevOptionsCurl',
120
- 'data': { 'filename': 'str' } }
121
+{ 'struct': 'BlockdevOptionsCurlFtps',
122
+ 'base': 'BlockdevOptionsCurlBase',
123
+ 'data': { '*sslverify': 'bool' } }
124
125
##
126
# @BlockdevOptionsNbd:
127
@@ -XXX,XX +XXX,XX @@
128
'cloop': 'BlockdevOptionsGenericFormat',
129
'dmg': 'BlockdevOptionsGenericFormat',
130
'file': 'BlockdevOptionsFile',
131
- 'ftp': 'BlockdevOptionsCurl',
132
- 'ftps': 'BlockdevOptionsCurl',
133
+ 'ftp': 'BlockdevOptionsCurlFtp',
134
+ 'ftps': 'BlockdevOptionsCurlFtps',
135
'gluster': 'BlockdevOptionsGluster',
136
'host_cdrom': 'BlockdevOptionsFile',
137
'host_device':'BlockdevOptionsFile',
138
- 'http': 'BlockdevOptionsCurl',
139
- 'https': 'BlockdevOptionsCurl',
140
+ 'http': 'BlockdevOptionsCurlHttp',
141
+ 'https': 'BlockdevOptionsCurlHttps',
142
'iscsi': 'BlockdevOptionsIscsi',
143
'luks': 'BlockdevOptionsLUKS',
144
'nbd': 'BlockdevOptionsNbd',
145
--
146
2.9.3
147
148
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
If the user has explicitly specified a block driver and thus a protocol,
4
we have to make sure the URL's protocol prefix matches. Otherwise the
5
latter will silently override the former which might catch some users by
6
surprise.
7
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
10
Reviewed-by: Jeff Cody <jcody@redhat.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Message-id: 20170331120431.1767-3-mreitz@redhat.com
13
Signed-off-by: Jeff Cody <jcody@redhat.com>
14
---
15
block/curl.c | 10 ++++++++++
16
1 file changed, 10 insertions(+)
17
18
diff --git a/block/curl.c b/block/curl.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/curl.c
21
+++ b/block/curl.c
22
@@ -XXX,XX +XXX,XX @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
23
const char *cookie;
24
double d;
25
const char *secretid;
26
+ const char *protocol_delimiter;
27
28
static int inited = 0;
29
30
@@ -XXX,XX +XXX,XX @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
31
goto out_noclean;
32
}
33
34
+ if (!strstart(file, bs->drv->protocol_name, &protocol_delimiter) ||
35
+ !strstart(protocol_delimiter, "://", NULL))
36
+ {
37
+ error_setg(errp, "%s curl driver cannot handle the URL '%s' (does not "
38
+ "start with '%s://')", bs->drv->protocol_name, file,
39
+ bs->drv->protocol_name);
40
+ goto out_noclean;
41
+ }
42
+
43
s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME));
44
secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORD_SECRET);
45
46
--
47
2.9.3
48
49
diff view generated by jsdifflib